From 75cbe4a1c1bfb853b393371e6f525aa2b49d4c70 Mon Sep 17 00:00:00 2001 From: Joon Date: Fri, 25 Jan 2019 22:10:36 +0900 Subject: [PATCH 001/259] R4R: Config TestRoot modification for LCD test (#3177) * add ResetTestRootWithChainID * modify chainid --- config/toml.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/config/toml.go b/config/toml.go index 79ae99beb..e842e9e37 100644 --- a/config/toml.go +++ b/config/toml.go @@ -2,6 +2,7 @@ package config import ( "bytes" + "fmt" "os" "path/filepath" "text/template" @@ -317,6 +318,10 @@ namespace = "{{ .Instrumentation.Namespace }}" /****** these are for test settings ***********/ func ResetTestRoot(testName string) *Config { + return ResetTestRootWithChainID(testName, "") +} + +func ResetTestRootWithChainID(testName string, chainID string) *Config { rootDir := os.ExpandEnv("$HOME/.tendermint_test") rootDir = filepath.Join(rootDir, testName) // Remove ~/.tendermint_test_bak @@ -353,6 +358,10 @@ func ResetTestRoot(testName string) *Config { writeDefaultConfigFile(configFilePath) } if !cmn.FileExists(genesisFilePath) { + if chainID == "" { + chainID = "tendermint_test" + } + testGenesis := fmt.Sprintf(testGenesisFmt, chainID) cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644) } // we always overwrite the priv val @@ -363,9 +372,9 @@ func ResetTestRoot(testName string) *Config { return config } -var testGenesis = `{ +var testGenesisFmt = `{ "genesis_time": "2018-10-10T08:20:13.695936996Z", - "chain_id": "tendermint_test", + "chain_id": "%s", "validators": [ { "pub_key": { From d6dd43cdaa6e80ff4184b5e5d8d7a0a101114e75 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Fri, 25 Jan 2019 14:38:26 +0100 Subject: [PATCH 002/259] adr: style fixes (#3206) - links to issues - fix a few markdown glitches - inline code - etc --- docs/architecture/adr-033-pubsub.md | 37 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/docs/architecture/adr-033-pubsub.md b/docs/architecture/adr-033-pubsub.md index c52bf44a7..55bf320c5 100644 --- a/docs/architecture/adr-033-pubsub.md +++ b/docs/architecture/adr-033-pubsub.md @@ -5,13 +5,15 @@ Author: Anton Kaliaev (@melekes) ## Changelog 02-10-2018: Initial draft + 16-01-2019: Second version based on our conversation with Jae + 17-01-2019: Third version explaining how new design solves current issues ## Context Since the initial version of the pubsub, there's been a number of issues -raised: #951, #1879, #1880. Some of them are high-level issues questioning the +raised: [#951], [#1879], [#1880]. Some of them are high-level issues questioning the core design choices made. Others are minor and mostly about the interface of `Subscribe()` / `Publish()` functions. @@ -91,7 +93,7 @@ can make capacity an argument. ## Decision -Change Subscribe() function to return a `Subscription` struct: +Change `Subscribe()` function to return a `Subscription` struct: ```go type Subscription struct { @@ -103,18 +105,18 @@ func (s *Subscription) Cancelled() <-chan struct{} func (s *Subscription) Err() error ``` -Out returns a channel onto which messages and tags are published. -Unsubscribe/UnsubscribeAll does not close the channel to avoid clients from +`Out()` returns a channel onto which messages and tags are published. +`Unsubscribe`/`UnsubscribeAll` does not close the channel to avoid clients from receiving a nil message. -Cancelled returns a channel that's closed when the subscription is terminated +`Cancelled()` returns a channel that's closed when the subscription is terminated and supposed to be used in a select statement. -If Cancelled is not closed yet, Err() returns nil. -If Cancelled is closed, Err returns a non-nil error explaining why: -Unsubscribed if the subscriber choose to unsubscribe, -OutOfCapacity if the subscriber is not pulling messages fast enough and the Out channel become full. -After Err returns a non-nil error, successive calls to Err() return the same error. +If the channel returned by `Cancelled()` is not closed yet, `Err()` returns nil. +If the channel is closed, `Err()` returns a non-nil error explaining why: +`ErrUnsubscribed` if the subscriber choose to unsubscribe, +`ErrOutOfCapacity` if the subscriber is not pulling messages fast enough and the channel returned by `Out()` became full. +After `Err()` returns a non-nil error, successive calls to `Err() return the same error. ```go subscription, err := pubsub.Subscribe(...) @@ -130,18 +132,18 @@ select { } ``` -Make Out() channel buffered (cap: 1) by default. In most cases, we want to +Make the `Out()` channel buffered (with capacity 1) by default. In most cases, we want to terminate the slow subscriber. Only in rare cases, we want to block the pubsub (e.g. when debugging consensus). This should lower the chances of the pubsub being frozen. ```go // outCap can be used to set capacity of Out channel (1 by default). Set to 0 -for unbuffered channel (WARNING: it may block the pubsub). +// for unbuffered channel (WARNING: it may block the pubsub). Subscribe(ctx context.Context, clientID string, query Query, outCap... int) (Subscription, error) { ``` -Also, Out() channel should return tags along with a message: +Also, the `Out()` channel should return tags along with a message: ```go type MsgAndTags struct { @@ -154,12 +156,12 @@ to inform clients of which Tags were used with Msg. ### How this new design solves the current issues? -https://github.com/tendermint/tendermint/issues/951 (https://github.com/tendermint/tendermint/issues/1880) +[#951] ([#1880]): Because of non-blocking send, situation where we'll deadlock is not possible anymore. If the client stops reading messages, it will be removed. -https://github.com/tendermint/tendermint/issues/1879 +[#1879]: MsgAndTags is used now instead of a plain message. @@ -191,3 +193,8 @@ In review - (since v1) no concurrency when it comes to publishing messages ### Neutral + + +[#951]: https://github.com/tendermint/tendermint/issues/951 +[#1879]: https://github.com/tendermint/tendermint/issues/1879 +[#1880]: https://github.com/tendermint/tendermint/issues/1880 From ddbdffb4e52710a3bbbb54f2fc9eb80719d7f065 Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Fri, 25 Jan 2019 11:00:55 -0800 Subject: [PATCH 003/259] add design philosophy doc (#3034) --- PHILOSOPHY.md | 158 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 PHILOSOPHY.md diff --git a/PHILOSOPHY.md b/PHILOSOPHY.md new file mode 100644 index 000000000..cf79710fd --- /dev/null +++ b/PHILOSOPHY.md @@ -0,0 +1,158 @@ +## Design goals + +The design goals for Tendermint (and the SDK and related libraries) are: + + * Simplicity and Legibility + * Parallel performance, namely ability to utilize multicore architecture + * Ability to evolve the codebase bug-free + * Debuggability + * Complete correctness that considers all edge cases, esp in concurrency + * Future-proof modular architecture, message protocol, APIs, and encapsulation + + +### Justification + +Legibility is key to maintaining bug-free software as it evolves toward more +optimizations, more ease of debugging, and additional features. + +It is too easy to introduce bugs over time by replacing lines of code with +those that may panic, which means ideally locks are unlocked by defer +statements. + +For example, + +```go +func (obj *MyObj) something() { + mtx.Lock() + obj.something = other + mtx.Unlock() +} +``` + +It is too easy to refactor the codebase in the future to replace `other` with +`other.String()` for example, and this may introduce a bug that causes a +deadlock. So as much as reasonably possible, we need to be using defer +statements, even though it introduces additional overhead. + +If it is necessary to optimize the unlocking of mutex locks, the solution is +more modularity via smaller functions, so that defer'd unlocks are scoped +within a smaller function. + +Similarly, idiomatic for-loops should always be preferred over those that use +custom counters, because it is too easy to evolve the body of a for-loop to +become more complicated over time, and it becomes more and more difficult to +assess the correctness of such a for-loop by visual inspection. + + +### On performance + +It doesn't matter whether there are alternative implementations that are 2x or +3x more performant, when the software doesn't work, deadlocks, or if bugs +cannot be debugged. By taking advantage of multicore concurrency, the +Tendermint implementation will at least be an order of magnitude within the +range of what is theoretically possible. The design philosophy of Tendermint, +and the choice of Go as implementation language, is designed to make Tendermint +implementation the standard specification for concurrent BFT software. + +By focusing on the message protocols (e.g. ABCI, p2p messages), and +encapsulation e.g. IAVL module, (relatively) independent reactors, we are both +implementing a standard implementation to be used as the specification for +future implementations in more optimizable languages like Rust, Java, and C++; +as well as creating sufficiently performant software. Tendermint Core will +never be as fast as future implementations of the Tendermint Spec, because Go +isn't designed to be as fast as possible. The advantage of using Go is that we +can develop the whole stack of modular components **faster** than in other +languages. + +Furthermore, the real bottleneck is in the application layer, and it isn't +necessary to support more than a sufficiently decentralized set of validators +(e.g. 100 ~ 300 validators is sufficient, with delegated bonded PoS). + +Instead of optimizing Tendermint performance down to the metal, lets focus on +optimizing on other matters, namely ability to push feature complete software +that works well enough, can be debugged and maintained, and can serve as a spec +for future implementations. + + +### On encapsulation + +In order to create maintainable, forward-optimizable software, it is critical +to develop well-encapsulated objects that have well understood properties, and +to re-use these easy-to-use-correctly components as building blocks for further +encapsulated meta-objects. + +For example, mutexes are cheap enough for Tendermint's design goals when there +isn't goroutine contention, so it is encouraged to create concurrency safe +structures with struct-level mutexes. If they are used in the context of +non-concurrent logic, then the performance is good enough. If they are used in +the context of concurrent logic, then it will still perform correctly. + +Examples of this design principle can be seen in the types.ValidatorSet struct, +and the cmn.Rand struct. It's one single struct declaration that can be used +in both concurrent and non-concurrent logic, and due to its well encapsulation, +it's easy to get the usage of the mutex right. + +#### example: cmn.Rand: + +`The default Source is safe for concurrent use by multiple goroutines, but +Sources created by NewSource are not`. The reason why the default +package-level source is safe for concurrent use is because it is protected (see +`lockedSource` in https://golang.org/src/math/rand/rand.go). + +But we shouldn't rely on the global source, we should be creating our own +Rand/Source instances and using them, especially for determinism in testing. +So it is reasonable to have cmn.Rand be protected by a mutex. Whether we want +our own implementation of Rand is another question, but the answer there is +also in the affirmative. Sometimes you want to know where Rand is being used +in your code, so it becomes a simple matter of dropping in a log statement to +inject inspectability into Rand usage. Also, it is nice to be able to extend +the functionality of Rand with custom methods. For these reasons, and for the +reasons which is outlined in this design philosophy document, we should +continue to use the cmn.Rand object, with mutex protection. + +Another key aspect of good encapsulation is the choice of exposed vs unexposed +methods. It should be clear to the reader of the code, which methods are +intended to be used in what context, and what safe usage is. Part of this is +solved by hiding methods via unexported methods. Another part of this is +naming conventions on the methods (e.g. underscores) with good documentation, +and code organization. If there are too many exposed methods and it isn't +clear what methods have what side effects, then there is something wrong about +the design of abstractions that should be revisited. + + +### On concurrency + +In order for Tendermint to remain relevant in the years to come, it is vital +for Tendermint to take advantage of multicore architectures. Due to the nature +of the problem, namely consensus across a concurrent p2p gossip network, and to +handle RPC requests for a large number of consuming subscribers, it is +unavoidable for Tendermint development to require expertise in concurrency +design, especially when it comes to the reactor design, and also for RPC +request handling. + + +## Guidelines + +Here are some guidelines for designing for (sufficient) performance and concurrency: + + * Mutex locks are cheap enough when there isn't contention. + * Do not optimize code without analytical or observed proof that it is in a hot path. + * Don't over-use channels when mutex locks w/ encapsulation are sufficient. + * The need to drain channels are often a hint of unconsidered edge cases. + * The creation of O(N) one-off goroutines is generally technical debt that + needs to get addressed sooner than later. Avoid creating too many +goroutines as a patch around incomplete concurrency design, or at least be +aware of the debt and do not invest in the debt. On the other hand, Tendermint +is designed to have a limited number of peers (e.g. 10 or 20), so the creation +of O(C) goroutines per O(P) peers is still O(C\*P=constant). + * Use defer statements to unlock as much as possible. If you want to unlock sooner, + try to create more modular functions that do make use of defer statements. + +## Matras + +* Premature optimization kills +* Readability is paramount +* Beautiful is better than fast. +* In the face of ambiguity, refuse the temptation to guess. +* In the face of bugs, refuse the temptation to cover the bug. +* There should be one-- and preferably only one --obvious way to do it. From a58d5897e42ff58ea0919e46444f4508ed38df90 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 25 Jan 2019 15:01:22 -0500 Subject: [PATCH 004/259] note about TmCoreSemVer --- version/version.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/version/version.go b/version/version.go index 9d05afe64..86c38c033 100644 --- a/version/version.go +++ b/version/version.go @@ -18,6 +18,8 @@ const ( // TMCoreSemVer is the current version of Tendermint Core. // It's the Semantic Version of the software. // Must be a string because scripts like dist.sh read this file. + // XXX: Don't change the name of this variable or you will break + // automation :) TMCoreSemVer = "0.29.1" // ABCISemVer is the semantic version of the ABCI library From 57af99d901fccd7078a4da910b665842321ac4cd Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 25 Jan 2019 15:01:39 -0500 Subject: [PATCH 005/259] types: comments on user vs internal events Distinguish between user events and internal consensus events --- types/events.go | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/types/events.go b/types/events.go index 9b3b158d8..b70bc9dc5 100644 --- a/types/events.go +++ b/types/events.go @@ -11,21 +11,30 @@ import ( // Reserved event types (alphabetically sorted). const ( - EventCompleteProposal = "CompleteProposal" - EventLock = "Lock" + // Block level events for mass consumption by users. + // These events are triggered from the state package, + // after a block has been committed. + // These are also used by the tx indexer for async indexing. + // All of this data can be fetched through the rpc. EventNewBlock = "NewBlock" EventNewBlockHeader = "NewBlockHeader" - EventNewRound = "NewRound" - EventNewRoundStep = "NewRoundStep" - EventPolka = "Polka" - EventRelock = "Relock" - EventTimeoutPropose = "TimeoutPropose" - EventTimeoutWait = "TimeoutWait" EventTx = "Tx" - EventUnlock = "Unlock" - EventValidBlock = "ValidBlock" EventValidatorSetUpdates = "ValidatorSetUpdates" - EventVote = "Vote" + + // Internal consensus events. + // These are used for testing the consensus state machine. + // They can also be used to build real-time consensus visualizers. + EventCompleteProposal = "CompleteProposal" + EventLock = "Lock" + EventNewRound = "NewRound" + EventNewRoundStep = "NewRoundStep" + EventPolka = "Polka" + EventRelock = "Relock" + EventTimeoutPropose = "TimeoutPropose" + EventTimeoutWait = "TimeoutWait" + EventUnlock = "Unlock" + EventValidBlock = "ValidBlock" + EventVote = "Vote" ) /////////////////////////////////////////////////////////////////////////////// From 9b6b792ce70e52516a643abba077ee82668db48b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 25 Jan 2019 18:11:31 -0500 Subject: [PATCH 006/259] pubsub: comments --- libs/pubsub/pubsub.go | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index b4e392bbd..cb41f8b38 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -223,9 +223,11 @@ func (s *Server) Unsubscribe(ctx context.Context, clientID string, query Query) } // original query is used here because we're using pointers as map keys + // ? select { case s.cmds <- cmd{op: unsub, clientID: clientID, query: origQuery}: s.mtx.Lock() + // if its the only query left, should we also delete the client? delete(clientSubscriptions, query.String()) s.mtx.Unlock() return nil @@ -353,20 +355,24 @@ func (state *state) remove(clientID string, q Query) { } ch, ok := clientToChannelMap[clientID] - if ok { - close(ch) + if !ok { + return + } - delete(state.clients[clientID], q) + close(ch) - // if it not subscribed to anything else, remove the client - if len(state.clients[clientID]) == 0 { - delete(state.clients, clientID) - } + // remove the query from client map. + // if client is not subscribed to anything else, remove it. + delete(state.clients[clientID], q) + if len(state.clients[clientID]) == 0 { + delete(state.clients, clientID) + } - delete(state.queries[q], clientID) - if len(state.queries[q]) == 0 { - delete(state.queries, q) - } + // remove the client from query map. + // if query has no other clients subscribed, remove it. + delete(state.queries[q], clientID) + if len(state.queries[q]) == 0 { + delete(state.queries, q) } } @@ -380,11 +386,15 @@ func (state *state) removeAll(clientID string) { ch := state.queries[q][clientID] close(ch) + // remove the client from query map. + // if query has no other clients subscribed, remove it. delete(state.queries[q], clientID) if len(state.queries[q]) == 0 { delete(state.queries, q) } } + + // remove the client. delete(state.clients, clientID) } From d91ea9b59d7f77b20b92ae8caf1f37d6584b994c Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 25 Jan 2019 18:28:06 -0500 Subject: [PATCH 007/259] adr-033 update --- docs/architecture/adr-033-pubsub.md | 69 ++++++++++++++++++++++++----- 1 file changed, 58 insertions(+), 11 deletions(-) diff --git a/docs/architecture/adr-033-pubsub.md b/docs/architecture/adr-033-pubsub.md index 55bf320c5..88922646f 100644 --- a/docs/architecture/adr-033-pubsub.md +++ b/docs/architecture/adr-033-pubsub.md @@ -10,6 +10,8 @@ Author: Anton Kaliaev (@melekes) 17-01-2019: Third version explaining how new design solves current issues +25-01-2019: Fourth version to treat buffered and unbuffered channels differently + ## Context Since the initial version of the pubsub, there's been a number of issues @@ -53,7 +55,10 @@ channels to distribute msg to these goroutines). ### Non-blocking send -There is also a question whenever we should have a non-blocking send: +There is also a question whenever we should have a non-blocking send. +Currently, sends are blocking, so publishing to one client can block on +publishing to another. This means a slow or unresponsive client can halt the +system. Instead, we can use a non-blocking send: ```go for each subscriber { @@ -89,10 +94,25 @@ Go channels are de-facto standard for carrying data between goroutines. ### Why `Subscribe()` accepts an `out` channel? Because in our tests, we create buffered channels (cap: 1). Alternatively, we -can make capacity an argument. +can make capacity an argument and return a channel. ## Decision +### MsgAndTags + +Use a `MsgAndTags` struct on the subscription channel to indicate what tags the +msg matched. + +```go +type MsgAndTags struct { + Msg interface{} + Tags TagMap +} +``` + +### Subscription Struct + + Change `Subscribe()` function to return a `Subscription` struct: ```go @@ -132,27 +152,53 @@ select { } ``` +### Capacity and Subscriptions + Make the `Out()` channel buffered (with capacity 1) by default. In most cases, we want to terminate the slow subscriber. Only in rare cases, we want to block the pubsub (e.g. when debugging consensus). This should lower the chances of the pubsub being frozen. ```go -// outCap can be used to set capacity of Out channel (1 by default). Set to 0 -// for unbuffered channel (WARNING: it may block the pubsub). +// outCap can be used to set capacity of Out channel +// (1 by default, must be greater than 0). Subscribe(ctx context.Context, clientID string, query Query, outCap... int) (Subscription, error) { ``` -Also, the `Out()` channel should return tags along with a message: +Use a different function for an unbuffered channel: ```go -type MsgAndTags struct { - Msg interface{} - Tags TagMap -} +// Subscription uses an unbuffered channel. Publishing will block. +SubscribeUnbuffered(ctx context.Context, clientID string, query Query) (Subscription, error) { ``` -to inform clients of which Tags were used with Msg. +SubscribeUnbuffered should not be exposed to users. + +### Blocking/Nonblocking + +The publisher should treat these kinds of channels separately. +It should block on unbuffered channels (for use with internal consensus events +in the consensus tests) and not block on the buffered ones. If a client is too +slow to keep up with it's messages, it's subscription is terminated: + +for each subscription { + out := subscription.outChan + if cap(out) == 0 { + // block on unbuffered channel + out <- msg + } else { + // don't block on buffered channels + select { + case out <- msg: + default: + // set the error, notify on the cancel chan + subscription.err = fmt.Errorf("client is too slow for msg) + close(subscription.cancelChan) + + // ... unsubscribe and close out + } + } +} ### How this new design solves the current issues? @@ -167,7 +213,7 @@ MsgAndTags is used now instead of a plain message. ### Future problems and their possible solutions -https://github.com/tendermint/tendermint/issues/2826 +[#2826] One question I am still pondering about: how to prevent pubsub from slowing down consensus. We can increase the pubsub queue size (which is 0 now). Also, @@ -198,3 +244,4 @@ In review [#951]: https://github.com/tendermint/tendermint/issues/951 [#1879]: https://github.com/tendermint/tendermint/issues/1879 [#1880]: https://github.com/tendermint/tendermint/issues/1880 +[#2826]: https://github.com/tendermint/tendermint/issues/2826 From 71e5939441b3ecdbad99345d9733ebe7e271645d Mon Sep 17 00:00:00 2001 From: cong Date: Mon, 28 Jan 2019 15:36:35 +0800 Subject: [PATCH 008/259] start eventBus & indexerService before replay and use them while replaying blocks (#3194) so if we did not index the last block (because of panic or smth else), we index it during replay Closes #3186 --- CHANGELOG_PENDING.md | 1 + consensus/replay.go | 9 +++++ consensus/replay_file.go | 13 +++---- node/node.go | 73 ++++++++++++++++++++++------------------ state/execution.go | 3 +- state/execution_test.go | 4 +-- 6 files changed, 61 insertions(+), 42 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 06b2ec52c..6b7677b52 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,3 +21,4 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: +- [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution diff --git a/consensus/replay.go b/consensus/replay.go index 16b66e86d..96e23b7b8 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -196,6 +196,7 @@ type Handshaker struct { stateDB dbm.DB initialState sm.State store sm.BlockStore + eventBus types.BlockEventPublisher genDoc *types.GenesisDoc logger log.Logger @@ -209,6 +210,7 @@ func NewHandshaker(stateDB dbm.DB, state sm.State, stateDB: stateDB, initialState: state, store: store, + eventBus: types.NopEventBus{}, genDoc: genDoc, logger: log.NewNopLogger(), nBlocks: 0, @@ -219,6 +221,12 @@ func (h *Handshaker) SetLogger(l log.Logger) { h.logger = l } +// SetEventBus - sets the event bus for publishing block related events. +// If not called, it defaults to types.NopEventBus. +func (h *Handshaker) SetEventBus(eventBus types.BlockEventPublisher) { + h.eventBus = eventBus +} + func (h *Handshaker) NBlocks() int { return h.nBlocks } @@ -432,6 +440,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap meta := h.store.LoadBlockMeta(height) blockExec := sm.NewBlockExecutor(h.stateDB, h.logger, proxyApp, sm.MockMempool{}, sm.MockEvidencePool{}) + blockExec.SetEventBus(h.eventBus) var err error state, err = blockExec.ApplyBlock(state, meta.BlockID, block) diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 2d0879147..3e92bad67 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -326,17 +326,18 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo cmn.Exit(fmt.Sprintf("Error starting proxy app conns: %v", err)) } - handshaker := NewHandshaker(stateDB, state, blockStore, gdoc) - err = handshaker.Handshake(proxyApp) - if err != nil { - cmn.Exit(fmt.Sprintf("Error on handshake: %v", err)) - } - eventBus := types.NewEventBus() if err := eventBus.Start(); err != nil { cmn.Exit(fmt.Sprintf("Failed to start event bus: %v", err)) } + handshaker := NewHandshaker(stateDB, state, blockStore, gdoc) + handshaker.SetEventBus(eventBus) + err = handshaker.Handshake(proxyApp) + if err != nil { + cmn.Exit(fmt.Sprintf("Error on handshake: %v", err)) + } + mempool, evpool := sm.MockMempool{}, sm.MockEvidencePool{} blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) diff --git a/node/node.go b/node/node.go index 0c38fc116..b7ca6517d 100644 --- a/node/node.go +++ b/node/node.go @@ -217,11 +217,51 @@ func NewNode(config *cfg.Config, return nil, fmt.Errorf("Error starting proxy app connections: %v", err) } + // EventBus and IndexerService must be started before the handshake because + // we might need to index the txs of the replayed block as this might not have happened + // when the node stopped last time (i.e. the node stopped after it saved the block + // but before it indexed the txs, or, endblocker panicked) + eventBus := types.NewEventBus() + eventBus.SetLogger(logger.With("module", "events")) + + err = eventBus.Start() + if err != nil { + return nil, err + } + + // Transaction indexing + var txIndexer txindex.TxIndexer + switch config.TxIndex.Indexer { + case "kv": + store, err := dbProvider(&DBContext{"tx_index", config}) + if err != nil { + return nil, err + } + if config.TxIndex.IndexTags != "" { + txIndexer = kv.NewTxIndex(store, kv.IndexTags(splitAndTrimEmpty(config.TxIndex.IndexTags, ",", " "))) + } else if config.TxIndex.IndexAllTags { + txIndexer = kv.NewTxIndex(store, kv.IndexAllTags()) + } else { + txIndexer = kv.NewTxIndex(store) + } + default: + txIndexer = &null.TxIndex{} + } + + indexerService := txindex.NewIndexerService(txIndexer, eventBus) + indexerService.SetLogger(logger.With("module", "txindex")) + + err = indexerService.Start() + if err != nil { + return nil, err + } + // Create the handshaker, which calls RequestInfo, sets the AppVersion on the state, // and replays any blocks as necessary to sync tendermint with the app. consensusLogger := logger.With("module", "consensus") handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc) handshaker.SetLogger(consensusLogger) + handshaker.SetEventBus(eventBus) if err := handshaker.Handshake(proxyApp); err != nil { return nil, fmt.Errorf("Error during handshake: %v", err) } @@ -343,35 +383,10 @@ func NewNode(config *cfg.Config, consensusReactor := cs.NewConsensusReactor(consensusState, fastSync, cs.ReactorMetrics(csMetrics)) consensusReactor.SetLogger(consensusLogger) - eventBus := types.NewEventBus() - eventBus.SetLogger(logger.With("module", "events")) - // services which will be publishing and/or subscribing for messages (events) // consensusReactor will set it on consensusState and blockExecutor consensusReactor.SetEventBus(eventBus) - // Transaction indexing - var txIndexer txindex.TxIndexer - switch config.TxIndex.Indexer { - case "kv": - store, err := dbProvider(&DBContext{"tx_index", config}) - if err != nil { - return nil, err - } - if config.TxIndex.IndexTags != "" { - txIndexer = kv.NewTxIndex(store, kv.IndexTags(splitAndTrimEmpty(config.TxIndex.IndexTags, ",", " "))) - } else if config.TxIndex.IndexAllTags { - txIndexer = kv.NewTxIndex(store, kv.IndexAllTags()) - } else { - txIndexer = kv.NewTxIndex(store) - } - default: - txIndexer = &null.TxIndex{} - } - - indexerService := txindex.NewIndexerService(txIndexer, eventBus) - indexerService.SetLogger(logger.With("module", "txindex")) - p2pLogger := logger.With("module", "p2p") nodeInfo, err := makeNodeInfo( config, @@ -534,11 +549,6 @@ func (n *Node) OnStart() error { time.Sleep(genTime.Sub(now)) } - err := n.eventBus.Start() - if err != nil { - return err - } - // Add private IDs to addrbook to block those peers being added n.addrBook.AddPrivateIDs(splitAndTrimEmpty(n.config.P2P.PrivatePeerIDs, ",", " ")) @@ -582,8 +592,7 @@ func (n *Node) OnStart() error { } } - // start tx indexer - return n.indexerService.Start() + return nil } // OnStop stops the Node. It implements cmn.Service. diff --git a/state/execution.go b/state/execution.go index 85bbd3827..d59c8af03 100644 --- a/state/execution.go +++ b/state/execution.go @@ -49,8 +49,7 @@ func BlockExecutorWithMetrics(metrics *Metrics) BlockExecutorOption { // NewBlockExecutor returns a new BlockExecutor with a NopEventBus. // Call SetEventBus to provide one. -func NewBlockExecutor(db dbm.DB, logger log.Logger, proxyApp proxy.AppConnConsensus, - mempool Mempool, evpool EvidencePool, options ...BlockExecutorOption) *BlockExecutor { +func NewBlockExecutor(db dbm.DB, logger log.Logger, proxyApp proxy.AppConnConsensus, mempool Mempool, evpool EvidencePool, options ...BlockExecutorOption) *BlockExecutor { res := &BlockExecutor{ db: db, proxyApp: proxyApp, diff --git a/state/execution_test.go b/state/execution_test.go index 21df1ee56..b14ee6492 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -309,8 +309,8 @@ func TestEndBlockValidatorUpdates(t *testing.T) { state, stateDB := state(1, 1) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), - MockMempool{}, MockEvidencePool{}) + blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), MockMempool{}, MockEvidencePool{}) + eventBus := types.NewEventBus() err = eventBus.Start() require.NoError(t, err) From 8d2dd7e554250cd1e912d4cd9b9ff829c2110ab3 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 28 Jan 2019 12:38:11 +0400 Subject: [PATCH 009/259] refactor TestListenerConnectDeadlines to avoid data races (#3201) Fixes #3179 --- privval/socket_test.go | 43 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/privval/socket_test.go b/privval/socket_test.go index b411b7f3a..7f7bbd892 100644 --- a/privval/socket_test.go +++ b/privval/socket_test.go @@ -98,36 +98,29 @@ func TestListenerAcceptDeadlines(t *testing.T) { func TestListenerConnectDeadlines(t *testing.T) { for _, tc := range listenerTestCases(t, time.Second, time.Millisecond) { - readyc := make(chan struct{}) - donec := make(chan struct{}) - go func(ln net.Listener) { - defer close(donec) - - c, err := ln.Accept() + go func(dialer Dialer) { + _, err := dialer() if err != nil { - t.Fatal(err) + panic(err) } - <-readyc + }(tc.dialer) - time.Sleep(2 * time.Millisecond) - - msg := make([]byte, 200) - _, err = c.Read(msg) - opErr, ok := err.(*net.OpError) - if !ok { - t.Fatalf("for %s listener, have %v, want *net.OpError", tc.description, err) - } - - if have, want := opErr.Op, "read"; have != want { - t.Errorf("for %s listener, have %v, want %v", tc.description, have, want) - } - }(tc.listener) - - _, err := tc.dialer() + c, err := tc.listener.Accept() if err != nil { t.Fatal(err) } - close(readyc) - <-donec + + time.Sleep(2 * time.Millisecond) + + msg := make([]byte, 200) + _, err = c.Read(msg) + opErr, ok := err.(*net.OpError) + if !ok { + t.Fatalf("for %s listener, have %v, want *net.OpError", tc.description, err) + } + + if have, want := opErr.Op, "read"; have != want { + t.Errorf("for %s listener, have %v, want %v", tc.description, have, want) + } } } From ff3c4bfc765199c31af6487661e1b23aa8be7037 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 28 Jan 2019 14:57:47 +0400 Subject: [PATCH 010/259] add go-deadlock tool to help detect deadlocks (#3218) * add go-deadlock tool to help detect deadlocks Run it with `make test_with_deadlock`. After it's done, use Git to cleanup `git checkout .` Link: https://github.com/sasha-s/go-deadlock/ Replaces https://github.com/tendermint/tendermint/pull/3148 * add a target to cleanup changes --- CHANGELOG_PENDING.md | 1 + Makefile | 15 ++++++++++++++- scripts/get_tools.sh | 5 +++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 6b7677b52..ac4b16284 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -19,6 +19,7 @@ Special thanks to external contributors on this release: ### FEATURES: ### IMPROVEMENTS: +- [tools] add go-deadlock tool to help detect deadlocks ### BUG FIXES: - [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution diff --git a/Makefile b/Makefile index 1250fccb9..853a01c94 100644 --- a/Makefile +++ b/Makefile @@ -226,6 +226,19 @@ test_race: @echo "--> Running go test --race" @GOCACHE=off go test -p 1 -v -race $(PACKAGES) +# uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks +test_with_deadlock: + find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.RWMutex/deadlock.RWMutex/' + find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.Mutex/deadlock.Mutex/' + find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w + make test + make cleanup_after_test_with_deadlock + +# cleanes up after you ran test_with_deadlock +cleanup_after_test_with_deadlock: + find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/deadlock.RWMutex/sync.RWMutex/' + find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/deadlock.Mutex/sync.Mutex/' + find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w ######################################## ### Formatting, linting, and vetting @@ -330,4 +343,4 @@ build-slate: # 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 build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c +.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock diff --git a/scripts/get_tools.sh b/scripts/get_tools.sh index 955ec943a..47077c104 100755 --- a/scripts/get_tools.sh +++ b/scripts/get_tools.sh @@ -51,3 +51,8 @@ installFromGithub golang/dep 22125cfaa6ddc71e145b1535d4b7ee9744fefff2 cmd/dep installFromGithub alecthomas/gometalinter 17a7ffa42374937bfecabfb8d2efbd4db0c26741 installFromGithub gogo/protobuf 61dbc136cf5d2f08d68a011382652244990a53a9 protoc-gen-gogo installFromGithub square/certstrap e27060a3643e814151e65b9807b6b06d169580a7 + +## make test_with_deadlock +installFromGithub petermattis/goid b0b1615b78e5ee59739545bb38426383b2cda4c9 +installFromGithub sasha-s/go-deadlock d68e2bc52ae3291765881b9056f2c1527f245f1e +go get golang.org/x/tools/cmd/goimports From a335caaedb5e0e700b7397864d0423c9158b7359 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Mon, 28 Jan 2019 14:13:17 +0200 Subject: [PATCH 011/259] alias amino imports (#3219) As per conversation here: https://github.com/tendermint/tendermint/pull/3218#discussion_r251364041 This is the result of running the following code on the repo: ```bash find . -name '*.go' | grep -v 'vendor/' | xargs -n 1 goimports -w ``` --- benchmarks/codec_test.go | 2 +- blockchain/wire.go | 2 +- cmd/tendermint/commands/wire.go | 2 +- consensus/reactor.go | 8 ++++---- consensus/reactor_test.go | 2 +- consensus/replay.go | 1 + consensus/state.go | 2 +- consensus/types/round_state_test.go | 2 +- consensus/wal_test.go | 1 + consensus/wire.go | 2 +- crypto/merkle/proof_test.go | 2 +- crypto/merkle/wire.go | 2 +- evidence/wire.go | 2 +- mempool/wire.go | 2 +- node/node.go | 2 +- p2p/conn/wire.go | 2 +- p2p/pex/wire.go | 2 +- p2p/wire.go | 2 +- privval/remote_signer.go | 2 +- privval/wire.go | 2 +- rpc/client/rpc_test.go | 2 +- rpc/core/types/wire.go | 2 +- rpc/grpc/grpc_test.go | 4 ++-- rpc/lib/client/args_test.go | 3 +-- rpc/lib/client/http_client.go | 2 +- rpc/lib/client/ws_client.go | 2 +- rpc/lib/types/types_test.go | 2 +- scripts/json2wal/main.go | 2 +- scripts/wal2json/main.go | 2 +- state/txindex/kv/wire.go | 2 +- state/wire.go | 2 +- tools/tm-monitor/mock/eventmeter.go | 2 +- tools/tm-monitor/monitor/monitor.go | 2 +- tools/tm-monitor/monitor/monitor_test.go | 2 +- tools/tm-monitor/monitor/node.go | 12 ++++++------ types/protobuf_test.go | 3 +-- types/tx.go | 2 +- types/wire.go | 2 +- 38 files changed, 47 insertions(+), 47 deletions(-) diff --git a/benchmarks/codec_test.go b/benchmarks/codec_test.go index 3e0270286..eff5c7349 100644 --- a/benchmarks/codec_test.go +++ b/benchmarks/codec_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" proto "github.com/tendermint/tendermint/benchmarks/proto" "github.com/tendermint/tendermint/crypto/ed25519" diff --git a/blockchain/wire.go b/blockchain/wire.go index 91156fa8f..487fbe2bc 100644 --- a/blockchain/wire.go +++ b/blockchain/wire.go @@ -1,7 +1,7 @@ package blockchain import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/types" ) diff --git a/cmd/tendermint/commands/wire.go b/cmd/tendermint/commands/wire.go index 0f0b536df..322f92b3f 100644 --- a/cmd/tendermint/commands/wire.go +++ b/cmd/tendermint/commands/wire.go @@ -1,7 +1,7 @@ package commands import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/consensus/reactor.go b/consensus/reactor.go index 1f508319d..b92ae1f72 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cstypes "github.com/tendermint/tendermint/consensus/types" cmn "github.com/tendermint/tendermint/libs/common" tmevents "github.com/tendermint/tendermint/libs/events" @@ -438,9 +438,9 @@ func (conR *ConsensusReactor) broadcastHasVoteMessage(vote *types.Vote) { func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) { nrsMsg = &NewRoundStepMessage{ - Height: rs.Height, - Round: rs.Round, - Step: rs.Step, + Height: rs.Height, + Round: rs.Round, + Step: rs.Step, SecondsSinceStartTime: int(time.Since(rs.StartTime).Seconds()), LastCommitRound: rs.LastCommit.Round(), } diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 4772108b2..28e245aec 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -14,7 +14,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/abci/client" + abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" bc "github.com/tendermint/tendermint/blockchain" diff --git a/consensus/replay.go b/consensus/replay.go index 96e23b7b8..3ac636577 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -6,6 +6,7 @@ import ( "hash/crc32" "io" "reflect" + //"strconv" //"strings" "time" diff --git a/consensus/state.go b/consensus/state.go index c69dfb874..158e1605e 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -94,7 +94,7 @@ type ConsensusState struct { // internal state mtx sync.RWMutex cstypes.RoundState - state sm.State // State until height-1. + state sm.State // State until height-1. // state changes may be triggered by: msgs from peers, // msgs from ourself, or by timeouts diff --git a/consensus/types/round_state_test.go b/consensus/types/round_state_test.go index 6a1c4533a..c2bc9f7c2 100644 --- a/consensus/types/round_state_test.go +++ b/consensus/types/round_state_test.go @@ -3,7 +3,7 @@ package types import ( "testing" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/types" diff --git a/consensus/wal_test.go b/consensus/wal_test.go index c056f2017..b2711fb4a 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -7,6 +7,7 @@ import ( "io/ioutil" "os" "path/filepath" + // "sync" "testing" "time" diff --git a/consensus/wire.go b/consensus/wire.go index 567e6095e..ecd092a1b 100644 --- a/consensus/wire.go +++ b/consensus/wire.go @@ -1,7 +1,7 @@ package consensus import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/types" ) diff --git a/crypto/merkle/proof_test.go b/crypto/merkle/proof_test.go index 320b9188a..2a0bdccfc 100644 --- a/crypto/merkle/proof_test.go +++ b/crypto/merkle/proof_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/stretchr/testify/assert" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cmn "github.com/tendermint/tendermint/libs/common" ) diff --git a/crypto/merkle/wire.go b/crypto/merkle/wire.go index c20ec9aa4..2b6ee350b 100644 --- a/crypto/merkle/wire.go +++ b/crypto/merkle/wire.go @@ -1,7 +1,7 @@ package merkle import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) var cdc *amino.Codec diff --git a/evidence/wire.go b/evidence/wire.go index 866559535..6752bc3e2 100644 --- a/evidence/wire.go +++ b/evidence/wire.go @@ -1,7 +1,7 @@ package evidence import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/types" ) diff --git a/mempool/wire.go b/mempool/wire.go index ed0897268..9224af413 100644 --- a/mempool/wire.go +++ b/mempool/wire.go @@ -1,7 +1,7 @@ package mempool import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) var cdc = amino.NewCodec() diff --git a/node/node.go b/node/node.go index b7ca6517d..1b7319811 100644 --- a/node/node.go +++ b/node/node.go @@ -34,7 +34,7 @@ import ( rpccore "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" grpccore "github.com/tendermint/tendermint/rpc/grpc" - "github.com/tendermint/tendermint/rpc/lib/server" + rpcserver "github.com/tendermint/tendermint/rpc/lib/server" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/state/txindex/kv" diff --git a/p2p/conn/wire.go b/p2p/conn/wire.go index 4bd778c74..5231a6ca1 100644 --- a/p2p/conn/wire.go +++ b/p2p/conn/wire.go @@ -1,7 +1,7 @@ package conn import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/p2p/pex/wire.go b/p2p/pex/wire.go index 57fc93858..c88b1941c 100644 --- a/p2p/pex/wire.go +++ b/p2p/pex/wire.go @@ -1,7 +1,7 @@ package pex import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) var cdc *amino.Codec = amino.NewCodec() diff --git a/p2p/wire.go b/p2p/wire.go index 40176e3af..191e3c523 100644 --- a/p2p/wire.go +++ b/p2p/wire.go @@ -1,7 +1,7 @@ package p2p import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/privval/remote_signer.go b/privval/remote_signer.go index 1371e333b..a5b8cac64 100644 --- a/privval/remote_signer.go +++ b/privval/remote_signer.go @@ -7,7 +7,7 @@ import ( "github.com/pkg/errors" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/types" diff --git a/privval/wire.go b/privval/wire.go index 2e11677e4..637039b77 100644 --- a/privval/wire.go +++ b/privval/wire.go @@ -1,7 +1,7 @@ package privval import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index dac7ec12d..8ae88f43b 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -12,7 +12,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/rpc/client" - "github.com/tendermint/tendermint/rpc/test" + rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" ) diff --git a/rpc/core/types/wire.go b/rpc/core/types/wire.go index ef1fa8001..2180b5a5a 100644 --- a/rpc/core/types/wire.go +++ b/rpc/core/types/wire.go @@ -1,7 +1,7 @@ package core_types import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/types" ) diff --git a/rpc/grpc/grpc_test.go b/rpc/grpc/grpc_test.go index eda3896fb..ff05c835f 100644 --- a/rpc/grpc/grpc_test.go +++ b/rpc/grpc/grpc_test.go @@ -8,8 +8,8 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/example/kvstore" - "github.com/tendermint/tendermint/rpc/grpc" - "github.com/tendermint/tendermint/rpc/test" + core_grpc "github.com/tendermint/tendermint/rpc/grpc" + rpctest "github.com/tendermint/tendermint/rpc/test" ) func TestMain(m *testing.M) { diff --git a/rpc/lib/client/args_test.go b/rpc/lib/client/args_test.go index cb7a56bd5..e3dd09e8f 100644 --- a/rpc/lib/client/args_test.go +++ b/rpc/lib/client/args_test.go @@ -5,8 +5,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) type Tx []byte diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 21be5fe0c..97b8dfe7b 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -12,7 +12,7 @@ import ( "strings" "github.com/pkg/errors" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" types "github.com/tendermint/tendermint/rpc/lib/types" ) diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go index b183118d9..e3b559569 100644 --- a/rpc/lib/client/ws_client.go +++ b/rpc/lib/client/ws_client.go @@ -13,7 +13,7 @@ import ( "github.com/pkg/errors" metrics "github.com/rcrowley/go-metrics" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cmn "github.com/tendermint/tendermint/libs/common" types "github.com/tendermint/tendermint/rpc/lib/types" ) diff --git a/rpc/lib/types/types_test.go b/rpc/lib/types/types_test.go index 3e8851326..a5b2da9ce 100644 --- a/rpc/lib/types/types_test.go +++ b/rpc/lib/types/types_test.go @@ -8,7 +8,7 @@ import ( "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) type SampleResult struct { diff --git a/scripts/json2wal/main.go b/scripts/json2wal/main.go index 9611b9b56..dd3e29d0f 100644 --- a/scripts/json2wal/main.go +++ b/scripts/json2wal/main.go @@ -14,7 +14,7 @@ import ( "os" "strings" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cs "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/types" ) diff --git a/scripts/wal2json/main.go b/scripts/wal2json/main.go index cf8ae86c1..ee90ecaa4 100644 --- a/scripts/wal2json/main.go +++ b/scripts/wal2json/main.go @@ -12,7 +12,7 @@ import ( "io" "os" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cs "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/types" ) diff --git a/state/txindex/kv/wire.go b/state/txindex/kv/wire.go index ccca75254..de168b228 100644 --- a/state/txindex/kv/wire.go +++ b/state/txindex/kv/wire.go @@ -1,7 +1,7 @@ package kv import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" ) var cdc = amino.NewCodec() diff --git a/state/wire.go b/state/wire.go index eeb156d67..f7a611294 100644 --- a/state/wire.go +++ b/state/wire.go @@ -1,7 +1,7 @@ package state import ( - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) diff --git a/tools/tm-monitor/mock/eventmeter.go b/tools/tm-monitor/mock/eventmeter.go index 271297581..7bbedc7fa 100644 --- a/tools/tm-monitor/mock/eventmeter.go +++ b/tools/tm-monitor/mock/eventmeter.go @@ -4,7 +4,7 @@ import ( stdlog "log" "reflect" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/libs/log" em "github.com/tendermint/tendermint/tools/tm-monitor/eventmeter" ) diff --git a/tools/tm-monitor/monitor/monitor.go b/tools/tm-monitor/monitor/monitor.go index 764f281ff..86022c31a 100644 --- a/tools/tm-monitor/monitor/monitor.go +++ b/tools/tm-monitor/monitor/monitor.go @@ -46,7 +46,7 @@ func NewMonitor(options ...func(*Monitor)) *Monitor { nodeQuit: make(map[string]chan struct{}), recalculateNetworkUptimeEvery: 10 * time.Second, numValidatorsUpdateInterval: 5 * time.Second, - logger: log.NewNopLogger(), + logger: log.NewNopLogger(), } for _, option := range options { diff --git a/tools/tm-monitor/monitor/monitor_test.go b/tools/tm-monitor/monitor/monitor_test.go index 9694e5771..324f43f74 100644 --- a/tools/tm-monitor/monitor/monitor_test.go +++ b/tools/tm-monitor/monitor/monitor_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto/ed25519" ctypes "github.com/tendermint/tendermint/rpc/core/types" mock "github.com/tendermint/tendermint/tools/tm-monitor/mock" diff --git a/tools/tm-monitor/monitor/node.go b/tools/tm-monitor/monitor/node.go index 1dc113a6d..6f7051456 100644 --- a/tools/tm-monitor/monitor/node.go +++ b/tools/tm-monitor/monitor/node.go @@ -55,13 +55,13 @@ func NewNode(rpcAddr string, options ...func(*Node)) *Node { func NewNodeWithEventMeterAndRpcClient(rpcAddr string, em eventMeter, rpcClient rpc_client.HTTPClient, options ...func(*Node)) *Node { n := &Node{ - rpcAddr: rpcAddr, - em: em, - rpcClient: rpcClient, - Name: rpcAddr, - quit: make(chan struct{}), + rpcAddr: rpcAddr, + em: em, + rpcClient: rpcClient, + Name: rpcAddr, + quit: make(chan struct{}), checkIsValidatorInterval: 5 * time.Second, - logger: log.NewNopLogger(), + logger: log.NewNopLogger(), } for _, option := range options { diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 18acf57a6..40859d9e3 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -7,8 +7,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" - "github.com/tendermint/go-amino" - + amino "github.com/tendermint/go-amino" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" diff --git a/types/tx.go b/types/tx.go index 87d387a02..0c6845a7d 100644 --- a/types/tx.go +++ b/types/tx.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/merkle" diff --git a/types/wire.go b/types/wire.go index 923048019..81a7bf76a 100644 --- a/types/wire.go +++ b/types/wire.go @@ -2,7 +2,7 @@ package types import ( amino "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/crypto/encoding/amino" + cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino" ) var cdc = amino.NewCodec() From 9a0bfafef6eb2f10a07006fde7780ed191ebdf19 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Mon, 28 Jan 2019 15:41:39 +0200 Subject: [PATCH 012/259] docs: fix links (#3220) Because there's nothing worse than having to copy/paste a link from a web page to navigate to it :grin: --- docs/tendermint-core/rpc.md | 2 +- docs/tools/benchmarking.md | 2 +- docs/tools/monitoring.md | 2 +- tools/tm-bench/README.md | 2 +- tools/tm-monitor/README.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/tendermint-core/rpc.md b/docs/tendermint-core/rpc.md index 7ae59f0d3..4ea5ab0d9 100644 --- a/docs/tendermint-core/rpc.md +++ b/docs/tendermint-core/rpc.md @@ -2,6 +2,6 @@ The RPC documentation is hosted here: -- https://tendermint.com/rpc/ +- [https://tendermint.com/rpc/](https://tendermint.com/rpc/) To update the documentation, edit the relevant `godoc` comments in the [rpc/core directory](https://github.com/tendermint/tendermint/tree/develop/rpc/core). diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md index e17c28564..67a472e4b 100644 --- a/docs/tools/benchmarking.md +++ b/docs/tools/benchmarking.md @@ -2,7 +2,7 @@ Tendermint blockchain benchmarking tool: -- https://github.com/tendermint/tools/tree/master/tm-bench +- [https://github.com/tendermint/tendermint/tree/master/tools/tm-bench](https://github.com/tendermint/tendermint/tree/master/tools/tm-bench) For example, the following: diff --git a/docs/tools/monitoring.md b/docs/tools/monitoring.md index c0fa94c09..fa3901dde 100644 --- a/docs/tools/monitoring.md +++ b/docs/tools/monitoring.md @@ -3,7 +3,7 @@ Tendermint blockchain monitoring tool; watches over one or more nodes, collecting and providing various statistics to the user: -- https://github.com/tendermint/tendermint/tree/master/tools/tm-monitor +- [https://github.com/tendermint/tendermint/tree/master/tools/tm-monitor](https://github.com/tendermint/tendermint/tree/master/tools/tm-monitor) ## Quick Start diff --git a/tools/tm-bench/README.md b/tools/tm-bench/README.md index 9159a7546..b4e8cec5a 100644 --- a/tools/tm-bench/README.md +++ b/tools/tm-bench/README.md @@ -2,7 +2,7 @@ Tendermint blockchain benchmarking tool: -- https://github.com/tendermint/tools/tree/master/tm-bench +- [https://github.com/tendermint/tendermint/tree/master/tools/tm-bench](https://github.com/tendermint/tendermint/tree/master/tools/tm-bench) For example, the following: `tm-bench -T 30 -r 10000 localhost:26657` diff --git a/tools/tm-monitor/README.md b/tools/tm-monitor/README.md index cf4216849..374a56b0a 100644 --- a/tools/tm-monitor/README.md +++ b/tools/tm-monitor/README.md @@ -3,7 +3,7 @@ Tendermint blockchain monitoring tool; watches over one or more nodes, collecting and providing various statistics to the user: -- https://github.com/tendermint/tools/tree/master/tm-monitor +- [https://github.com/tendermint/tendermint/tree/master/tools/tm-monitor](https://github.com/tendermint/tendermint/tree/master/tools/tm-monitor) ## Quick Start From e1edd2aa6a860d505313b73cba4cb74fa4de10fc Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 28 Jan 2019 14:41:37 -0500 Subject: [PATCH 013/259] hardcode rpc link (#3223) --- docs/.vuepress/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 5ecc97cf5..3e55a2404 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -21,7 +21,7 @@ module.exports = { }, nav: [ { text: "Back to Tendermint", link: "https://tendermint.com" }, - { text: "RPC", link: "../rpc/" } + { text: "RPC", link: "https://tendermint.com/rpc/" } ], sidebar: [ { From 0b3a87a3232d59ef51ee810ca8f3645504ab6d2f Mon Sep 17 00:00:00 2001 From: rickyyangz <38900912+rickyyangz@users.noreply.github.com> Date: Tue, 29 Jan 2019 14:12:07 +0800 Subject: [PATCH 014/259] mempool: correct args order in the log msg (#3221) Before: Unexpected tx response from proxy during recheck\n Expected: {r.CheckTx.Data}, got: {memTx.tx} After: Unexpected tx response from proxy during recheck\n Expected: {memTx.tx}, got: {tx} Closes #3214 --- mempool/mempool.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/mempool/mempool.go b/mempool/mempool.go index 9069dab62..8550f2f81 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -408,14 +408,11 @@ func (mem *Mempool) resCbRecheck(req *abci.Request, res *abci.Response) { case *abci.Response_CheckTx: tx := req.GetCheckTx().Tx memTx := mem.recheckCursor.Value.(*mempoolTx) - if !bytes.Equal(req.GetCheckTx().Tx, memTx.tx) { - cmn.PanicSanity( - fmt.Sprintf( - "Unexpected tx response from proxy during recheck\nExpected %X, got %X", - r.CheckTx.Data, - memTx.tx, - ), - ) + if !bytes.Equal(tx, memTx.tx) { + panic(fmt.Sprintf( + "Unexpected tx response from proxy during recheck\nExpected %X, got %X", + memTx.tx, + tx)) } var postCheckErr error if mem.postCheck != nil { From 6dd817cbbcd9b0b2e2672d8871bce1c5ff385856 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 29 Jan 2019 09:44:59 +0100 Subject: [PATCH 015/259] secret connection: check for low order points (#3040) > Implement a check for the blacklisted low order points, ala the X25519 has_small_order() function in libsodium (#3010 (comment)) resolves first half of #3010 --- p2p/conn/secret_connection.go | 52 ++++++++++++++++++++++++++++++ p2p/conn/secret_connection_test.go | 26 +++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index aa019aa31..d2ba6fb51 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -4,6 +4,7 @@ import ( "bytes" crand "crypto/rand" "crypto/sha256" + "crypto/subtle" "encoding/binary" "errors" "io" @@ -28,6 +29,8 @@ const aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag const aeadKeySize = chacha20poly1305.KeySize const aeadNonceSize = chacha20poly1305.NonceSize +var ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer") + // SecretConnection implements net.Conn. // It is an implementation of the STS protocol. // See https://github.com/tendermint/tendermint/blob/0.1/docs/sts-final.pdf for @@ -251,6 +254,9 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3 if err2 != nil { return nil, err2, true // abort } + if hasSmallOrder(_remEphPub) { + return nil, ErrSmallOrderRemotePubKey, true + } return _remEphPub, nil, false }, ) @@ -266,6 +272,52 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3 return &_remEphPub, nil } +// use the samne blacklist as lib sodium (see https://eprint.iacr.org/2017/806.pdf for reference): +// https://github.com/jedisct1/libsodium/blob/536ed00d2c5e0c65ac01e29141d69a30455f2038/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L11-L17 +var blacklist = [][32]byte{ + // 0 (order 4) + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // 1 (order 1) + {0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + // 325606250916557431795983626356110631294008115727848805560023387167927233504 + // (order 8) + {0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, + 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32, + 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00}, + // 39382357235489614581723060781553021112529911719440698176882885853963445705823 + // (order 8) + {0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, + 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c, + 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57}, + // p-1 (order 2) + {0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + // p (=0, order 4) + {0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + // p+1 (=1, order 1) + {0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, +} + +func hasSmallOrder(pubKey [32]byte) bool { + isSmallOrderPoint := false + for _, bl := range blacklist { + if subtle.ConstantTimeCompare(pubKey[:], bl[:]) == 1 { + isSmallOrderPoint = true + break + } + } + return isSmallOrderPoint +} + func deriveSecretAndChallenge(dhSecret *[32]byte, locIsLeast bool) (recvSecret, sendSecret *[aeadKeySize]byte, challenge *[32]byte) { hash := sha256.New hkdf := hkdf.New(hash, dhSecret[:], nil, []byte("TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN")) diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 131ab9229..69d9c09fe 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -100,6 +100,32 @@ func TestSecretConnectionHandshake(t *testing.T) { } } +func TestShareLowOrderPubkey(t *testing.T) { + var fooConn, barConn = makeKVStoreConnPair() + locEphPub, _ := genEphKeys() + + // all blacklisted low order points: + for _, remLowOrderPubKey := range blacklist { + _, _ = cmn.Parallel( + func(_ int) (val interface{}, err error, abort bool) { + _, err = shareEphPubKey(fooConn, locEphPub) + + require.Error(t, err) + require.Equal(t, err, ErrSmallOrderRemotePubKey) + + return nil, nil, false + }, + func(_ int) (val interface{}, err error, abort bool) { + readRemKey, err := shareEphPubKey(barConn, &remLowOrderPubKey) + + require.NoError(t, err) + require.Equal(t, locEphPub, readRemKey) + + return nil, nil, false + }) + } +} + func TestConcurrentWrite(t *testing.T) { fooSecConn, barSecConn := makeSecretConnPair(t) fooWriteText := cmn.RandStr(dataMaxSize) From 8985a1fa63fd9d64840d187243e74b7218a7167e Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 29 Jan 2019 13:16:43 +0400 Subject: [PATCH 016/259] pubsub: fixes after Ethan's review (#3212) in https://github.com/tendermint/tendermint/pull/3209 --- libs/pubsub/pubsub.go | 112 ++++++++++++++++++++++++------------- libs/pubsub/pubsub_test.go | 19 +++++++ 2 files changed, 91 insertions(+), 40 deletions(-) diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index cb41f8b38..a39c8c731 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -101,7 +101,7 @@ type Server struct { cmdsCap int mtx sync.RWMutex - subscriptions map[string]map[string]Query // subscriber -> query (string) -> Query + subscriptions map[string]map[string]struct{} // subscriber -> query (string) -> empty struct } // Option sets a parameter for the server. @@ -143,7 +143,7 @@ func (ts tagMap) Len() int { // provided, the resulting server's queue is unbuffered. func NewServer(options ...Option) *Server { s := &Server{ - subscriptions: make(map[string]map[string]Query), + subscriptions: make(map[string]map[string]struct{}), } s.BaseService = *cmn.NewBaseService(nil, "PubSub", s) @@ -193,11 +193,9 @@ func (s *Server) Subscribe(ctx context.Context, clientID string, query Query, ou case s.cmds <- cmd{op: sub, clientID: clientID, query: query, ch: out}: s.mtx.Lock() if _, ok = s.subscriptions[clientID]; !ok { - s.subscriptions[clientID] = make(map[string]Query) + s.subscriptions[clientID] = make(map[string]struct{}) } - // preserve original query - // see Unsubscribe - s.subscriptions[clientID][query.String()] = query + s.subscriptions[clientID][query.String()] = struct{}{} s.mtx.Unlock() return nil case <-ctx.Done(): @@ -211,24 +209,23 @@ func (s *Server) Subscribe(ctx context.Context, clientID string, query Query, ou // returned to the caller if the context is canceled or if subscription does // not exist. func (s *Server) Unsubscribe(ctx context.Context, clientID string, query Query) error { - var origQuery Query s.mtx.RLock() clientSubscriptions, ok := s.subscriptions[clientID] if ok { - origQuery, ok = clientSubscriptions[query.String()] + _, ok = clientSubscriptions[query.String()] } s.mtx.RUnlock() if !ok { return ErrSubscriptionNotFound } - // original query is used here because we're using pointers as map keys - // ? select { - case s.cmds <- cmd{op: unsub, clientID: clientID, query: origQuery}: + case s.cmds <- cmd{op: unsub, clientID: clientID, query: query}: s.mtx.Lock() - // if its the only query left, should we also delete the client? delete(clientSubscriptions, query.String()) + if len(clientSubscriptions) == 0 { + delete(s.subscriptions, clientID) + } s.mtx.Unlock() return nil case <-ctx.Done(): @@ -288,17 +285,27 @@ func (s *Server) OnStop() { // NOTE: not goroutine safe type state struct { - // query -> client -> ch - queries map[Query]map[string]chan<- interface{} - // client -> query -> struct{} - clients map[string]map[Query]struct{} + // query string -> client -> ch + queryToChanMap map[string]map[string]chan<- interface{} + // client -> query string -> struct{} + clientToQueryMap map[string]map[string]struct{} + // query string -> queryPlusRefCount + queries map[string]*queryPlusRefCount +} + +// queryPlusRefCount holds a pointer to a query and reference counter. When +// refCount is zero, query will be removed. +type queryPlusRefCount struct { + q Query + refCount int } // OnStart implements Service.OnStart by starting the server. func (s *Server) OnStart() error { go s.loop(state{ - queries: make(map[Query]map[string]chan<- interface{}), - clients: make(map[string]map[Query]struct{}), + queryToChanMap: make(map[string]map[string]chan<- interface{}), + clientToQueryMap: make(map[string]map[string]struct{}), + queries: make(map[string]*queryPlusRefCount), }) return nil } @@ -319,7 +326,7 @@ loop: state.removeAll(cmd.clientID) } case shutdown: - for clientID := range state.clients { + for clientID := range state.clientToQueryMap { state.removeAll(clientID) } break loop @@ -332,24 +339,34 @@ loop: } func (state *state) add(clientID string, q Query, ch chan<- interface{}) { + qStr := q.String() // initialize clientToChannelMap per query if needed - if _, ok := state.queries[q]; !ok { - state.queries[q] = make(map[string]chan<- interface{}) + if _, ok := state.queryToChanMap[qStr]; !ok { + state.queryToChanMap[qStr] = make(map[string]chan<- interface{}) } // create subscription - state.queries[q][clientID] = ch + state.queryToChanMap[qStr][clientID] = ch + + // initialize queries if needed + if _, ok := state.queries[qStr]; !ok { + state.queries[qStr] = &queryPlusRefCount{q: q, refCount: 0} + } + // increment reference counter + state.queries[qStr].refCount++ // add client if needed - if _, ok := state.clients[clientID]; !ok { - state.clients[clientID] = make(map[Query]struct{}) + if _, ok := state.clientToQueryMap[clientID]; !ok { + state.clientToQueryMap[clientID] = make(map[string]struct{}) } - state.clients[clientID][q] = struct{}{} + state.clientToQueryMap[clientID][qStr] = struct{}{} } func (state *state) remove(clientID string, q Query) { - clientToChannelMap, ok := state.queries[q] + qStr := q.String() + + clientToChannelMap, ok := state.queryToChanMap[qStr] if !ok { return } @@ -363,43 +380,58 @@ func (state *state) remove(clientID string, q Query) { // remove the query from client map. // if client is not subscribed to anything else, remove it. - delete(state.clients[clientID], q) - if len(state.clients[clientID]) == 0 { - delete(state.clients, clientID) + delete(state.clientToQueryMap[clientID], qStr) + if len(state.clientToQueryMap[clientID]) == 0 { + delete(state.clientToQueryMap, clientID) } // remove the client from query map. // if query has no other clients subscribed, remove it. - delete(state.queries[q], clientID) - if len(state.queries[q]) == 0 { - delete(state.queries, q) + delete(state.queryToChanMap[qStr], clientID) + if len(state.queryToChanMap[qStr]) == 0 { + delete(state.queryToChanMap, qStr) + } + + // decrease ref counter in queries + state.queries[qStr].refCount-- + // remove the query if nobody else is using it + if state.queries[qStr].refCount == 0 { + delete(state.queries, qStr) } } func (state *state) removeAll(clientID string) { - queryMap, ok := state.clients[clientID] + queryMap, ok := state.clientToQueryMap[clientID] if !ok { return } - for q := range queryMap { - ch := state.queries[q][clientID] + for qStr := range queryMap { + ch := state.queryToChanMap[qStr][clientID] close(ch) // remove the client from query map. // if query has no other clients subscribed, remove it. - delete(state.queries[q], clientID) - if len(state.queries[q]) == 0 { - delete(state.queries, q) + delete(state.queryToChanMap[qStr], clientID) + if len(state.queryToChanMap[qStr]) == 0 { + delete(state.queryToChanMap, qStr) + } + + // decrease ref counter in queries + state.queries[qStr].refCount-- + // remove the query if nobody else is using it + if state.queries[qStr].refCount == 0 { + delete(state.queries, qStr) } } // remove the client. - delete(state.clients, clientID) + delete(state.clientToQueryMap, clientID) } func (state *state) send(msg interface{}, tags TagMap) { - for q, clientToChannelMap := range state.queries { + for qStr, clientToChannelMap := range state.queryToChanMap { + q := state.queries[qStr].q if q.Matches(tags) { for _, ch := range clientToChannelMap { ch <- msg diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index 5e9931e40..bb660d9e0 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -115,6 +115,25 @@ func TestUnsubscribe(t *testing.T) { assert.False(t, ok) } +func TestClientUnsubscribesTwice(t *testing.T) { + s := pubsub.NewServer() + s.SetLogger(log.TestingLogger()) + s.Start() + defer s.Stop() + + ctx := context.Background() + ch := make(chan interface{}) + err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"), ch) + require.NoError(t, err) + err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) + require.NoError(t, err) + + err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) + assert.Equal(t, pubsub.ErrSubscriptionNotFound, err) + err = s.UnsubscribeAll(ctx, clientID) + assert.Equal(t, pubsub.ErrSubscriptionNotFound, err) +} + func TestResubscribe(t *testing.T) { s := pubsub.NewServer() s.SetLogger(log.TestingLogger()) From d47094550315c094512a242445e0dde24b5a03f5 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 30 Jan 2019 12:24:26 +0400 Subject: [PATCH 017/259] update gometalinter to 3.0.0 (#3233) in the attempt to fix https://circleci.com/gh/tendermint/tendermint/43165 also code is simplified by running gofmt -s . remove unused vars enable linters we're currently passing remove deprecated linters --- Makefile | 42 +++++++++++------------ abci/types/messages_test.go | 2 +- cmd/tendermint/commands/root_test.go | 4 --- consensus/wal_test.go | 4 +-- crypto/merkle/rfc6962_test.go | 2 +- evidence/pool_test.go | 2 -- evidence/reactor.go | 2 +- libs/flowrate/io_test.go | 16 ++++----- libs/pubsub/query/query_test.go | 6 ++-- p2p/conn/connection_test.go | 2 +- p2p/transport_test.go | 8 ++--- privval/client_test.go | 4 +-- scripts/get_tools.sh | 4 +-- state/txindex/kv/kv_test.go | 4 +-- tools/tm-monitor/eventmeter/eventmeter.go | 2 +- types/genesis_test.go | 6 ++-- types/part_set.go | 3 -- 17 files changed, 52 insertions(+), 61 deletions(-) diff --git a/Makefile b/Makefile index 853a01c94..8c0928d04 100644 --- a/Makefile +++ b/Makefile @@ -249,31 +249,31 @@ fmt: metalinter: @echo "--> Running linter" @gometalinter $(LINT_FLAGS) --disable-all \ + --enable=vet \ + --enable=vetshadow \ --enable=deadcode \ - --enable=gosimple \ + --enable=varcheck \ + --enable=structcheck \ --enable=misspell \ --enable=safesql \ + --enable=gosec \ + --enable=goimports \ + --enable=gofmt \ ./... - #--enable=gas \ - #--enable=maligned \ - #--enable=dupl \ - #--enable=errcheck \ - #--enable=goconst \ - #--enable=gocyclo \ - #--enable=goimports \ - #--enable=golint \ <== comments on anything exported #--enable=gotype \ - #--enable=ineffassign \ - #--enable=interfacer \ - #--enable=megacheck \ - #--enable=staticcheck \ - #--enable=structcheck \ - #--enable=unconvert \ - #--enable=unparam \ - #--enable=unused \ - #--enable=varcheck \ - #--enable=vet \ - #--enable=vetshadow \ + #--enable=gotypex \ + #--enable=gocyclo \ + #--enable=golint \ + #--enable=maligned \ + #--enable=errcheck \ + #--enable=staticcheck \ + #--enable=dupl \ + #--enable=ineffassign \ + #--enable=interfacer \ + #--enable=unconvert \ + #--enable=goconst \ + #--enable=unparam \ + #--enable=nakedret \ metalinter_all: @echo "--> Running linter (all)" @@ -343,4 +343,4 @@ build-slate: # 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 build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock +.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock metalinter metalinter_all diff --git a/abci/types/messages_test.go b/abci/types/messages_test.go index 14bc5718f..762111b61 100644 --- a/abci/types/messages_test.go +++ b/abci/types/messages_test.go @@ -83,7 +83,7 @@ func TestWriteReadMessage2(t *testing.T) { Log: phrase, GasWanted: 10, Tags: []cmn.KVPair{ - cmn.KVPair{Key: []byte("abc"), Value: []byte("def")}, + {Key: []byte("abc"), Value: []byte("def")}, }, }, // TODO: add the rest diff --git a/cmd/tendermint/commands/root_test.go b/cmd/tendermint/commands/root_test.go index e8095b387..892a49b74 100644 --- a/cmd/tendermint/commands/root_test.go +++ b/cmd/tendermint/commands/root_test.go @@ -22,10 +22,6 @@ var ( defaultRoot = os.ExpandEnv("$HOME/.some/test/dir") ) -const ( - rootName = "root" -) - // clearConfig clears env vars, the given root dir, and resets viper. func clearConfig(dir string) { if err := os.Unsetenv("TMHOME"); err != nil { diff --git a/consensus/wal_test.go b/consensus/wal_test.go index b2711fb4a..c2a7d8bba 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -68,8 +68,8 @@ func TestWALTruncate(t *testing.T) { func TestWALEncoderDecoder(t *testing.T) { now := tmtime.Now() msgs := []TimedWALMessage{ - TimedWALMessage{Time: now, Msg: EndHeightMessage{0}}, - TimedWALMessage{Time: now, Msg: timeoutInfo{Duration: time.Second, Height: 1, Round: 1, Step: types.RoundStepPropose}}, + {Time: now, Msg: EndHeightMessage{0}}, + {Time: now, Msg: timeoutInfo{Duration: time.Second, Height: 1, Round: 1, Step: types.RoundStepPropose}}, } b := new(bytes.Buffer) diff --git a/crypto/merkle/rfc6962_test.go b/crypto/merkle/rfc6962_test.go index b6413b479..52eab4228 100644 --- a/crypto/merkle/rfc6962_test.go +++ b/crypto/merkle/rfc6962_test.go @@ -26,7 +26,7 @@ import ( func TestRFC6962Hasher(t *testing.T) { _, leafHashTrail := trailsFromByteSlices([][]byte{[]byte("L123456")}) leafHash := leafHashTrail.Hash - _, leafHashTrail = trailsFromByteSlices([][]byte{[]byte{}}) + _, leafHashTrail = trailsFromByteSlices([][]byte{{}}) emptyLeafHash := leafHashTrail.Hash for _, tc := range []struct { desc string diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 0640c1da7..1f4f1a06f 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -13,8 +13,6 @@ import ( tmtime "github.com/tendermint/tendermint/types/time" ) -var mockState = sm.State{} - func TestMain(m *testing.M) { types.RegisterMockEvidences(cdc) diff --git a/evidence/reactor.go b/evidence/reactor.go index 6bb45e689..bbbab3e96 100644 --- a/evidence/reactor.go +++ b/evidence/reactor.go @@ -48,7 +48,7 @@ func (evR *EvidenceReactor) SetLogger(l log.Logger) { // It returns the list of channels for this reactor. func (evR *EvidenceReactor) GetChannels() []*p2p.ChannelDescriptor { return []*p2p.ChannelDescriptor{ - &p2p.ChannelDescriptor{ + { ID: EvidenceChannel, Priority: 5, }, diff --git a/libs/flowrate/io_test.go b/libs/flowrate/io_test.go index c84029d5e..ab2c7121f 100644 --- a/libs/flowrate/io_test.go +++ b/libs/flowrate/io_test.go @@ -81,12 +81,12 @@ func TestReader(t *testing.T) { // Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress want := []Status{ - Status{true, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - Status{true, start, _100ms, 0, 10, 1, 100, 100, 100, 100, 0, 0, 0}, - Status{true, start, _200ms, _100ms, 20, 2, 100, 100, 100, 100, 0, 0, 0}, - Status{true, start, _300ms, _200ms, 20, 3, 0, 90, 67, 100, 0, 0, 0}, - Status{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0}, - Status{false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0}, + {true, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {true, start, _100ms, 0, 10, 1, 100, 100, 100, 100, 0, 0, 0}, + {true, start, _200ms, _100ms, 20, 2, 100, 100, 100, 100, 0, 0, 0}, + {true, start, _300ms, _200ms, 20, 3, 0, 90, 67, 100, 0, 0, 0}, + {false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0}, + {false, start, _300ms, 0, 20, 3, 0, 0, 67, 100, 0, 0, 0}, } for i, s := range status { if !statusesAreEqual(&s, &want[i]) { @@ -139,8 +139,8 @@ func TestWriter(t *testing.T) { // Active, Start, Duration, Idle, Bytes, Samples, InstRate, CurRate, AvgRate, PeakRate, BytesRem, TimeRem, Progress want := []Status{ - Status{true, start, _400ms, 0, 80, 4, 200, 200, 200, 200, 20, _100ms, 80000}, - Status{true, start, _500ms, _100ms, 100, 5, 200, 200, 200, 200, 0, 0, 100000}, + {true, start, _400ms, 0, 80, 4, 200, 200, 200, 200, 20, _100ms, 80000}, + {true, start, _500ms, _100ms, 100, 5, 200, 200, 200, 200, 0, 0, 100000}, } for i, s := range status { if !statusesAreEqual(&s, &want[i]) { diff --git a/libs/pubsub/query/query_test.go b/libs/pubsub/query/query_test.go index f0d940992..d1810f466 100644 --- a/libs/pubsub/query/query_test.go +++ b/libs/pubsub/query/query_test.go @@ -73,9 +73,9 @@ func TestConditions(t *testing.T) { s string conditions []query.Condition }{ - {s: "tm.events.type='NewBlock'", conditions: []query.Condition{query.Condition{Tag: "tm.events.type", Op: query.OpEqual, Operand: "NewBlock"}}}, - {s: "tx.gas > 7 AND tx.gas < 9", conditions: []query.Condition{query.Condition{Tag: "tx.gas", Op: query.OpGreater, Operand: int64(7)}, query.Condition{Tag: "tx.gas", Op: query.OpLess, Operand: int64(9)}}}, - {s: "tx.time >= TIME 2013-05-03T14:45:00Z", conditions: []query.Condition{query.Condition{Tag: "tx.time", Op: query.OpGreaterEqual, Operand: txTime}}}, + {s: "tm.events.type='NewBlock'", conditions: []query.Condition{{Tag: "tm.events.type", Op: query.OpEqual, Operand: "NewBlock"}}}, + {s: "tx.gas > 7 AND tx.gas < 9", conditions: []query.Condition{{Tag: "tx.gas", Op: query.OpGreater, Operand: int64(7)}, {Tag: "tx.gas", Op: query.OpLess, Operand: int64(9)}}}, + {s: "tx.time >= TIME 2013-05-03T14:45:00Z", conditions: []query.Condition{{Tag: "tx.time", Op: query.OpGreaterEqual, Operand: txTime}}}, } for _, tc := range testCases { diff --git a/p2p/conn/connection_test.go b/p2p/conn/connection_test.go index a757f07a6..afad69d1d 100644 --- a/p2p/conn/connection_test.go +++ b/p2p/conn/connection_test.go @@ -30,7 +30,7 @@ func createMConnectionWithCallbacks(conn net.Conn, onReceive func(chID byte, msg cfg := DefaultMConnConfig() cfg.PingInterval = 90 * time.Millisecond cfg.PongTimeout = 45 * time.Millisecond - chDescs := []*ChannelDescriptor{&ChannelDescriptor{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} + chDescs := []*ChannelDescriptor{{ID: 0x01, Priority: 1, SendQueueCapacity: 1}} c := NewMConnectionWithConfig(conn, chDescs, onReceive, onError, cfg) c.SetLogger(log.TestingLogger()) return c diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 182b28899..7d9c17fb4 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -498,13 +498,13 @@ func TestTransportConnDuplicateIPFilter(t *testing.T) { ) cs.Set(c, []net.IP{ - net.IP{10, 0, 10, 1}, - net.IP{10, 0, 10, 2}, - net.IP{10, 0, 10, 3}, + {10, 0, 10, 1}, + {10, 0, 10, 2}, + {10, 0, 10, 3}, }) if err := filter(cs, c, []net.IP{ - net.IP{10, 0, 10, 2}, + {10, 0, 10, 2}, }); err == nil { t.Errorf("expected Peer to be rejected as duplicate") } diff --git a/privval/client_test.go b/privval/client_test.go index 72682a8d8..1aea58cf0 100644 --- a/privval/client_test.go +++ b/privval/client_test.go @@ -37,11 +37,11 @@ func socketTestCases(t *testing.T) []socketTestCase { require.NoError(t, err) unixAddr := fmt.Sprintf("unix://%s", unixFilePath) return []socketTestCase{ - socketTestCase{ + { addr: tcpAddr, dialer: DialTCPFn(tcpAddr, testConnDeadline, ed25519.GenPrivKey()), }, - socketTestCase{ + { addr: unixAddr, dialer: DialUnixFn(unixFilePath), }, diff --git a/scripts/get_tools.sh b/scripts/get_tools.sh index 47077c104..87e30a3d9 100755 --- a/scripts/get_tools.sh +++ b/scripts/get_tools.sh @@ -47,8 +47,8 @@ installFromGithub() { installFromGithub mitchellh/gox 51ed453898ca5579fea9ad1f08dff6b121d9f2e8 installFromGithub golang/dep 22125cfaa6ddc71e145b1535d4b7ee9744fefff2 cmd/dep -## gometalinter v2.0.11 -installFromGithub alecthomas/gometalinter 17a7ffa42374937bfecabfb8d2efbd4db0c26741 +## gometalinter v3.0.0 +installFromGithub alecthomas/gometalinter df395bfa67c5d0630d936c0044cf07ff05086655 installFromGithub gogo/protobuf 61dbc136cf5d2f08d68a011382652244990a53a9 protoc-gen-gogo installFromGithub square/certstrap e27060a3643e814151e65b9807b6b06d169580a7 diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index 0f2065146..b726a423c 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -184,8 +184,8 @@ func TestIndexAllTags(t *testing.T) { indexer := NewTxIndex(db.NewMemDB(), IndexAllTags()) txResult := txResultWithTags([]cmn.KVPair{ - cmn.KVPair{Key: []byte("account.owner"), Value: []byte("Ivan")}, - cmn.KVPair{Key: []byte("account.number"), Value: []byte("1")}, + {Key: []byte("account.owner"), Value: []byte("Ivan")}, + {Key: []byte("account.number"), Value: []byte("1")}, }) err := indexer.Index(txResult) diff --git a/tools/tm-monitor/eventmeter/eventmeter.go b/tools/tm-monitor/eventmeter/eventmeter.go index 185f37749..63d58b96e 100644 --- a/tools/tm-monitor/eventmeter/eventmeter.go +++ b/tools/tm-monitor/eventmeter/eventmeter.go @@ -196,7 +196,7 @@ func (em *EventMeter) RegisterDisconnectCallback(f DisconnectCallbackFunc) { // Private func (em *EventMeter) subscribe() error { - for query, _ := range em.queryToMetricMap { + for query := range em.queryToMetricMap { if err := em.wsc.Subscribe(context.TODO(), query); err != nil { return err } diff --git a/types/genesis_test.go b/types/genesis_test.go index e7f041a85..0e81187e3 100644 --- a/types/genesis_test.go +++ b/types/genesis_test.go @@ -15,9 +15,9 @@ import ( func TestGenesisBad(t *testing.T) { // test some bad ones from raw json testCases := [][]byte{ - []byte{}, // empty - []byte{1, 1, 1, 1, 1}, // junk - []byte(`{}`), // empty + {}, // empty + {1, 1, 1, 1, 1}, // junk + []byte(`{}`), // empty []byte(`{"chain_id":"mychain","validators":[{}]}`), // invalid validator // missing pub_key type []byte(`{"validators":[{"pub_key":{"value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="},"power":"10","name":""}]}`), diff --git a/types/part_set.go b/types/part_set.go index 3c1c8b299..4533fb759 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -21,9 +21,6 @@ type Part struct { Index int `json:"index"` Bytes cmn.HexBytes `json:"bytes"` Proof merkle.SimpleProof `json:"proof"` - - // Cache - hash []byte } // ValidateBasic performs basic validation. From 6485e68bebcbf4f91b1bb8fcc52e01e2c97ae6a4 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Mon, 4 Feb 2019 09:24:54 +0100 Subject: [PATCH 018/259] Use ethereum's secp256k1 lib (#3234) * switch from fork (tendermint/btcd) to orig package (btcsuite/btcd); also - remove obsolete check in test `size != -1` is always true - WIP as the serialization still needs to be wrapped * WIP: wrap signature & privkey, pubkey needs to be wrapped as well * wrap pubkey too * use "github.com/ethereum/go-ethereum/crypto/secp256k1" if cgo is available, else use "github.com/btcsuite/btcd/btcec" and take care of lower-S when verifying Annoyingly, had to disable pruning when importing github.com/ethereum/go-ethereum/ :-/ * update comment * update comment * emulate signature_nocgo.go for additional benchmarks: https://github.com/ethereum/go-ethereum/blob/592bf6a59cac9697f0491b24e5093cb759d7e44c/crypto/signature_nocgo.go#L60-L76 * use our format (r || s) in lower-s form when in the non-cgo case * remove comment about using the C library directly * vendor github.com/btcsuite/btcd too * Add test for the !cgo case * update changelog pending Closes #3162 #3163 Refs #1958, #2091, tendermint/btcd#1 --- CHANGELOG_PENDING.md | 2 + Gopkg.lock | 24 ++++---- Gopkg.toml | 16 +++++- crypto/encoding/amino/encode_test.go | 5 +- crypto/secp256k1/secp256k1.go | 30 ++-------- crypto/secp256k1/secp256k1_cgo.go | 24 ++++++++ crypto/secp256k1/secp256k1_nocgo.go | 71 ++++++++++++++++++++++++ crypto/secp256k1/secp256k1_nocgo_test.go | 39 +++++++++++++ crypto/secp256k1/secpk256k1_test.go | 2 +- 9 files changed, 169 insertions(+), 44 deletions(-) create mode 100644 crypto/secp256k1/secp256k1_cgo.go create mode 100644 crypto/secp256k1/secp256k1_nocgo.go create mode 100644 crypto/secp256k1/secp256k1_nocgo_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index ac4b16284..b66cd4e8e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,6 +20,8 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: - [tools] add go-deadlock tool to help detect deadlocks +- [crypto] \#3163 use ethereum's libsecp256k1 go-wrapper for signatures when cgo is available +- [crypto] \#3162 wrap btcd instead of forking it to keep up with fixes (used if cgo is not available) ### BUG FIXES: - [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution diff --git a/Gopkg.lock b/Gopkg.lock index 9880f3f39..9e3d5d8a4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -10,12 +10,11 @@ revision = "3a771d992973f24aa725d07868b467d1ddfceafb" [[projects]] - branch = "master" - digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7" + digest = "1:093bf93a65962e8191e3e8cd8fc6c363f83d43caca9739c906531ba7210a9904" name = "github.com/btcsuite/btcd" packages = ["btcec"] pruneopts = "UT" - revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0" + revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d" [[projects]] digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf" @@ -35,6 +34,14 @@ revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" +[[projects]] + digest = "1:b42be5a3601f833e0b9f2d6625d887ec1309764bfcac3d518f3db425dcd4ec5c" + name = "github.com/ethereum/go-ethereum" + packages = ["crypto/secp256k1"] + pruneopts = "T" + revision = "9dc5d1a915ac0e0bd8429d6ac41df50eec91de5f" + version = "v1.8.21" + [[projects]] digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70" name = "github.com/fortytw2/leaktest" @@ -360,14 +367,6 @@ pruneopts = "UT" revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43" -[[projects]] - digest = "1:83f5e189eea2baad419a6a410984514266ff690075759c87e9ede596809bd0b8" - name = "github.com/tendermint/btcd" - packages = ["btcec"] - pruneopts = "UT" - revision = "80daadac05d1cd29571fccf27002d79667a88b58" - version = "v0.1.1" - [[projects]] digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a" name = "github.com/tendermint/go-amino" @@ -504,8 +503,10 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/btcsuite/btcd/btcec", "github.com/btcsuite/btcutil/base58", "github.com/btcsuite/btcutil/bech32", + "github.com/ethereum/go-ethereum/crypto/secp256k1", "github.com/fortytw2/leaktest", "github.com/go-kit/kit/log", "github.com/go-kit/kit/log/level", @@ -535,7 +536,6 @@ "github.com/syndtr/goleveldb/leveldb/errors", "github.com/syndtr/goleveldb/leveldb/iterator", "github.com/syndtr/goleveldb/leveldb/opt", - "github.com/tendermint/btcd/btcec", "github.com/tendermint/go-amino", "golang.org/x/crypto/bcrypt", "golang.org/x/crypto/chacha20poly1305", diff --git a/Gopkg.toml b/Gopkg.toml index 72ec66594..e5d6b8da2 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -75,14 +75,26 @@ name = "github.com/prometheus/client_golang" version = "^0.9.1" +# we use the secp256k1 implementation: [[constraint]] - name = "github.com/tendermint/btcd" - version = "v0.1.1" + name = "github.com/ethereum/go-ethereum" + version = "^v1.8.21" + + # Prevent dep from pruning build scripts and codegen templates + # note: this leaves the whole go-ethereum package in vendor + # can be removed when https://github.com/golang/dep/issues/1847 is resolved + [[prune.project]] + name = "github.com/ethereum/go-ethereum" + unused-packages = false ################################### ## Some repos dont have releases. ## Pin to revision +[[constraint]] + name = "github.com/btcsuite/btcd" + revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d" + [[constraint]] name = "golang.org/x/crypto" revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index c8cb24136..955103069 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -25,9 +25,8 @@ func checkAminoBinary(t *testing.T, src, dst interface{}, size int) { assert.Equal(t, byterSrc.Bytes(), bz, "Amino binary vs Bytes() mismatch") } // Make sure we have the expected length. - if size != -1 { - assert.Equal(t, size, len(bz), "Amino binary size mismatch") - } + assert.Equal(t, size, len(bz), "Amino binary size mismatch") + // Unmarshal. err = cdc.UnmarshalBinaryBare(bz, dst) require.Nil(t, err, "%+v", err) diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index d3528fdd6..78857c45c 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -7,10 +7,12 @@ import ( "fmt" "io" - secp256k1 "github.com/tendermint/btcd/btcec" - amino "github.com/tendermint/go-amino" "golang.org/x/crypto/ripemd160" + secp256k1 "github.com/btcsuite/btcd/btcec" + + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" ) @@ -44,16 +46,6 @@ func (privKey PrivKeySecp256k1) Bytes() []byte { return cdc.MustMarshalBinaryBare(privKey) } -// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. -func (privKey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) { - priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) - sig, err := priv.Sign(crypto.Sha256(msg)) - if err != nil { - return nil, err - } - return sig.Serialize(), nil -} - // PubKey performs the point-scalar multiplication from the privKey on the // generator point to get the pubkey. func (privKey PrivKeySecp256k1) PubKey() crypto.PubKey { @@ -137,20 +129,6 @@ func (pubKey PubKeySecp256k1) Bytes() []byte { return bz } -func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool { - pub, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) - if err != nil { - return false - } - parsedSig, err := secp256k1.ParseSignature(sig[:], secp256k1.S256()) - if err != nil { - return false - } - // Underlying library ensures that this signature is in canonical form, to - // prevent Secp256k1 malleability from altering the sign of the s term. - return parsedSig.Verify(crypto.Sha256(msg), pub) -} - func (pubKey PubKeySecp256k1) String() string { return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:]) } diff --git a/crypto/secp256k1/secp256k1_cgo.go b/crypto/secp256k1/secp256k1_cgo.go new file mode 100644 index 000000000..30414d2b7 --- /dev/null +++ b/crypto/secp256k1/secp256k1_cgo.go @@ -0,0 +1,24 @@ +// +build cgo + +package secp256k1 + +import ( + "github.com/ethereum/go-ethereum/crypto/secp256k1" + + "github.com/tendermint/tendermint/crypto" +) + +// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. +func (privKey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) { + rsv, err := secp256k1.Sign(crypto.Sha256(msg), privKey[:]) + if err != nil { + return nil, err + } + // we do not need v in r||s||v: + rs := rsv[:len(rsv)-1] + return rs, nil +} + +func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool { + return secp256k1.VerifySignature(pubKey[:], crypto.Sha256(msg), sig) +} diff --git a/crypto/secp256k1/secp256k1_nocgo.go b/crypto/secp256k1/secp256k1_nocgo.go new file mode 100644 index 000000000..34b006faa --- /dev/null +++ b/crypto/secp256k1/secp256k1_nocgo.go @@ -0,0 +1,71 @@ +// +build !cgo + +package secp256k1 + +import ( + "math/big" + + secp256k1 "github.com/btcsuite/btcd/btcec" + + "github.com/tendermint/tendermint/crypto" +) + +// used to reject malleable signatures +// see: +// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 +// - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39 +var secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) +var secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) + +// Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. +// The returned signature will be of the form R || S (in lower-S form). +func (privKey PrivKeySecp256k1) Sign(msg []byte) ([]byte, error) { + priv, _ := secp256k1.PrivKeyFromBytes(secp256k1.S256(), privKey[:]) + sig, err := priv.Sign(crypto.Sha256(msg)) + if err != nil { + return nil, err + } + sigBytes := serializeSig(sig) + return sigBytes, nil +} + +// VerifyBytes verifies a signature of the form R || S. +// It rejects signatures which are not in lower-S form. +func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sigStr []byte) bool { + if len(sigStr) != 64 { + return false + } + pub, err := secp256k1.ParsePubKey(pubKey[:], secp256k1.S256()) + if err != nil { + return false + } + // parse the signature: + signature := signatureFromBytes(sigStr) + // Reject malleable signatures. libsecp256k1 does this check but btcec doesn't. + // see: https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 + if signature.S.Cmp(secp256k1halfN) > 0 { + return false + } + return signature.Verify(crypto.Sha256(msg), pub) +} + +// Read Signature struct from R || S. Caller needs to ensure +// that len(sigStr) == 64. +func signatureFromBytes(sigStr []byte) *secp256k1.Signature { + return &secp256k1.Signature{ + new(big.Int).SetBytes(sigStr[:32]), + new(big.Int).SetBytes(sigStr[32:64]), + } +} + +// Serialize signature to R || S. +// R, S are padded to 32 bytes respectively. +func serializeSig(sig *secp256k1.Signature) []byte { + rBytes := sig.R.Bytes() + sBytes := sig.S.Bytes() + sigBytes := make([]byte, 64) + // 0 pad the byte arrays from the left if they aren't big enough. + copy(sigBytes[32-len(rBytes):32], rBytes) + copy(sigBytes[64-len(sBytes):64], sBytes) + return sigBytes +} diff --git a/crypto/secp256k1/secp256k1_nocgo_test.go b/crypto/secp256k1/secp256k1_nocgo_test.go new file mode 100644 index 000000000..95966478b --- /dev/null +++ b/crypto/secp256k1/secp256k1_nocgo_test.go @@ -0,0 +1,39 @@ +// +build !cgo + +package secp256k1 + +import ( + "testing" + + secp256k1 "github.com/btcsuite/btcd/btcec" + + "github.com/stretchr/testify/require" +) + +// Ensure that signature verification works, and that +// non-canonical signatures fail. +// Note: run with CGO_ENABLED=0 or go test -tags !cgo. +func TestSignatureVerificationAndRejectUpperS(t *testing.T) { + msg := []byte("We have lingered long enough on the shores of the cosmic ocean.") + for i := 0; i < 500; i++ { + priv := GenPrivKey() + sigStr, err := priv.Sign(msg) + require.NoError(t, err) + sig := signatureFromBytes(sigStr) + require.False(t, sig.S.Cmp(secp256k1halfN) > 0) + + pub := priv.PubKey() + require.True(t, pub.VerifyBytes(msg, sigStr)) + + // malleate: + sig.S.Sub(secp256k1.S256().CurveParams.N, sig.S) + require.True(t, sig.S.Cmp(secp256k1halfN) > 0) + malSigStr := serializeSig(sig) + + require.False(t, pub.VerifyBytes(msg, malSigStr), + "VerifyBytes incorrect with malleated & invalid S. sig=%v, key=%v", + sig, + priv, + ) + } +} diff --git a/crypto/secp256k1/secpk256k1_test.go b/crypto/secp256k1/secpk256k1_test.go index 2fa483014..0f0b5adce 100644 --- a/crypto/secp256k1/secpk256k1_test.go +++ b/crypto/secp256k1/secpk256k1_test.go @@ -11,7 +11,7 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/secp256k1" - underlyingSecp256k1 "github.com/tendermint/btcd/btcec" + underlyingSecp256k1 "github.com/btcsuite/btcd/btcec" ) type keyData struct { From eb4e23b91eca39e3568ad1dc3a1f793773810374 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 4 Feb 2019 07:30:24 -0800 Subject: [PATCH 019/259] fix FlushStop (#3247) * p2p/pex: failing test * p2p/conn: add stopMtx for FlushStop and OnStop * changelog --- CHANGELOG_PENDING.md | 2 + p2p/conn/connection.go | 19 +++++++++- p2p/pex/pex_reactor_test.go | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index b66cd4e8e..434232e4f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -25,3 +25,5 @@ Special thanks to external contributors on this release: ### BUG FIXES: - [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution +- [p2p] \#3247 Fix panic in SeedMode when calling FlushStop and OnStop + concurrently diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index fb20c4775..920734914 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -8,6 +8,7 @@ import ( "math" "net" "reflect" + "sync" "sync/atomic" "time" @@ -89,6 +90,10 @@ type MConnection struct { quitSendRoutine chan struct{} doneSendRoutine chan struct{} + // used to ensure FlushStop and OnStop + // are safe to call concurrently. + stopMtx sync.Mutex + flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled. pingTimer *cmn.RepeatTimer // send pings periodically @@ -210,8 +215,17 @@ func (c *MConnection) OnStart() error { // It additionally ensures that all successful // .Send() calls will get flushed before closing // the connection. -// NOTE: it is not safe to call this method more than once. func (c *MConnection) FlushStop() { + c.stopMtx.Lock() + defer c.stopMtx.Unlock() + + select { + case <-c.quitSendRoutine: + // already quit via OnStop + return + default: + } + c.BaseService.OnStop() c.flushTimer.Stop() c.pingTimer.Stop() @@ -247,6 +261,9 @@ func (c *MConnection) FlushStop() { // OnStop implements BaseService func (c *MConnection) OnStop() { + c.stopMtx.Lock() + defer c.stopMtx.Unlock() + select { case <-c.quitSendRoutine: // already quit via FlushStop diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index f5125c603..4f4ccb039 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -316,6 +316,81 @@ func TestPEXReactorCrawlStatus(t *testing.T) { // TODO: test } +// connect a peer to a seed, wait a bit, then stop it. +// this should give it time to request addrs and for the seed +// to call FlushStop, and allows us to test calling Stop concurrently +// with FlushStop. Before a fix, this non-deterministically reproduced +// https://github.com/tendermint/tendermint/issues/3231. +func TestPEXReactorSeedModeFlushStop(t *testing.T) { + N := 2 + 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 + + books := make([]*addrBook, N) + logger := log.TestingLogger() + + // create switches + for i := 0; i < N; i++ { + switches[i] = p2p.MakeSwitch(cfg, 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)) + + config := &PEXReactorConfig{} + if i == 0 { + // first one is a seed node + config = &PEXReactorConfig{SeedMode: true} + } + r := NewPEXReactor(books[i], config) + r.SetLogger(logger.With("pex", i)) + r.SetEnsurePeersPeriod(250 * time.Millisecond) + sw.AddReactor("pex", r) + + return sw + }) + } + + for _, sw := range switches { + err := sw.Start() // start switch and reactors + require.Nil(t, err) + } + + reactor := switches[0].Reactors()["pex"].(*PEXReactor) + peerID := switches[1].NodeInfo().ID() + + err = switches[1].DialPeerWithAddress(switches[0].NodeInfo().NetAddress(), false) + assert.NoError(t, err) + + // sleep up to a second while waiting for the peer to send us a message. + // this isn't perfect since it's possible the peer sends us a msg and we FlushStop + // before this loop catches it. but non-deterministically it works pretty well. + for i := 0; i < 1000; i++ { + v := reactor.lastReceivedRequests.Get(string(peerID)) + if v != nil { + break + } + time.Sleep(time.Millisecond) + } + + // by now the FlushStop should have happened. Try stopping the peer. + // it should be safe to do this. + peers := switches[0].Peers().List() + for _, peer := range peers { + peer.Stop() + } + + // stop the switches + for _, s := range switches { + s.Stop() + } +} + func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) { peer := p2p.CreateRandomPeer(false) From 39eba4e1543960e7783581ec75d2e531e8d44afa Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 4 Feb 2019 13:00:06 -0500 Subject: [PATCH 020/259] WAL: better errors and new fail point (#3246) * privval: more info in errors * wal: change Debug logs to Info * wal: log and return error on corrupted wal instead of panicing * fail: Exit right away instead of sending interupt * consensus: FAIL before handling our own vote allows to replicate #3089: - run using `FAIL_TEST_INDEX=0` - delete some bytes from the end of the WAL - start normally Results in logs like: ``` I[2019-02-03|18:12:58.225] Searching for height module=consensus wal=/Users/ethanbuchman/.tendermint/data/cs.wal/wal height=1 min=0 max=0 E[2019-02-03|18:12:58.225] Error on catchup replay. Proceeding to start ConsensusState anyway module=consensus err="failed to read data: EOF" I[2019-02-03|18:12:58.225] Started node module=main nodeInfo="{ProtocolVersion:{P2P:6 Block:9 App:1} ID_:35e87e93f2e31f305b65a5517fd2102331b56002 ListenAddr:tcp://0.0.0.0:26656 Network:test-chain-J8JvJH Version:0.29.1 Channels:4020212223303800 Moniker:Ethans-MacBook-Pro.local Other:{TxIndex:on RPCAddress:tcp://0.0.0.0:26657}}" E[2019-02-03|18:12:58.226] Couldn't connect to any seeds module=p2p I[2019-02-03|18:12:59.229] Timed out module=consensus dur=998.568ms height=1 round=0 step=RoundStepNewHeight I[2019-02-03|18:12:59.230] enterNewRound(1/0). Current: 1/0/RoundStepNewHeight module=consensus height=1 round=0 I[2019-02-03|18:12:59.230] enterPropose(1/0). Current: 1/0/RoundStepNewRound module=consensus height=1 round=0 I[2019-02-03|18:12:59.230] enterPropose: Our turn to propose module=consensus height=1 round=0 proposer=AD278B7767B05D7FBEB76207024C650988FA77D5 privValidator="PrivValidator{AD278B7767B05D7FBEB76207024C650988FA77D5 LH:1, LR:0, LS:2}" E[2019-02-03|18:12:59.230] enterPropose: Error signing proposal module=consensus height=1 round=0 err="Error signing proposal: Step regression at height 1 round 0. Got 1, last step 2" I[2019-02-03|18:13:02.233] Timed out module=consensus dur=3s height=1 round=0 step=RoundStepPropose I[2019-02-03|18:13:02.233] enterPrevote(1/0). Current: 1/0/RoundStepPropose module=consensus I[2019-02-03|18:13:02.233] enterPrevote: ProposalBlock is nil module=consensus height=1 round=0 E[2019-02-03|18:13:02.234] Error signing vote module=consensus height=1 round=0 vote="Vote{0:AD278B7767B0 1/00/1(Prevote) 000000000000 000000000000 @ 2019-02-04T02:13:02.233897Z}" err="Error signing vote: Conflicting data" ``` Notice the EOF, the step regression, and the conflicting data. * wal: change errors to be DataCorruptionError * exit on corrupt WAL * fix log * fix new line --- consensus/replay.go | 4 ++-- consensus/state.go | 26 ++++++++++++++++++++++++++ consensus/wal.go | 14 +++++++------- libs/fail/fail.go | 5 +++-- privval/file.go | 6 +++--- 5 files changed, 41 insertions(+), 14 deletions(-) diff --git a/consensus/replay.go b/consensus/replay.go index 3ac636577..21fef6b29 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -144,8 +144,8 @@ func (cs *ConsensusState) catchupReplay(csHeight int64) error { if err == io.EOF { break } else if IsDataCorruptionError(err) { - cs.Logger.Debug("data has been corrupted in last height of consensus WAL", "err", err, "height", csHeight) - panic(fmt.Sprintf("data has been corrupted (%v) in last height %d of consensus WAL", err, csHeight)) + cs.Logger.Error("data has been corrupted in last height of consensus WAL", "err", err, "height", csHeight) + return err } else if err != nil { return err } diff --git a/consensus/state.go b/consensus/state.go index 158e1605e..cec7e5f5e 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -307,6 +307,23 @@ func (cs *ConsensusState) OnStart() error { // reload from consensus log to catchup if cs.doWALCatchup { if err := cs.catchupReplay(cs.Height); err != nil { + // don't try to recover from data corruption error + if IsDataCorruptionError(err) { + cs.Logger.Error("Encountered corrupt WAL file", "err", err.Error()) + cs.Logger.Error("Please repair the WAL file before restarting") + fmt.Println(`You can attempt to repair the WAL as follows: + +---- +WALFILE=~/.tendermint/data/cs.wal/wal +cp $WALFILE ${WALFILE}.bak # backup the file +go run scripts/wal2json/main.go $WALFILE > wal.json # this will panic, but can be ignored +rm $WALFILE # remove the corrupt file +go run scripts/json2wal/main.go wal.json $WALFILE # rebuild the file without corruption +----`) + + return err + } + cs.Logger.Error("Error on catchup replay. Proceeding to start ConsensusState anyway", "err", err.Error()) // NOTE: if we ever do return an error here, // make sure to stop the timeoutTicker @@ -624,6 +641,15 @@ func (cs *ConsensusState) receiveRoutine(maxSteps int) { cs.handleMsg(mi) case mi = <-cs.internalMsgQueue: cs.wal.WriteSync(mi) // NOTE: fsync + + if _, ok := mi.Msg.(*VoteMessage); ok { + // we actually want to simulate failing during + // the previous WriteSync, but this isn't easy to do. + // Equivalent would be to fail here and manually remove + // some bytes from the end of the wal. + fail.Fail() // XXX + } + // handles proposals, block parts, votes cs.handleMsg(mi) case ti := <-cs.timeoutTicker.Chan(): // tockChan: diff --git a/consensus/wal.go b/consensus/wal.go index bbc9908fb..ba89cd1aa 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -163,7 +163,7 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions) // NOTE: starting from the last file in the group because we're usually // searching for the last height. See replay.go min, max := wal.group.MinIndex(), wal.group.MaxIndex() - wal.Logger.Debug("Searching for height", "height", height, "min", min, "max", max) + wal.Logger.Info("Searching for height", "height", height, "min", min, "max", max) for index := max; index >= min; index-- { gr, err = wal.group.NewReader(index) if err != nil { @@ -183,7 +183,7 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions) break } if options.IgnoreDataCorruptionErrors && IsDataCorruptionError(err) { - wal.Logger.Debug("Corrupted entry. Skipping...", "err", err) + wal.Logger.Error("Corrupted entry. Skipping...", "err", err) // do nothing continue } else if err != nil { @@ -194,7 +194,7 @@ func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions) if m, ok := msg.Msg.(EndHeightMessage); ok { lastHeightFound = m.Height if m.Height == height { // found - wal.Logger.Debug("Found", "height", height, "index", index) + wal.Logger.Info("Found", "height", height, "index", index) return gr, true, nil } } @@ -281,25 +281,25 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) { return nil, err } if err != nil { - return nil, fmt.Errorf("failed to read checksum: %v", err) + return nil, DataCorruptionError{fmt.Errorf("failed to read checksum: %v", err)} } crc := binary.BigEndian.Uint32(b) b = make([]byte, 4) _, err = dec.rd.Read(b) if err != nil { - return nil, fmt.Errorf("failed to read length: %v", err) + return nil, DataCorruptionError{fmt.Errorf("failed to read length: %v", err)} } length := binary.BigEndian.Uint32(b) if length > maxMsgSizeBytes { - return nil, fmt.Errorf("length %d exceeded maximum possible value of %d bytes", length, maxMsgSizeBytes) + return nil, DataCorruptionError{fmt.Errorf("length %d exceeded maximum possible value of %d bytes", length, maxMsgSizeBytes)} } data := make([]byte, length) _, err = dec.rd.Read(data) if err != nil { - return nil, fmt.Errorf("failed to read data: %v", err) + return nil, DataCorruptionError{fmt.Errorf("failed to read data: %v", err)} } // check checksum before decoding data diff --git a/libs/fail/fail.go b/libs/fail/fail.go index edfca13e3..d7912af5c 100644 --- a/libs/fail/fail.go +++ b/libs/fail/fail.go @@ -72,7 +72,8 @@ func FailRand(n int) { func Exit() { fmt.Printf("*** fail-test %d ***\n", callIndex) - proc, _ := os.FindProcess(os.Getpid()) - proc.Signal(os.Interrupt) + os.Exit(1) + // proc, _ := os.FindProcess(os.Getpid()) + // proc.Signal(os.Interrupt) // panic(fmt.Sprintf("*** fail-test %d ***", callIndex)) } diff --git a/privval/file.go b/privval/file.go index 8072cfa4a..d27d7a788 100644 --- a/privval/file.go +++ b/privval/file.go @@ -87,17 +87,17 @@ type FilePVLastSignState struct { func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bool, error) { if lss.Height > height { - return false, errors.New("Height regression") + return false, fmt.Errorf("Height regression. Got %v, last height %v", height, lss.Height) } if lss.Height == height { if lss.Round > round { - return false, errors.New("Round regression") + return false, fmt.Errorf("Round regression at height %v. Got %v, last round %v", height, round, lss.Round) } if lss.Round == round { if lss.Step > step { - return false, errors.New("Step regression") + return false, fmt.Errorf("Step regression at height %v round %v. Got %v, last step %v", height, round, step, lss.Step) } else if lss.Step == step { if lss.SignBytes != nil { if lss.Signature == nil { From 1809efa3500e215e531dfd78dd6fe180ef3ef4b1 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 4 Feb 2019 13:01:59 -0500 Subject: [PATCH 021/259] Introduce CommitSig alias for Vote in Commit (#3245) * types: memoize height/round in commit instead of first vote * types: commit.ValidateBasic in VerifyCommit * types: new CommitSig alias for Vote In preparation for reducing the redundancy in Commits, we introduce the CommitSig as an alias for Vote. This is non-breaking on the protocol, and minor breaking on the Go API, as Commit now contains a list of CommitSig instead of Vote. * remove dependence on ToVote * update some comments * fix tests * fix tests * fixes from review --- CHANGELOG_PENDING.md | 1 + blockchain/reactor_test.go | 4 +- blockchain/store_test.go | 23 ++++---- consensus/replay_test.go | 2 +- consensus/state.go | 4 +- consensus/types/round_state_test.go | 6 +-- lite/helpers.go | 6 +-- state/execution.go | 2 +- state/execution_test.go | 20 +++---- types/block.go | 83 +++++++++++++++++++++-------- types/block_test.go | 1 - types/validator_set.go | 18 +++---- types/validator_set_test.go | 4 +- types/vote.go | 10 ++++ types/vote_set.go | 8 +-- types/vote_test.go | 14 +++++ 16 files changed, 136 insertions(+), 70 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 434232e4f..daa42654c 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -11,6 +11,7 @@ Special thanks to external contributors on this release: * Apps * Go API + - [types] \#3245 Commit uses `type CommitSig Vote` instead of `Vote` directly. * Blockchain Protocol diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index f6c29d65d..138e16222 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -100,8 +100,8 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) lastBlock := blockStore.LoadBlock(blockHeight - 1) - vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]) - lastCommit = &types.Commit{Precommits: []*types.Vote{vote}, BlockID: lastBlockMeta.BlockID} + vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]).CommitSig() + lastCommit = &types.Commit{Precommits: []*types.CommitSig{vote}, BlockID: lastBlockMeta.BlockID} } thisBlock := makeBlock(blockHeight, state, lastCommit) diff --git a/blockchain/store_test.go b/blockchain/store_test.go index 8059072e1..9abc210b1 100644 --- a/blockchain/store_test.go +++ b/blockchain/store_test.go @@ -6,6 +6,7 @@ import ( "runtime/debug" "strings" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -20,6 +21,15 @@ import ( tmtime "github.com/tendermint/tendermint/types/time" ) +// make a Commit with a single vote containing just the height and a timestamp +func makeTestCommit(height int64, timestamp time.Time) *types.Commit { + return &types.Commit{ + Precommits: []*types.CommitSig{ + {Height: height, Timestamp: timestamp}, + }, + } +} + func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { config := cfg.ResetTestRoot("blockchain_reactor_test") // blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB()) @@ -86,8 +96,7 @@ var ( partSet = block.MakePartSet(2) part1 = partSet.GetPart(0) part2 = partSet.GetPart(1) - seenCommit1 = &types.Commit{Precommits: []*types.Vote{{Height: 10, - Timestamp: tmtime.Now()}}} + seenCommit1 = makeTestCommit(10, tmtime.Now()) ) // TODO: This test should be simplified ... @@ -107,8 +116,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { // save a block block := makeBlock(bs.Height()+1, state, new(types.Commit)) validPartSet := block.MakePartSet(2) - seenCommit := &types.Commit{Precommits: []*types.Vote{{Height: 10, - Timestamp: tmtime.Now()}}} + seenCommit := makeTestCommit(10, tmtime.Now()) bs.SaveBlock(block, partSet, seenCommit) require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") @@ -127,8 +135,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) { // End of setup, test data - commitAtH10 := &types.Commit{Precommits: []*types.Vote{{Height: 10, - Timestamp: tmtime.Now()}}} + commitAtH10 := makeTestCommit(10, tmtime.Now()) tuples := []struct { block *types.Block parts *types.PartSet @@ -351,9 +358,7 @@ func TestBlockFetchAtHeight(t *testing.T) { block := makeBlock(bs.Height()+1, state, new(types.Commit)) partSet := block.MakePartSet(2) - seenCommit := &types.Commit{Precommits: []*types.Vote{{Height: 10, - Timestamp: tmtime.Now()}}} - + seenCommit := makeTestCommit(10, tmtime.Now()) bs.SaveBlock(block, partSet, seenCommit) require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed") diff --git a/consensus/replay_test.go b/consensus/replay_test.go index d3aaebf12..e7269254c 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -539,7 +539,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { if p.Type == types.PrecommitType { thisBlockCommit = &types.Commit{ BlockID: p.BlockID, - Precommits: []*types.Vote{p}, + Precommits: []*types.CommitSig{p.CommitSig()}, } } } diff --git a/consensus/state.go b/consensus/state.go index cec7e5f5e..c8185d46e 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -489,7 +489,7 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) { if precommit == nil { continue } - added, err := lastPrecommits.AddVote(precommit) + added, err := lastPrecommits.AddVote(seenCommit.ToVote(precommit)) if !added || err != nil { cmn.PanicCrisis(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) } @@ -1356,7 +1356,7 @@ func (cs *ConsensusState) recordMetrics(height int64, block *types.Block) { missingValidators := 0 missingValidatorsPower := int64(0) for i, val := range cs.Validators.Validators { - var vote *types.Vote + var vote *types.CommitSig if i < len(block.LastCommit.Precommits) { vote = block.LastCommit.Precommits[i] } diff --git a/consensus/types/round_state_test.go b/consensus/types/round_state_test.go index c2bc9f7c2..cb16f939a 100644 --- a/consensus/types/round_state_test.go +++ b/consensus/types/round_state_test.go @@ -16,7 +16,7 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) { // Random validators nval, ntxs := 100, 100 vset, _ := types.RandValidatorSet(nval, 1) - precommits := make([]*types.Vote, nval) + precommits := make([]*types.CommitSig, nval) blockID := types.BlockID{ Hash: cmn.RandBytes(20), PartsHeader: types.PartSetHeader{ @@ -25,12 +25,12 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) { } sig := make([]byte, ed25519.SignatureSize) for i := 0; i < nval; i++ { - precommits[i] = &types.Vote{ + precommits[i] = (&types.Vote{ ValidatorAddress: types.Address(cmn.RandBytes(20)), Timestamp: tmtime.Now(), BlockID: blockID, Signature: sig, - } + }).CommitSig() } txs := make([]types.Tx, ntxs) for i := 0; i < ntxs; i++ { diff --git a/lite/helpers.go b/lite/helpers.go index 5177ee50b..6b18b3514 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -70,7 +70,7 @@ func (pkz privKeys) ToValidators(init, inc int64) *types.ValidatorSet { // signHeader properly signs the header with all keys from first to last exclusive. func (pkz privKeys) signHeader(header *types.Header, first, last int) *types.Commit { - votes := make([]*types.Vote, len(pkz)) + commitSigs := make([]*types.CommitSig, len(pkz)) // We need this list to keep the ordering. vset := pkz.ToValidators(1, 0) @@ -78,12 +78,12 @@ func (pkz privKeys) signHeader(header *types.Header, first, last int) *types.Com // Fill in the votes we want. for i := first; i < last && i < len(pkz); i++ { vote := makeVote(header, vset, pkz[i]) - votes[vote.ValidatorIndex] = vote + commitSigs[vote.ValidatorIndex] = vote.CommitSig() } res := &types.Commit{ BlockID: types.BlockID{Hash: header.Hash()}, - Precommits: votes, + Precommits: commitSigs, } return res } diff --git a/state/execution.go b/state/execution.go index d59c8af03..85477eebf 100644 --- a/state/execution.go +++ b/state/execution.go @@ -315,7 +315,7 @@ func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorS // Collect the vote info (list of validators and whether or not they signed). voteInfos := make([]abci.VoteInfo, len(lastValSet.Validators)) for i, val := range lastValSet.Validators { - var vote *types.Vote + var vote *types.CommitSig if i < len(block.LastCommit.Precommits) { vote = block.LastCommit.Precommits[i] } diff --git a/state/execution_test.go b/state/execution_test.go index b14ee6492..041fb558e 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -65,17 +65,17 @@ func TestBeginBlockValidators(t *testing.T) { prevBlockID := types.BlockID{prevHash, prevParts} now := tmtime.Now() - vote0 := &types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType} - vote1 := &types.Vote{ValidatorIndex: 1, Timestamp: now} + commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig() + commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig() testCases := []struct { desc string - lastCommitPrecommits []*types.Vote + lastCommitPrecommits []*types.CommitSig expectedAbsentValidators []int }{ - {"none absent", []*types.Vote{vote0, vote1}, []int{}}, - {"one absent", []*types.Vote{vote0, nil}, []int{1}}, - {"multiple absent", []*types.Vote{nil, nil}, []int{0, 1}}, + {"none absent", []*types.CommitSig{commitSig0, commitSig1}, []int{}}, + {"one absent", []*types.CommitSig{commitSig0, nil}, []int{1}}, + {"multiple absent", []*types.CommitSig{nil, nil}, []int{0, 1}}, } for _, tc := range testCases { @@ -136,10 +136,10 @@ func TestBeginBlockByzantineValidators(t *testing.T) { types.TM2PB.Evidence(ev2, valSet, now)}}, } - vote0 := &types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType} - vote1 := &types.Vote{ValidatorIndex: 1, Timestamp: now} - votes := []*types.Vote{vote0, vote1} - lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: votes} + commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig() + commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig() + commitSigs := []*types.CommitSig{commitSig0, commitSig1} + lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: commitSigs} for _, tc := range testCases { block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) diff --git a/types/block.go b/types/block.go index 99ee3f8e1..ec09fd449 100644 --- a/types/block.go +++ b/types/block.go @@ -477,39 +477,77 @@ func (h *Header) StringIndented(indent string) string { //------------------------------------- +// CommitSig is a vote included in a Commit. +// For now, it is identical to a vote, +// but in the future it will contain fewer fields +// to eliminate the redundancy in commits. +// See https://github.com/tendermint/tendermint/issues/1648. +type CommitSig Vote + +// String returns the underlying Vote.String() +func (cs *CommitSig) String() string { + return cs.toVote().String() +} + +// toVote converts the CommitSig to a vote. +// Once CommitSig has fewer fields than vote, +// converting to a Vote will require more information. +func (cs *CommitSig) toVote() *Vote { + if cs == nil { + return nil + } + v := Vote(*cs) + return &v +} + // Commit contains the evidence that a block was committed by a set of validators. // NOTE: Commit is empty for height 1, but never nil. type Commit struct { // NOTE: The Precommits are in order of address to preserve the bonded ValidatorSet order. // Any peer with a block can gossip precommits by index with a peer without recalculating the // active ValidatorSet. - BlockID BlockID `json:"block_id"` - Precommits []*Vote `json:"precommits"` + BlockID BlockID `json:"block_id"` + Precommits []*CommitSig `json:"precommits"` // Volatile - firstPrecommit *Vote - hash cmn.HexBytes - bitArray *cmn.BitArray + height int64 + round int + hash cmn.HexBytes + bitArray *cmn.BitArray } -// FirstPrecommit returns the first non-nil precommit in the commit. -// If all precommits are nil, it returns an empty precommit with height 0. -func (commit *Commit) FirstPrecommit() *Vote { +// VoteSignBytes constructs the SignBytes for the given CommitSig. +// The only unique part of the SignBytes is the Timestamp - all other fields +// signed over are otherwise the same for all validators. +func (commit *Commit) VoteSignBytes(chainID string, cs *CommitSig) []byte { + return cs.toVote().SignBytes(chainID) +} + +// memoizeHeightRound memoizes the height and round of the commit using +// the first non-nil vote. +func (commit *Commit) memoizeHeightRound() { if len(commit.Precommits) == 0 { - return nil + return } - if commit.firstPrecommit != nil { - return commit.firstPrecommit + if commit.height > 0 { + return } for _, precommit := range commit.Precommits { if precommit != nil { - commit.firstPrecommit = precommit - return precommit + commit.height = precommit.Height + commit.round = precommit.Round + return } } - return &Vote{ - Type: PrecommitType, - } +} + +// ToVote converts a CommitSig to a Vote. +// If the CommitSig is nil, the Vote will be nil. +// When CommitSig is reduced to contain fewer fields, +// this will need access to the ValidatorSet to properly +// reconstruct the vote. +func (commit *Commit) ToVote(cs *CommitSig) *Vote { + return cs.toVote() } // Height returns the height of the commit @@ -517,7 +555,8 @@ func (commit *Commit) Height() int64 { if len(commit.Precommits) == 0 { return 0 } - return commit.FirstPrecommit().Height + commit.memoizeHeightRound() + return commit.height } // Round returns the round of the commit @@ -525,7 +564,8 @@ func (commit *Commit) Round() int { if len(commit.Precommits) == 0 { return 0 } - return commit.FirstPrecommit().Round + commit.memoizeHeightRound() + return commit.round } // Type returns the vote type of the commit, which is always VoteTypePrecommit @@ -554,12 +594,13 @@ func (commit *Commit) BitArray() *cmn.BitArray { return commit.bitArray } -// GetByIndex returns the vote corresponding to a given validator index +// GetByIndex returns the vote corresponding to a given validator index. +// Implements VoteSetReader. func (commit *Commit) GetByIndex(index int) *Vote { - return commit.Precommits[index] + return commit.Precommits[index].toVote() } -// IsCommit returns true if there is at least one vote +// IsCommit returns true if there is at least one vote. func (commit *Commit) IsCommit() bool { return len(commit.Precommits) != 0 } diff --git a/types/block_test.go b/types/block_test.go index bedd8c8da..31e7983f2 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -198,7 +198,6 @@ func TestCommit(t *testing.T) { commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) require.NoError(t, err) - assert.NotNil(t, commit.FirstPrecommit()) assert.Equal(t, h-1, commit.Height()) assert.Equal(t, 1, commit.Round()) assert.Equal(t, PrecommitType, SignedMsgType(commit.Type())) diff --git a/types/validator_set.go b/types/validator_set.go index a36e1920d..2edec595d 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -368,6 +368,10 @@ func (vals *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) { // Verify that +2/3 of the set had signed the given signBytes. func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height int64, commit *Commit) error { + + if err := commit.ValidateBasic(); err != nil { + return err + } if vals.Size() != len(commit.Precommits) { return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", vals.Size(), len(commit.Precommits)) } @@ -380,24 +384,14 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i } talliedVotingPower := int64(0) - round := commit.Round() for idx, precommit := range commit.Precommits { if precommit == nil { continue // OK, some precommits can be missing. } - if precommit.Height != height { - return fmt.Errorf("Invalid commit -- wrong height: want %v got %v", height, precommit.Height) - } - if precommit.Round != round { - return fmt.Errorf("Invalid commit -- wrong round: want %v got %v", round, precommit.Round) - } - if precommit.Type != PrecommitType { - return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx) - } _, val := vals.GetByIndex(idx) // Validate signature. - precommitSignBytes := precommit.SignBytes(chainID) + precommitSignBytes := commit.VoteSignBytes(chainID, precommit) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit) } @@ -481,7 +475,7 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin seen[idx] = true // Validate signature. - precommitSignBytes := precommit.SignBytes(chainID) + precommitSignBytes := commit.VoteSignBytes(chainID, precommit) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return cmn.NewError("Invalid commit -- invalid signature: %v", precommit) } diff --git a/types/validator_set_test.go b/types/validator_set_test.go index dd49ee16f..72b2f6613 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -565,7 +565,7 @@ func TestValidatorSetVerifyCommit(t *testing.T) { vote.Signature = sig commit := &Commit{ BlockID: blockID, - Precommits: []*Vote{vote}, + Precommits: []*CommitSig{vote.CommitSig()}, } badChainID := "notmychainID" @@ -573,7 +573,7 @@ func TestValidatorSetVerifyCommit(t *testing.T) { badHeight := height + 1 badCommit := &Commit{ BlockID: blockID, - Precommits: []*Vote{nil}, + Precommits: []*CommitSig{nil}, } // test some error cases diff --git a/types/vote.go b/types/vote.go index 8ff51d3c7..ad05d688d 100644 --- a/types/vote.go +++ b/types/vote.go @@ -59,6 +59,16 @@ type Vote struct { Signature []byte `json:"signature"` } +// CommitSig converts the Vote to a CommitSig. +// If the Vote is nil, the CommitSig will be nil. +func (vote *Vote) CommitSig() *CommitSig { + if vote == nil { + return nil + } + cs := CommitSig(*vote) + return &cs +} + func (vote *Vote) SignBytes(chainID string) []byte { bz, err := cdc.MarshalBinaryLengthPrefixed(CanonicalizeVote(chainID, vote)) if err != nil { diff --git a/types/vote_set.go b/types/vote_set.go index 0cf6cbb7f..14930da4a 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -541,11 +541,13 @@ func (voteSet *VoteSet) MakeCommit() *Commit { } // For every validator, get the precommit - votesCopy := make([]*Vote, len(voteSet.votes)) - copy(votesCopy, voteSet.votes) + commitSigs := make([]*CommitSig, len(voteSet.votes)) + for i, v := range voteSet.votes { + commitSigs[i] = v.CommitSig() + } return &Commit{ BlockID: *voteSet.maj23, - Precommits: votesCopy, + Precommits: commitSigs, } } diff --git a/types/vote_test.go b/types/vote_test.go index aefa4fcfa..e4bf658bc 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" @@ -43,6 +44,19 @@ func exampleVote(t byte) *Vote { } } +// Ensure that Vote and CommitSig have the same encoding. +// This ensures using CommitSig isn't a breaking change. +// This test will fail and can be removed once CommitSig contains only sigs and +// timestamps. +func TestVoteEncoding(t *testing.T) { + vote := examplePrecommit() + commitSig := vote.CommitSig() + cdc := amino.NewCodec() + bz1 := cdc.MustMarshalBinaryBare(vote) + bz2 := cdc.MustMarshalBinaryBare(commitSig) + assert.Equal(t, bz1, bz2) +} + func TestVoteSignable(t *testing.T) { vote := examplePrecommit() signBytes := vote.SignBytes("test_chain_id") From d8f0bc3e60eaddef11fe7e83328a22a149de5a01 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 14:11:35 +0400 Subject: [PATCH 022/259] Revert "quick fix for CircleCI (#2279)" This reverts commit 1cf6712a36e8ecc843a68aa373748e89e0afecba. --- .circleci/config.yml | 156 ++++++------------------------------------- 1 file changed, 22 insertions(+), 134 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ecc7c0ac7..826793370 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,10 +48,10 @@ jobs: key: v3-pkg-cache paths: - /go/pkg - # - save_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - # paths: - # - /go/src/github.com/tendermint/tendermint + - save_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} + paths: + - /go/src/github.com/tendermint/tendermint build_slate: <<: *defaults @@ -60,23 +60,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # https://discuss.circleci.com/t/saving-cache-stopped-working-warning-skipping-this-step-disabled-in-configuration/24423/2 - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: name: slate docs command: | @@ -91,23 +76,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - make get_dev_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: name: metalinter command: | @@ -128,22 +98,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: name: Run abci apps tests command: | @@ -159,22 +115,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: name: Run abci-cli tests command: | @@ -188,22 +130,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils - run: name: Run tests @@ -217,22 +145,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: mkdir -p /tmp/logs - run: name: Run tests @@ -256,22 +170,8 @@ jobs: at: /tmp/workspace - restore_cache: key: v3-pkg-cache - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: name: Run tests command: bash test/persist/test_failure_indices.sh @@ -317,22 +217,10 @@ jobs: steps: - attach_workspace: at: /tmp/workspace - # - restore_cache: - # key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - checkout - - run: - name: tools - command: | - export PATH="$GOBIN:$PATH" - make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - - run: mkdir -p $GOPATH/src/github.com/tendermint - - run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint - + - restore_cache: + key: v3-pkg-cache + - restore_cache: + key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: name: gather command: | From da33dd04cc90f60f339afa8182f59294e67d151b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 15:00:55 +0400 Subject: [PATCH 023/259] switch to golangci-lint from gometalinter :speedboat: --- .circleci/config.yml | 2 +- .golangci.yml | 65 ++++++++++++++++++++++++++++++++++++++++++++ Makefile | 43 +++-------------------------- Vagrantfile | 2 +- libs/test.sh | 2 +- scripts/get_tools.sh | 21 ++++++++++---- 6 files changed, 88 insertions(+), 47 deletions(-) create mode 100644 .golangci.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 826793370..d29680945 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -83,7 +83,7 @@ jobs: command: | set -ex export PATH="$GOBIN:$PATH" - make metalinter + make lint - run: name: check_dep command: | diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 000000000..ed2f6ab3e --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,65 @@ +run: + deadline: 1m + +linters: + enable-all: true + disable: + - gocyclo + - golint + - maligned + - errcheck + - staticcheck + - dupl + - ineffassign + - interfacer + - unconvert + - goconst + - unparam + - nakedret + - lll + - gochecknoglobals + - govet + - gocritic + - gosec + - gochecknoinits + - scopelint + - stylecheck + - deadcode + - prealloc + - unused + - gosimple + +# linters-settings: +# govet: +# check-shadowing: true +# golint: +# min-confidence: 0 +# gocyclo: +# min-complexity: 10 +# maligned: +# suggest-new: true +# dupl: +# threshold: 100 +# goconst: +# min-len: 2 +# min-occurrences: 2 +# depguard: +# list-type: blacklist +# packages: +# # logging is allowed only by logutils.Log, logrus +# # is allowed to use only in logutils package +# - github.com/sirupsen/logrus +# misspell: +# locale: US +# lll: +# line-length: 140 +# goimports: +# local-prefixes: github.com/golangci/golangci-lint +# gocritic: +# enabled-tags: +# - performance +# - style +# - experimental +# disabled-checks: +# - wrapperFunc +# - commentFormatting # https://github.com/go-critic/go-critic/issues/755 diff --git a/Makefile b/Makefile index 8c0928d04..ac9700d65 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ GOTOOLS = \ github.com/mitchellh/gox \ github.com/golang/dep/cmd/dep \ - github.com/alecthomas/gometalinter \ + github.com/golangci/golangci-lint/cmd/golangci-lint \ github.com/gogo/protobuf/protoc-gen-gogo \ github.com/square/certstrap GOBIN?=${GOPATH}/bin @@ -11,8 +11,6 @@ INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protob BUILD_TAGS?='tendermint' BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" -LINT_FLAGS = --exclude '.*\.pb\.go' --exclude 'vendor/*' --vendor --deadline=600s - all: check build test install check: check_tools get_vendor_deps @@ -82,10 +80,6 @@ get_tools: @echo "--> Installing tools" ./scripts/get_tools.sh -get_dev_tools: - @echo "--> Downloading linters (this may take awhile)" - $(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN) - update_tools: @echo "--> Updating tools" ./scripts/get_tools.sh @@ -246,38 +240,9 @@ cleanup_after_test_with_deadlock: fmt: @go fmt ./... -metalinter: +lint: @echo "--> Running linter" - @gometalinter $(LINT_FLAGS) --disable-all \ - --enable=vet \ - --enable=vetshadow \ - --enable=deadcode \ - --enable=varcheck \ - --enable=structcheck \ - --enable=misspell \ - --enable=safesql \ - --enable=gosec \ - --enable=goimports \ - --enable=gofmt \ - ./... - #--enable=gotype \ - #--enable=gotypex \ - #--enable=gocyclo \ - #--enable=golint \ - #--enable=maligned \ - #--enable=errcheck \ - #--enable=staticcheck \ - #--enable=dupl \ - #--enable=ineffassign \ - #--enable=interfacer \ - #--enable=unconvert \ - #--enable=goconst \ - #--enable=unparam \ - #--enable=nakedret \ - -metalinter_all: - @echo "--> Running linter (all)" - gometalinter $(LINT_FLAGS) --enable-all --disable=lll ./... + @golangci-lint run DESTINATION = ./index.html.md @@ -343,4 +308,4 @@ build-slate: # 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 build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock metalinter metalinter_all +.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint diff --git a/Vagrantfile b/Vagrantfile index f058d78e7..320f3b1c3 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -53,6 +53,6 @@ Vagrant.configure("2") do |config| # get all deps and tools, ready to install/test su - vagrant -c 'source /home/vagrant/.bash_profile' - su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_dev_tools && make get_vendor_deps' + su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_vendor_deps' SHELL end diff --git a/libs/test.sh b/libs/test.sh index ecf17fc45..64898b0d2 100755 --- a/libs/test.sh +++ b/libs/test.sh @@ -2,7 +2,7 @@ set -e # run the linter -# make metalinter_test +# make lint # setup certs make gen_certs diff --git a/scripts/get_tools.sh b/scripts/get_tools.sh index 87e30a3d9..dd9566917 100755 --- a/scripts/get_tools.sh +++ b/scripts/get_tools.sh @@ -5,11 +5,14 @@ set -e # specific git hash. # # repos it installs: -# github.com/mitchellh/gox # github.com/golang/dep/cmd/dep -# gopkg.in/alecthomas/gometalinter.v2 # github.com/gogo/protobuf/protoc-gen-gogo # github.com/square/certstrap +# github.com/mitchellh/gox +# github.com/golangci/golangci-lint +# github.com/petermattis/goid +# github.com/sasha-s/go-deadlock +# goimports ## check if GOPATH is set if [ -z ${GOPATH+x} ]; then @@ -45,14 +48,22 @@ installFromGithub() { echo "" } -installFromGithub mitchellh/gox 51ed453898ca5579fea9ad1f08dff6b121d9f2e8 +######################## COMMON TOOLS ######################################## installFromGithub golang/dep 22125cfaa6ddc71e145b1535d4b7ee9744fefff2 cmd/dep -## gometalinter v3.0.0 -installFromGithub alecthomas/gometalinter df395bfa67c5d0630d936c0044cf07ff05086655 + +######################## DEVELOPER TOOLS ##################################### installFromGithub gogo/protobuf 61dbc136cf5d2f08d68a011382652244990a53a9 protoc-gen-gogo + installFromGithub square/certstrap e27060a3643e814151e65b9807b6b06d169580a7 +# used to build tm-monitor & tm-bench binaries +installFromGithub mitchellh/gox 51ed453898ca5579fea9ad1f08dff6b121d9f2e8 + +## golangci-lint v1.13.2 +installFromGithub golangci/golangci-lint 7b2421d55194c9dc385eff7720a037aa9244ca3c cmd/golangci-lint + ## make test_with_deadlock +## XXX: https://github.com/tendermint/tendermint/issues/3242 installFromGithub petermattis/goid b0b1615b78e5ee59739545bb38426383b2cda4c9 installFromGithub sasha-s/go-deadlock d68e2bc52ae3291765881b9056f2c1527f245f1e go get golang.org/x/tools/cmd/goimports From ffd3bf8448d5ca5fd36e1056a97586d7bbbf8fa4 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 15:16:38 +0400 Subject: [PATCH 024/259] remove or comment out unused code --- .golangci.yml | 2 - blockchain/pool.go | 32 +++---- consensus/common_test.go | 56 ++++++------ consensus/state_test.go | 4 - crypto/merkle/proof_test.go | 22 ++--- lite/proxy/query_test.go | 139 ++++++++++++++--------------- p2p/conn/secret_connection_test.go | 9 -- p2p/switch.go | 8 +- p2p/trust/metric_test.go | 72 +++++++-------- state/state_test.go | 4 - 10 files changed, 163 insertions(+), 185 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ed2f6ab3e..6175fc90b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -26,8 +26,6 @@ linters: - stylecheck - deadcode - prealloc - - unused - - gosimple # linters-settings: # govet: diff --git a/blockchain/pool.go b/blockchain/pool.go index e6be36012..236bf7e09 100644 --- a/blockchain/pool.go +++ b/blockchain/pool.go @@ -363,23 +363,23 @@ func (pool *BlockPool) sendError(err error, peerID p2p.ID) { pool.errorsCh <- peerError{err, peerID} } -// unused by tendermint; left for debugging purposes -func (pool *BlockPool) debug() string { - pool.mtx.Lock() - defer pool.mtx.Unlock() +// for debugging purposes +// func (pool *BlockPool) debug() string { +// pool.mtx.Lock() +// defer pool.mtx.Unlock() - str := "" - nextHeight := pool.height + pool.requestersLen() - for h := pool.height; h < nextHeight; h++ { - if pool.requesters[h] == nil { - str += fmt.Sprintf("H(%v):X ", h) - } else { - str += fmt.Sprintf("H(%v):", h) - str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil) - } - } - return str -} +// str := "" +// nextHeight := pool.height + pool.requestersLen() +// for h := pool.height; h < nextHeight; h++ { +// if pool.requesters[h] == nil { +// str += fmt.Sprintf("H(%v):X ", h) +// } else { +// str += fmt.Sprintf("H(%v):", h) +// str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil) +// } +// } +// return str +// } //------------------------------------- diff --git a/consensus/common_test.go b/consensus/common_test.go index a975b2b60..4e3d60e1d 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -378,35 +378,35 @@ func ensureNewEvent( } } -func ensureNewRoundStep(stepCh <-chan interface{}, height int64, round int) { - ensureNewEvent( - stepCh, - height, - round, - ensureTimeout, - "Timeout expired while waiting for NewStep event") -} +// func ensureNewRoundStep(stepCh <-chan interface{}, height int64, round int) { +// ensureNewEvent( +// stepCh, +// height, +// round, +// ensureTimeout, +// "Timeout expired while waiting for NewStep event") +// } -func ensureNewVote(voteCh <-chan interface{}, height int64, round int) { - select { - case <-time.After(ensureTimeout): - break - case v := <-voteCh: - edv, ok := v.(types.EventDataVote) - if !ok { - panic(fmt.Sprintf("expected a *types.Vote, "+ - "got %v. wrong subscription channel?", - reflect.TypeOf(v))) - } - vote := edv.Vote - if vote.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height)) - } - if vote.Round != round { - panic(fmt.Sprintf("expected round %v, got %v", round, vote.Round)) - } - } -} +// func ensureNewVote(voteCh <-chan interface{}, height int64, round int) { +// select { +// case <-time.After(ensureTimeout): +// break +// case v := <-voteCh: +// edv, ok := v.(types.EventDataVote) +// if !ok { +// panic(fmt.Sprintf("expected a *types.Vote, "+ +// "got %v. wrong subscription channel?", +// reflect.TypeOf(v))) +// } +// vote := edv.Vote +// if vote.Height != height { +// panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height)) +// } +// if vote.Round != round { +// panic(fmt.Sprintf("expected round %v, got %v", round, vote.Round)) +// } +// } +// } func ensureNewRound(roundCh <-chan interface{}, height int64, round int) { select { diff --git a/consensus/state_test.go b/consensus/state_test.go index 10c04fbc5..153f51e16 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -22,10 +22,6 @@ func init() { config = ResetConfig("consensus_state_test") } -func ensureProposeTimeout(timeoutPropose time.Duration) time.Duration { - return time.Duration(timeoutPropose.Nanoseconds()*2) * time.Nanosecond -} - /* ProposeSuite diff --git a/crypto/merkle/proof_test.go b/crypto/merkle/proof_test.go index 2a0bdccfc..504156246 100644 --- a/crypto/merkle/proof_test.go +++ b/crypto/merkle/proof_test.go @@ -26,17 +26,17 @@ func NewDominoOp(key, input, output string) DominoOp { } } -func DominoOpDecoder(pop ProofOp) (ProofOperator, error) { - if pop.Type != ProofOpDomino { - panic("unexpected proof op type") - } - var op DominoOp // a bit strange as we'll discard this, but it works. - err := amino.UnmarshalBinaryLengthPrefixed(pop.Data, &op) - if err != nil { - return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp") - } - return NewDominoOp(string(pop.Key), op.Input, op.Output), nil -} +// func DominoOpDecoder(pop ProofOp) (ProofOperator, error) { +// if pop.Type != ProofOpDomino { +// panic("unexpected proof op type") +// } +// var op DominoOp // a bit strange as we'll discard this, but it works. +// err := amino.UnmarshalBinaryLengthPrefixed(pop.Data, &op) +// if err != nil { +// return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp") +// } +// return NewDominoOp(string(pop.Key), op.Input, op.Output), nil +// } func (dop DominoOp) ProofOp() ProofOp { bz := amino.MustMarshalBinaryLengthPrefixed(dop) diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index d8d45df3b..707430b61 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -4,7 +4,6 @@ import ( "fmt" "os" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -21,7 +20,7 @@ import ( var node *nm.Node var chainID = "tendermint_test" // TODO use from config. -var waitForEventTimeout = 5 * time.Second +// var waitForEventTimeout = 5 * time.Second // TODO fix tests!! @@ -42,83 +41,83 @@ func kvstoreTx(k, v []byte) []byte { // TODO: enable it after general proof format has been adapted // in abci/examples/kvstore.go -func _TestAppProofs(t *testing.T) { - assert, require := assert.New(t), require.New(t) +// func TestAppProofs(t *testing.T) { +// assert, require := assert.New(t), require.New(t) - prt := defaultProofRuntime() - cl := client.NewLocal(node) - client.WaitForHeight(cl, 1, nil) +// prt := defaultProofRuntime() +// cl := client.NewLocal(node) +// client.WaitForHeight(cl, 1, nil) - // This sets up our trust on the node based on some past point. - source := certclient.NewProvider(chainID, cl) - seed, err := source.LatestFullCommit(chainID, 1, 1) - require.NoError(err, "%#v", err) - cert := lite.NewBaseVerifier(chainID, seed.Height(), seed.Validators) +// // This sets up our trust on the node based on some past point. +// source := certclient.NewProvider(chainID, cl) +// seed, err := source.LatestFullCommit(chainID, 1, 1) +// require.NoError(err, "%#v", err) +// cert := lite.NewBaseVerifier(chainID, seed.Height(), seed.Validators) - // Wait for tx confirmation. - done := make(chan int64) - go func() { - evtTyp := types.EventTx - _, err = client.WaitForOneEvent(cl, evtTyp, waitForEventTimeout) - require.Nil(err, "%#v", err) - close(done) - }() +// // Wait for tx confirmation. +// done := make(chan int64) +// go func() { +// evtTyp := types.EventTx +// _, err = client.WaitForOneEvent(cl, evtTyp, waitForEventTimeout) +// require.Nil(err, "%#v", err) +// close(done) +// }() - // Submit a transaction. - k := []byte("my-key") - v := []byte("my-value") - tx := kvstoreTx(k, v) - br, err := cl.BroadcastTxCommit(tx) - require.NoError(err, "%#v", err) - require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx) - require.EqualValues(0, br.DeliverTx.Code) - brh := br.Height +// // Submit a transaction. +// k := []byte("my-key") +// v := []byte("my-value") +// tx := kvstoreTx(k, v) +// br, err := cl.BroadcastTxCommit(tx) +// require.NoError(err, "%#v", err) +// require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx) +// require.EqualValues(0, br.DeliverTx.Code) +// brh := br.Height - // Fetch latest after tx commit. - <-done - latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1) - require.NoError(err, "%#v", err) - rootHash := latest.SignedHeader.AppHash - if rootHash == nil { - // Fetch one block later, AppHash hasn't been committed yet. - // TODO find a way to avoid doing this. - client.WaitForHeight(cl, latest.SignedHeader.Height+1, nil) - latest, err = source.LatestFullCommit(chainID, latest.SignedHeader.Height+1, 1<<63-1) - require.NoError(err, "%#v", err) - rootHash = latest.SignedHeader.AppHash - } - require.NotNil(rootHash) +// // Fetch latest after tx commit. +// <-done +// latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1) +// require.NoError(err, "%#v", err) +// rootHash := latest.SignedHeader.AppHash +// if rootHash == nil { +// // Fetch one block later, AppHash hasn't been committed yet. +// // TODO find a way to avoid doing this. +// client.WaitForHeight(cl, latest.SignedHeader.Height+1, nil) +// latest, err = source.LatestFullCommit(chainID, latest.SignedHeader.Height+1, 1<<63-1) +// require.NoError(err, "%#v", err) +// rootHash = latest.SignedHeader.AppHash +// } +// require.NotNil(rootHash) - // verify a query before the tx block has no data (and valid non-exist proof) - bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert) - require.NoError(err, "%#v", err) - // require.NotNil(proof) - // TODO: Ensure that *some* keys will be there, ensuring that proof is nil, - // (currently there's a race condition) - // and ensure that proof proves absence of k. - require.Nil(bs) +// // verify a query before the tx block has no data (and valid non-exist proof) +// bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert) +// require.NoError(err, "%#v", err) +// // require.NotNil(proof) +// // TODO: Ensure that *some* keys will be there, ensuring that proof is nil, +// // (currently there's a race condition) +// // and ensure that proof proves absence of k. +// require.Nil(bs) - // but given that block it is good - bs, height, proof, err = GetWithProof(prt, k, brh, cl, cert) - require.NoError(err, "%#v", err) - require.NotNil(proof) - require.Equal(height, brh) +// // but given that block it is good +// bs, height, proof, err = GetWithProof(prt, k, brh, cl, cert) +// require.NoError(err, "%#v", err) +// require.NotNil(proof) +// require.Equal(height, brh) - assert.EqualValues(v, bs) - err = prt.VerifyValue(proof, rootHash, string(k), bs) // XXX key encoding - assert.NoError(err, "%#v", err) +// assert.EqualValues(v, bs) +// err = prt.VerifyValue(proof, rootHash, string(k), bs) // XXX key encoding +// assert.NoError(err, "%#v", err) - // Test non-existing key. - missing := []byte("my-missing-key") - bs, _, proof, err = GetWithProof(prt, missing, 0, cl, cert) - require.NoError(err) - require.Nil(bs) - require.NotNil(proof) - err = prt.VerifyAbsence(proof, rootHash, string(missing)) // XXX VerifyAbsence(), keyencoding - assert.NoError(err, "%#v", err) - err = prt.VerifyAbsence(proof, rootHash, string(k)) // XXX VerifyAbsence(), keyencoding - assert.Error(err, "%#v", err) -} +// // Test non-existing key. +// missing := []byte("my-missing-key") +// bs, _, proof, err = GetWithProof(prt, missing, 0, cl, cert) +// require.NoError(err) +// require.Nil(bs) +// require.NotNil(proof) +// err = prt.VerifyAbsence(proof, rootHash, string(missing)) // XXX VerifyAbsence(), keyencoding +// assert.NoError(err, "%#v", err) +// err = prt.VerifyAbsence(proof, rootHash, string(k)) // XXX VerifyAbsence(), keyencoding +// assert.Error(err, "%#v", err) +// } func TestTxProofs(t *testing.T) { assert, require := assert.New(t), require.New(t) diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 69d9c09fe..6b2854767 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -398,12 +398,3 @@ func BenchmarkSecretConnection(b *testing.B) { } //barSecConn.Close() race condition } - -func fingerprint(bz []byte) []byte { - const fbsize = 40 - if len(bz) < fbsize { - return bz - } else { - return bz[:fbsize] - } -} diff --git a/p2p/switch.go b/p2p/switch.go index dbd9c2a60..7d2e6c3f3 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -480,14 +480,12 @@ func (sw *Switch) acceptRoutine() { metrics: sw.metrics, }) if err != nil { - switch err.(type) { + switch err := err.(type) { case ErrRejected: - rErr := err.(ErrRejected) - - if rErr.IsSelf() { + if err.IsSelf() { // Remove the given address from the address book and add to our addresses // to avoid dialing in the future. - addr := rErr.Addr() + addr := err.Addr() sw.addrBook.RemoveAddress(&addr) sw.addrBook.AddOurAddress(&addr) } diff --git a/p2p/trust/metric_test.go b/p2p/trust/metric_test.go index f690ce557..89327c0e1 100644 --- a/p2p/trust/metric_test.go +++ b/p2p/trust/metric_test.go @@ -65,44 +65,44 @@ func TestTrustMetricCopyNilPointer(t *testing.T) { } // XXX: This test fails non-deterministically -func _TestTrustMetricStopPause(t *testing.T) { - // The TestTicker will provide manual control over - // the passing of time within the metric - tt := NewTestTicker() - tm := NewMetric() - tm.SetTicker(tt) - tm.Start() - // Allow some time intervals to pass and pause - tt.NextTick() - tt.NextTick() - tm.Pause() +// func _TestTrustMetricStopPause(t *testing.T) { +// // The TestTicker will provide manual control over +// // the passing of time within the metric +// tt := NewTestTicker() +// tm := NewMetric() +// tm.SetTicker(tt) +// tm.Start() +// // Allow some time intervals to pass and pause +// tt.NextTick() +// tt.NextTick() +// tm.Pause() - // could be 1 or 2 because Pause and NextTick race - first := tm.Copy().numIntervals +// // could be 1 or 2 because Pause and NextTick race +// first := tm.Copy().numIntervals - // Allow more time to pass and check the intervals are unchanged - tt.NextTick() - tt.NextTick() - assert.Equal(t, first, tm.Copy().numIntervals) +// // Allow more time to pass and check the intervals are unchanged +// tt.NextTick() +// tt.NextTick() +// assert.Equal(t, first, tm.Copy().numIntervals) - // Get the trust metric activated again - tm.GoodEvents(5) - // Allow some time intervals to pass and stop - tt.NextTick() - tt.NextTick() - tm.Stop() - tm.Wait() +// // Get the trust metric activated again +// tm.GoodEvents(5) +// // Allow some time intervals to pass and stop +// tt.NextTick() +// tt.NextTick() +// tm.Stop() +// tm.Wait() - second := tm.Copy().numIntervals - // Allow more intervals to pass while the metric is stopped - // and check that the number of intervals match - tm.NextTimeInterval() - tm.NextTimeInterval() - // XXX: fails non-deterministically: - // expected 5, got 6 - assert.Equal(t, second+2, tm.Copy().numIntervals) +// second := tm.Copy().numIntervals +// // Allow more intervals to pass while the metric is stopped +// // and check that the number of intervals match +// tm.NextTimeInterval() +// tm.NextTimeInterval() +// // XXX: fails non-deterministically: +// // expected 5, got 6 +// assert.Equal(t, second+2, tm.Copy().numIntervals) - if first > second { - t.Fatalf("numIntervals should always increase or stay the same over time") - } -} +// if first > second { +// t.Fatalf("numIntervals should always increase or stay the same over time") +// } +// } diff --git a/state/state_test.go b/state/state_test.go index 9ab0de135..904d7a10b 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -938,10 +938,6 @@ func makeParams(blockBytes, blockGas, evidenceAge int64) types.ConsensusParams { } } -func pk() []byte { - return ed25519.GenPrivKey().PubKey().Bytes() -} - func TestApplyUpdates(t *testing.T) { initParams := makeParams(1, 2, 3) From 3c8156a55a7a13188c7c8fd6e54433b465212920 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 15:24:54 +0400 Subject: [PATCH 025/259] preallocating memory when we can --- .golangci.yml | 1 - crypto/merkle/proof.go | 2 +- libs/common/colors.go | 2 +- libs/events/events.go | 2 +- p2p/pex/pex_reactor.go | 3 +-- 5 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 6175fc90b..ca86e6d5b 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -25,7 +25,6 @@ linters: - scopelint - stylecheck - deadcode - - prealloc # linters-settings: # govet: diff --git a/crypto/merkle/proof.go b/crypto/merkle/proof.go index 8f8b460c9..5e2a3ab12 100644 --- a/crypto/merkle/proof.go +++ b/crypto/merkle/proof.go @@ -98,7 +98,7 @@ func (prt *ProofRuntime) Decode(pop ProofOp) (ProofOperator, error) { } func (prt *ProofRuntime) DecodeProof(proof *Proof) (ProofOperators, error) { - var poz ProofOperators + poz := make(ProofOperators, 0, len(proof.Ops)) for _, pop := range proof.Ops { operator, err := prt.Decode(pop) if err != nil { diff --git a/libs/common/colors.go b/libs/common/colors.go index 4837f97b4..89dda2c97 100644 --- a/libs/common/colors.go +++ b/libs/common/colors.go @@ -43,7 +43,7 @@ func treat(s string, color string) string { } func treatAll(color string, args ...interface{}) string { - var parts []string + parts := make([]string, 0, len(args)) for _, arg := range args { parts = append(parts, treat(fmt.Sprintf("%v", arg), color)) } diff --git a/libs/events/events.go b/libs/events/events.go index fb90bbea6..34333a068 100644 --- a/libs/events/events.go +++ b/libs/events/events.go @@ -188,7 +188,7 @@ func (cell *eventCell) RemoveListener(listenerID string) int { func (cell *eventCell) FireEvent(data EventData) { cell.mtx.RLock() - var eventCallbacks []EventCallback + eventCallbacks := make([]EventCallback, 0, len(cell.listeners)) for _, cb := range cell.listeners { eventCallbacks = append(eventCallbacks, cb) } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 0b043ca83..01d1d8db5 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -616,10 +616,9 @@ func (of oldestFirst) Less(i, j int) bool { return of[i].LastAttempt.Before(of[j // getPeersToCrawl returns addresses of potential peers that we wish to validate. // NOTE: The status information is ordered as described above. func (r *PEXReactor) getPeersToCrawl() []crawlPeerInfo { - var of oldestFirst - // TODO: be more selective addrs := r.book.ListOfKnownAddresses() + of := make(oldestFirst, 0, len(addrs)) for _, addr := range addrs { if len(addr.ID()) == 0 { continue // dont use peers without id From 23314daee4c1d3c2cb85c67f1debbdf2d5e9bd86 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 15:38:02 +0400 Subject: [PATCH 026/259] comment out clist tests w/ depend on runtime.SetFinalizer --- .golangci.yml | 1 - libs/clist/clist_test.go | 162 +++++++++++++++++++-------------------- 2 files changed, 80 insertions(+), 83 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ca86e6d5b..4cae2f76a 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,6 @@ linters: - gochecknoinits - scopelint - stylecheck - - deadcode # linters-settings: # govet: diff --git a/libs/clist/clist_test.go b/libs/clist/clist_test.go index 4ded6177a..edf013752 100644 --- a/libs/clist/clist_test.go +++ b/libs/clist/clist_test.go @@ -2,8 +2,6 @@ package clist import ( "fmt" - "runtime" - "sync/atomic" "testing" "time" @@ -65,100 +63,100 @@ func TestSmall(t *testing.T) { } -/* -This test is quite hacky because it relies on SetFinalizer -which isn't guaranteed to run at all. -*/ -// nolint: megacheck -func _TestGCFifo(t *testing.T) { +// This test is quite hacky because it relies on SetFinalizer +// which isn't guaranteed to run at all. +//func TestGCFifo(t *testing.T) { +// if runtime.GOARCH != "amd64" { +// t.Skipf("Skipping on non-amd64 machine") +// } - const numElements = 1000000 - l := New() - gcCount := new(uint64) +// const numElements = 1000000 +// l := New() +// gcCount := new(uint64) - // SetFinalizer doesn't work well with circular structures, - // so we construct a trivial non-circular structure to - // track. - type value struct { - Int int - } - done := make(chan struct{}) +// // SetFinalizer doesn't work well with circular structures, +// // so we construct a trivial non-circular structure to +// // track. +// type value struct { +// Int int +// } +// done := make(chan struct{}) - for i := 0; i < numElements; i++ { - v := new(value) - v.Int = i - l.PushBack(v) - runtime.SetFinalizer(v, func(v *value) { - atomic.AddUint64(gcCount, 1) - }) - } +// for i := 0; i < numElements; i++ { +// v := new(value) +// v.Int = i +// l.PushBack(v) +// runtime.SetFinalizer(v, func(v *value) { +// atomic.AddUint64(gcCount, 1) +// }) +// } - for el := l.Front(); el != nil; { - l.Remove(el) - //oldEl := el - el = el.Next() - //oldEl.DetachPrev() - //oldEl.DetachNext() - } +// for el := l.Front(); el != nil; { +// l.Remove(el) +// //oldEl := el +// el = el.Next() +// //oldEl.DetachPrev() +// //oldEl.DetachNext() +// } - runtime.GC() - time.Sleep(time.Second * 3) - runtime.GC() - time.Sleep(time.Second * 3) - _ = done +// runtime.GC() +// time.Sleep(time.Second * 3) +// runtime.GC() +// time.Sleep(time.Second * 3) +// _ = done - if *gcCount != numElements { - t.Errorf("Expected gcCount to be %v, got %v", numElements, - *gcCount) - } -} +// if *gcCount != numElements { +// t.Errorf("Expected gcCount to be %v, got %v", numElements, +// *gcCount) +// } +//} -/* -This test is quite hacky because it relies on SetFinalizer -which isn't guaranteed to run at all. -*/ -// nolint: megacheck -func _TestGCRandom(t *testing.T) { +// This test is quite hacky because it relies on SetFinalizer +// which isn't guaranteed to run at all. +// func TestGCRandom(t *testing.T) { +// if runtime.GOARCH != "amd64" { +// t.Skipf("Skipping on non-amd64 machine") +// } - const numElements = 1000000 - l := New() - gcCount := 0 +// const numElements = 1000000 +// l := New() +// gcCount := 0 - // SetFinalizer doesn't work well with circular structures, - // so we construct a trivial non-circular structure to - // track. - type value struct { - Int int - } +// // SetFinalizer doesn't work well with circular structures, +// // so we construct a trivial non-circular structure to +// // track. +// type value struct { +// Int int +// } - for i := 0; i < numElements; i++ { - v := new(value) - v.Int = i - l.PushBack(v) - runtime.SetFinalizer(v, func(v *value) { - gcCount++ - }) - } +// for i := 0; i < numElements; i++ { +// v := new(value) +// v.Int = i +// l.PushBack(v) +// runtime.SetFinalizer(v, func(v *value) { +// gcCount++ +// }) +// } - els := make([]*CElement, 0, numElements) - for el := l.Front(); el != nil; el = el.Next() { - els = append(els, el) - } +// els := make([]*CElement, 0, numElements) +// for el := l.Front(); el != nil; el = el.Next() { +// els = append(els, el) +// } - for _, i := range cmn.RandPerm(numElements) { - el := els[i] - l.Remove(el) - _ = el.Next() - } +// for _, i := range cmn.RandPerm(numElements) { +// el := els[i] +// l.Remove(el) +// _ = el.Next() +// } - runtime.GC() - time.Sleep(time.Second * 3) +// runtime.GC() +// time.Sleep(time.Second * 3) - if gcCount != numElements { - t.Errorf("Expected gcCount to be %v, got %v", numElements, - gcCount) - } -} +// if gcCount != numElements { +// t.Errorf("Expected gcCount to be %v, got %v", numElements, +// gcCount) +// } +// } func TestScanRightDeleteRandom(t *testing.T) { From 6941d1bb35a04a1d32a027af38d85a0d78374063 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 18:20:10 +0400 Subject: [PATCH 027/259] use nolint label instead of commenting --- blockchain/pool.go | 31 +++---- consensus/common_test.go | 30 ------- crypto/merkle/proof_test.go | 23 +++--- libs/clist/clist_test.go | 156 ++++++++++++++++++------------------ lite/proxy/query_test.go | 141 ++++++++++++++++---------------- p2p/trust/metric_test.go | 73 ++++++++--------- 6 files changed, 217 insertions(+), 237 deletions(-) diff --git a/blockchain/pool.go b/blockchain/pool.go index 236bf7e09..804a43250 100644 --- a/blockchain/pool.go +++ b/blockchain/pool.go @@ -364,22 +364,23 @@ func (pool *BlockPool) sendError(err error, peerID p2p.ID) { } // for debugging purposes -// func (pool *BlockPool) debug() string { -// pool.mtx.Lock() -// defer pool.mtx.Unlock() +//nolint:unused +func (pool *BlockPool) debug() string { + pool.mtx.Lock() + defer pool.mtx.Unlock() -// str := "" -// nextHeight := pool.height + pool.requestersLen() -// for h := pool.height; h < nextHeight; h++ { -// if pool.requesters[h] == nil { -// str += fmt.Sprintf("H(%v):X ", h) -// } else { -// str += fmt.Sprintf("H(%v):", h) -// str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil) -// } -// } -// return str -// } + str := "" + nextHeight := pool.height + pool.requestersLen() + for h := pool.height; h < nextHeight; h++ { + if pool.requesters[h] == nil { + str += fmt.Sprintf("H(%v):X ", h) + } else { + str += fmt.Sprintf("H(%v):", h) + str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil) + } + } + return str +} //------------------------------------- diff --git a/consensus/common_test.go b/consensus/common_test.go index 4e3d60e1d..e6e64c252 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -378,36 +378,6 @@ func ensureNewEvent( } } -// func ensureNewRoundStep(stepCh <-chan interface{}, height int64, round int) { -// ensureNewEvent( -// stepCh, -// height, -// round, -// ensureTimeout, -// "Timeout expired while waiting for NewStep event") -// } - -// func ensureNewVote(voteCh <-chan interface{}, height int64, round int) { -// select { -// case <-time.After(ensureTimeout): -// break -// case v := <-voteCh: -// edv, ok := v.(types.EventDataVote) -// if !ok { -// panic(fmt.Sprintf("expected a *types.Vote, "+ -// "got %v. wrong subscription channel?", -// reflect.TypeOf(v))) -// } -// vote := edv.Vote -// if vote.Height != height { -// panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height)) -// } -// if vote.Round != round { -// panic(fmt.Sprintf("expected round %v, got %v", round, vote.Round)) -// } -// } -// } - func ensureNewRound(roundCh <-chan interface{}, height int64, round int) { select { case <-time.After(ensureTimeout): diff --git a/crypto/merkle/proof_test.go b/crypto/merkle/proof_test.go index 504156246..4de3246f1 100644 --- a/crypto/merkle/proof_test.go +++ b/crypto/merkle/proof_test.go @@ -26,17 +26,18 @@ func NewDominoOp(key, input, output string) DominoOp { } } -// func DominoOpDecoder(pop ProofOp) (ProofOperator, error) { -// if pop.Type != ProofOpDomino { -// panic("unexpected proof op type") -// } -// var op DominoOp // a bit strange as we'll discard this, but it works. -// err := amino.UnmarshalBinaryLengthPrefixed(pop.Data, &op) -// if err != nil { -// return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp") -// } -// return NewDominoOp(string(pop.Key), op.Input, op.Output), nil -// } +//nolint:unused +func DominoOpDecoder(pop ProofOp) (ProofOperator, error) { + if pop.Type != ProofOpDomino { + panic("unexpected proof op type") + } + var op DominoOp // a bit strange as we'll discard this, but it works. + err := amino.UnmarshalBinaryLengthPrefixed(pop.Data, &op) + if err != nil { + return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp") + } + return NewDominoOp(string(pop.Key), op.Input, op.Output), nil +} func (dop DominoOp) ProofOp() ProofOp { bz := amino.MustMarshalBinaryLengthPrefixed(dop) diff --git a/libs/clist/clist_test.go b/libs/clist/clist_test.go index edf013752..7fb7db4d2 100644 --- a/libs/clist/clist_test.go +++ b/libs/clist/clist_test.go @@ -2,6 +2,8 @@ package clist import ( "fmt" + "runtime" + "sync/atomic" "testing" "time" @@ -65,98 +67,100 @@ func TestSmall(t *testing.T) { // This test is quite hacky because it relies on SetFinalizer // which isn't guaranteed to run at all. -//func TestGCFifo(t *testing.T) { -// if runtime.GOARCH != "amd64" { -// t.Skipf("Skipping on non-amd64 machine") -// } +//nolint:unused,deadcode +func _TestGCFifo(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skipf("Skipping on non-amd64 machine") + } -// const numElements = 1000000 -// l := New() -// gcCount := new(uint64) + const numElements = 1000000 + l := New() + gcCount := new(uint64) -// // SetFinalizer doesn't work well with circular structures, -// // so we construct a trivial non-circular structure to -// // track. -// type value struct { -// Int int -// } -// done := make(chan struct{}) + // SetFinalizer doesn't work well with circular structures, + // so we construct a trivial non-circular structure to + // track. + type value struct { + Int int + } + done := make(chan struct{}) -// for i := 0; i < numElements; i++ { -// v := new(value) -// v.Int = i -// l.PushBack(v) -// runtime.SetFinalizer(v, func(v *value) { -// atomic.AddUint64(gcCount, 1) -// }) -// } + for i := 0; i < numElements; i++ { + v := new(value) + v.Int = i + l.PushBack(v) + runtime.SetFinalizer(v, func(v *value) { + atomic.AddUint64(gcCount, 1) + }) + } -// for el := l.Front(); el != nil; { -// l.Remove(el) -// //oldEl := el -// el = el.Next() -// //oldEl.DetachPrev() -// //oldEl.DetachNext() -// } + for el := l.Front(); el != nil; { + l.Remove(el) + //oldEl := el + el = el.Next() + //oldEl.DetachPrev() + //oldEl.DetachNext() + } -// runtime.GC() -// time.Sleep(time.Second * 3) -// runtime.GC() -// time.Sleep(time.Second * 3) -// _ = done + runtime.GC() + time.Sleep(time.Second * 3) + runtime.GC() + time.Sleep(time.Second * 3) + _ = done -// if *gcCount != numElements { -// t.Errorf("Expected gcCount to be %v, got %v", numElements, -// *gcCount) -// } -//} + if *gcCount != numElements { + t.Errorf("Expected gcCount to be %v, got %v", numElements, + *gcCount) + } +} // This test is quite hacky because it relies on SetFinalizer // which isn't guaranteed to run at all. -// func TestGCRandom(t *testing.T) { -// if runtime.GOARCH != "amd64" { -// t.Skipf("Skipping on non-amd64 machine") -// } +//nolint:unused,deadcode +func TestGCRandom(t *testing.T) { + if runtime.GOARCH != "amd64" { + t.Skipf("Skipping on non-amd64 machine") + } -// const numElements = 1000000 -// l := New() -// gcCount := 0 + const numElements = 1000000 + l := New() + gcCount := 0 -// // SetFinalizer doesn't work well with circular structures, -// // so we construct a trivial non-circular structure to -// // track. -// type value struct { -// Int int -// } + // SetFinalizer doesn't work well with circular structures, + // so we construct a trivial non-circular structure to + // track. + type value struct { + Int int + } -// for i := 0; i < numElements; i++ { -// v := new(value) -// v.Int = i -// l.PushBack(v) -// runtime.SetFinalizer(v, func(v *value) { -// gcCount++ -// }) -// } + for i := 0; i < numElements; i++ { + v := new(value) + v.Int = i + l.PushBack(v) + runtime.SetFinalizer(v, func(v *value) { + gcCount++ + }) + } -// els := make([]*CElement, 0, numElements) -// for el := l.Front(); el != nil; el = el.Next() { -// els = append(els, el) -// } + els := make([]*CElement, 0, numElements) + for el := l.Front(); el != nil; el = el.Next() { + els = append(els, el) + } -// for _, i := range cmn.RandPerm(numElements) { -// el := els[i] -// l.Remove(el) -// _ = el.Next() -// } + for _, i := range cmn.RandPerm(numElements) { + el := els[i] + l.Remove(el) + _ = el.Next() + } -// runtime.GC() -// time.Sleep(time.Second * 3) + runtime.GC() + time.Sleep(time.Second * 3) -// if gcCount != numElements { -// t.Errorf("Expected gcCount to be %v, got %v", numElements, -// gcCount) -// } -// } + if gcCount != numElements { + t.Errorf("Expected gcCount to be %v, got %v", numElements, + gcCount) + } +} func TestScanRightDeleteRandom(t *testing.T) { diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index 707430b61..9547f7713 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -4,6 +4,7 @@ import ( "fmt" "os" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -20,7 +21,8 @@ import ( var node *nm.Node var chainID = "tendermint_test" // TODO use from config. -// var waitForEventTimeout = 5 * time.Second +//nolint:unused +var waitForEventTimeout = 5 * time.Second // TODO fix tests!! @@ -41,83 +43,84 @@ func kvstoreTx(k, v []byte) []byte { // TODO: enable it after general proof format has been adapted // in abci/examples/kvstore.go -// func TestAppProofs(t *testing.T) { -// assert, require := assert.New(t), require.New(t) +//nolint:unused,deadcode +func _TestAppProofs(t *testing.T) { + assert, require := assert.New(t), require.New(t) -// prt := defaultProofRuntime() -// cl := client.NewLocal(node) -// client.WaitForHeight(cl, 1, nil) + prt := defaultProofRuntime() + cl := client.NewLocal(node) + client.WaitForHeight(cl, 1, nil) -// // This sets up our trust on the node based on some past point. -// source := certclient.NewProvider(chainID, cl) -// seed, err := source.LatestFullCommit(chainID, 1, 1) -// require.NoError(err, "%#v", err) -// cert := lite.NewBaseVerifier(chainID, seed.Height(), seed.Validators) + // This sets up our trust on the node based on some past point. + source := certclient.NewProvider(chainID, cl) + seed, err := source.LatestFullCommit(chainID, 1, 1) + require.NoError(err, "%#v", err) + cert := lite.NewBaseVerifier(chainID, seed.Height(), seed.Validators) -// // Wait for tx confirmation. -// done := make(chan int64) -// go func() { -// evtTyp := types.EventTx -// _, err = client.WaitForOneEvent(cl, evtTyp, waitForEventTimeout) -// require.Nil(err, "%#v", err) -// close(done) -// }() + // Wait for tx confirmation. + done := make(chan int64) + go func() { + evtTyp := types.EventTx + _, err = client.WaitForOneEvent(cl, evtTyp, waitForEventTimeout) + require.Nil(err, "%#v", err) + close(done) + }() -// // Submit a transaction. -// k := []byte("my-key") -// v := []byte("my-value") -// tx := kvstoreTx(k, v) -// br, err := cl.BroadcastTxCommit(tx) -// require.NoError(err, "%#v", err) -// require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx) -// require.EqualValues(0, br.DeliverTx.Code) -// brh := br.Height + // Submit a transaction. + k := []byte("my-key") + v := []byte("my-value") + tx := kvstoreTx(k, v) + br, err := cl.BroadcastTxCommit(tx) + require.NoError(err, "%#v", err) + require.EqualValues(0, br.CheckTx.Code, "%#v", br.CheckTx) + require.EqualValues(0, br.DeliverTx.Code) + brh := br.Height -// // Fetch latest after tx commit. -// <-done -// latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1) -// require.NoError(err, "%#v", err) -// rootHash := latest.SignedHeader.AppHash -// if rootHash == nil { -// // Fetch one block later, AppHash hasn't been committed yet. -// // TODO find a way to avoid doing this. -// client.WaitForHeight(cl, latest.SignedHeader.Height+1, nil) -// latest, err = source.LatestFullCommit(chainID, latest.SignedHeader.Height+1, 1<<63-1) -// require.NoError(err, "%#v", err) -// rootHash = latest.SignedHeader.AppHash -// } -// require.NotNil(rootHash) + // Fetch latest after tx commit. + <-done + latest, err := source.LatestFullCommit(chainID, 1, 1<<63-1) + require.NoError(err, "%#v", err) + rootHash := latest.SignedHeader.AppHash + if rootHash == nil { + // Fetch one block later, AppHash hasn't been committed yet. + // TODO find a way to avoid doing this. + client.WaitForHeight(cl, latest.SignedHeader.Height+1, nil) + latest, err = source.LatestFullCommit(chainID, latest.SignedHeader.Height+1, 1<<63-1) + require.NoError(err, "%#v", err) + rootHash = latest.SignedHeader.AppHash + } + require.NotNil(rootHash) -// // verify a query before the tx block has no data (and valid non-exist proof) -// bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert) -// require.NoError(err, "%#v", err) -// // require.NotNil(proof) -// // TODO: Ensure that *some* keys will be there, ensuring that proof is nil, -// // (currently there's a race condition) -// // and ensure that proof proves absence of k. -// require.Nil(bs) + // verify a query before the tx block has no data (and valid non-exist proof) + bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert) + require.NoError(err, "%#v", err) + // require.NotNil(proof) + // TODO: Ensure that *some* keys will be there, ensuring that proof is nil, + // (currently there's a race condition) + // and ensure that proof proves absence of k. + require.Nil(bs) -// // but given that block it is good -// bs, height, proof, err = GetWithProof(prt, k, brh, cl, cert) -// require.NoError(err, "%#v", err) -// require.NotNil(proof) -// require.Equal(height, brh) + // but given that block it is good + bs, height, proof, err = GetWithProof(prt, k, brh, cl, cert) + require.NoError(err, "%#v", err) + require.NotNil(proof) + require.Equal(height, brh) -// assert.EqualValues(v, bs) -// err = prt.VerifyValue(proof, rootHash, string(k), bs) // XXX key encoding -// assert.NoError(err, "%#v", err) + assert.EqualValues(v, bs) + err = prt.VerifyValue(proof, rootHash, string(k), bs) // XXX key encoding + assert.NoError(err, "%#v", err) -// // Test non-existing key. -// missing := []byte("my-missing-key") -// bs, _, proof, err = GetWithProof(prt, missing, 0, cl, cert) -// require.NoError(err) -// require.Nil(bs) -// require.NotNil(proof) -// err = prt.VerifyAbsence(proof, rootHash, string(missing)) // XXX VerifyAbsence(), keyencoding -// assert.NoError(err, "%#v", err) -// err = prt.VerifyAbsence(proof, rootHash, string(k)) // XXX VerifyAbsence(), keyencoding -// assert.Error(err, "%#v", err) -// } + // Test non-existing key. + missing := []byte("my-missing-key") + bs, _, proof, err = GetWithProof(prt, missing, 0, cl, cert) + require.NoError(err) + require.Nil(bs) + require.NotNil(proof) + err = prt.VerifyAbsence(proof, rootHash, string(missing)) // XXX VerifyAbsence(), keyencoding + assert.NoError(err, "%#v", err) + err = prt.VerifyAbsence(proof, rootHash, string(k)) // XXX VerifyAbsence(), keyencoding + assert.Error(err, "%#v", err) +} func TestTxProofs(t *testing.T) { assert, require := assert.New(t), require.New(t) diff --git a/p2p/trust/metric_test.go b/p2p/trust/metric_test.go index 89327c0e1..0273dad69 100644 --- a/p2p/trust/metric_test.go +++ b/p2p/trust/metric_test.go @@ -65,44 +65,45 @@ func TestTrustMetricCopyNilPointer(t *testing.T) { } // XXX: This test fails non-deterministically -// func _TestTrustMetricStopPause(t *testing.T) { -// // The TestTicker will provide manual control over -// // the passing of time within the metric -// tt := NewTestTicker() -// tm := NewMetric() -// tm.SetTicker(tt) -// tm.Start() -// // Allow some time intervals to pass and pause -// tt.NextTick() -// tt.NextTick() -// tm.Pause() +//nolint:unused,deadcode +func _TestTrustMetricStopPause(t *testing.T) { + // The TestTicker will provide manual control over + // the passing of time within the metric + tt := NewTestTicker() + tm := NewMetric() + tm.SetTicker(tt) + tm.Start() + // Allow some time intervals to pass and pause + tt.NextTick() + tt.NextTick() + tm.Pause() -// // could be 1 or 2 because Pause and NextTick race -// first := tm.Copy().numIntervals + // could be 1 or 2 because Pause and NextTick race + first := tm.Copy().numIntervals -// // Allow more time to pass and check the intervals are unchanged -// tt.NextTick() -// tt.NextTick() -// assert.Equal(t, first, tm.Copy().numIntervals) + // Allow more time to pass and check the intervals are unchanged + tt.NextTick() + tt.NextTick() + assert.Equal(t, first, tm.Copy().numIntervals) -// // Get the trust metric activated again -// tm.GoodEvents(5) -// // Allow some time intervals to pass and stop -// tt.NextTick() -// tt.NextTick() -// tm.Stop() -// tm.Wait() + // Get the trust metric activated again + tm.GoodEvents(5) + // Allow some time intervals to pass and stop + tt.NextTick() + tt.NextTick() + tm.Stop() + tm.Wait() -// second := tm.Copy().numIntervals -// // Allow more intervals to pass while the metric is stopped -// // and check that the number of intervals match -// tm.NextTimeInterval() -// tm.NextTimeInterval() -// // XXX: fails non-deterministically: -// // expected 5, got 6 -// assert.Equal(t, second+2, tm.Copy().numIntervals) + second := tm.Copy().numIntervals + // Allow more intervals to pass while the metric is stopped + // and check that the number of intervals match + tm.NextTimeInterval() + tm.NextTimeInterval() + // XXX: fails non-deterministically: + // expected 5, got 6 + assert.Equal(t, second+2, tm.Copy().numIntervals) -// if first > second { -// t.Fatalf("numIntervals should always increase or stay the same over time") -// } -// } + if first > second { + t.Fatalf("numIntervals should always increase or stay the same over time") + } +} From 1a35895ac80caa50418ed1172ec80e71a0a6b692 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 18:23:25 +0400 Subject: [PATCH 028/259] remove gotype linter WARN [runner/nolint] Found unknown linters in //nolint directives: gotype --- consensus/byzantine_test.go | 3 +-- consensus/state.go | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 6f46c04d3..ba69d0cc1 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -76,8 +76,7 @@ func TestByzantine(t *testing.T) { conR.SetLogger(logger.With("validator", i)) conR.SetEventBus(eventBus) - var conRI p2p.Reactor // nolint: gotype, gosimple - conRI = conR + var conRI p2p.Reactor = conR // make first val byzantine if i == 0 { diff --git a/consensus/state.go b/consensus/state.go index c8185d46e..74165801b 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -454,7 +454,7 @@ func (cs *ConsensusState) updateRoundStep(round int, step cstypes.RoundStepType) // enterNewRound(height, 0) at cs.StartTime. func (cs *ConsensusState) scheduleRound0(rs *cstypes.RoundState) { //cs.Logger.Info("scheduleRound0", "now", tmtime.Now(), "startTime", cs.StartTime) - sleepDuration := rs.StartTime.Sub(tmtime.Now()) // nolint: gotype, gosimple + sleepDuration := rs.StartTime.Sub(tmtime.Now()) cs.scheduleTimeout(sleepDuration, rs.Height, 0, cstypes.RoundStepNewHeight) } From 1386707ceb922f8cd390105c86c6a598c044f748 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 6 Feb 2019 18:36:54 +0400 Subject: [PATCH 029/259] rename TestGCRandom to _TestGCRandom --- libs/clist/clist_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/clist/clist_test.go b/libs/clist/clist_test.go index 7fb7db4d2..13aca3577 100644 --- a/libs/clist/clist_test.go +++ b/libs/clist/clist_test.go @@ -117,7 +117,7 @@ func _TestGCFifo(t *testing.T) { // This test is quite hacky because it relies on SetFinalizer // which isn't guaranteed to run at all. //nolint:unused,deadcode -func TestGCRandom(t *testing.T) { +func _TestGCRandom(t *testing.T) { if runtime.GOARCH != "amd64" { t.Skipf("Skipping on non-amd64 machine") } From 4429826229a1b036c6396d4faf4f60ed9a0065ab Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Feb 2019 10:14:03 -0500 Subject: [PATCH 030/259] cmn: GetFreePort (#3255) --- libs/common/net.go | 17 +++++++++++++++++ p2p/test_util.go | 22 ++++++---------------- rpc/test/helpers.go | 15 +++++++++------ 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/libs/common/net.go b/libs/common/net.go index bdbe38f79..c7fff4cc3 100644 --- a/libs/common/net.go +++ b/libs/common/net.go @@ -24,3 +24,20 @@ func ProtocolAndAddress(listenAddr string) (string, string) { } return protocol, address } + +// GetFreePort gets a free port from the operating system. +// Ripped from https://github.com/phayes/freeport. +// BSD-licensed. +func GetFreePort() (int, error) { + addr, err := net.ResolveTCPAddr("tcp", "localhost:0") + if err != nil { + return 0, err + } + + l, err := net.ListenTCP("tcp", addr) + if err != nil { + return 0, err + } + defer l.Close() + return l.Addr().(*net.TCPAddr).Port, nil +} diff --git a/p2p/test_util.go b/p2p/test_util.go index 04629fcae..aad32ef8f 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -247,35 +247,25 @@ func testNodeInfo(id ID, name string) NodeInfo { } func testNodeInfoWithNetwork(id ID, name, network string) NodeInfo { - port, err := getFreePort() - if err != nil { - panic(err) - } return DefaultNodeInfo{ ProtocolVersion: defaultProtocolVersion, ID_: id, - ListenAddr: fmt.Sprintf("127.0.0.1:%d", port), + ListenAddr: fmt.Sprintf("127.0.0.1:%d", getFreePort()), Network: network, Version: "1.2.3-rc0-deadbeef", Channels: []byte{testCh}, Moniker: name, Other: DefaultNodeInfoOther{ TxIndex: "on", - RPCAddress: fmt.Sprintf("127.0.0.1:%d", port), + RPCAddress: fmt.Sprintf("127.0.0.1:%d", getFreePort()), }, } } -func getFreePort() (int, error) { - addr, err := net.ResolveTCPAddr("tcp", "localhost:0") +func getFreePort() int { + port, err := cmn.GetFreePort() if err != nil { - return 0, err + panic(err) } - - l, err := net.ListenTCP("tcp", addr) - if err != nil { - return 0, err - } - defer l.Close() - return l.Addr().(*net.TCPAddr).Port, nil + return port } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index b89c0a177..67439b1da 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -11,9 +11,9 @@ import ( "github.com/tendermint/tendermint/libs/log" abci "github.com/tendermint/tendermint/abci/types" - cmn "github.com/tendermint/tendermint/libs/common" cfg "github.com/tendermint/tendermint/config" + cmn "github.com/tendermint/tendermint/libs/common" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" @@ -64,14 +64,17 @@ func makePathname() string { } func randPort() int { - return int(cmn.RandUint16()/2 + 10000) + port, err := cmn.GetFreePort() + if err != nil { + panic(err) + } + return port } func makeAddrs() (string, string, string) { - start := randPort() - return fmt.Sprintf("tcp://0.0.0.0:%d", start), - fmt.Sprintf("tcp://0.0.0.0:%d", start+1), - fmt.Sprintf("tcp://0.0.0.0:%d", start+2) + return fmt.Sprintf("tcp://0.0.0.0:%d", randPort()), + fmt.Sprintf("tcp://0.0.0.0:%d", randPort()), + fmt.Sprintf("tcp://0.0.0.0:%d", randPort()) } // GetConfig returns a config for the test cases as a singleton From 45b70ae031f18bb5b95794205785bd90e36ccd4b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Feb 2019 10:24:43 -0500 Subject: [PATCH 031/259] fix non deterministic test failures and race in privval socket (#3258) * node: decrease retry conn timeout in test Should fix #3256 The retry timeout was set to the default, which is the same as the accept timeout, so it's no wonder this would fail. Here we decrease the retry timeout so we can try many times before the accept timeout. * p2p: increase handshake timeout in test This fails sometimes, presumably because the handshake timeout is so low (only 50ms). So increase it to 1s. Should fix #3187 * privval: fix race with ping. closes #3237 Pings happen in a go-routine and can happen concurrently with other messages. Since we use a request/response protocol, we expect to send a request and get back the corresponding response. But with pings happening concurrently, this assumption could be violated. We were using a mutex, but only a RWMutex, where the RLock was being held for sending messages - this was to allow the underlying connection to be replaced if it fails. Turns out we actually need to use a full lock (not just a read lock) to prevent multiple requests from happening concurrently. * node: fix test name. DelayedStop -> DelayedStart * autofile: Wait() method In the TestWALTruncate in consensus/wal_test.go we remove the WAL directory at the end of the test. However the wal.Stop() does not properly wait for the autofile group to finish shutting down. Hence it was possible that the group's go-routine is still running when the cleanup happens, which causes a panic since the directory disappeared. Here we add a Wait() method to properly wait until the go-routine exits so we can safely clean up. This fixes #2852. --- consensus/wal.go | 9 +++++++++ consensus/wal_test.go | 7 ++++++- libs/autofile/group.go | 12 ++++++++++++ node/node_test.go | 21 ++++++++++----------- p2p/test_util.go | 2 +- privval/client.go | 24 +++++++++++++----------- 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/consensus/wal.go b/consensus/wal.go index ba89cd1aa..d56ede26d 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -112,11 +112,20 @@ func (wal *baseWAL) OnStart() error { return err } +// Stop the underlying autofile group. +// Use Wait() to ensure it's finished shutting down +// before cleaning up files. func (wal *baseWAL) OnStop() { wal.group.Stop() wal.group.Close() } +// Wait for the underlying autofile group to finish shutting down +// so it's safe to cleanup files. +func (wal *baseWAL) Wait() { + wal.group.Wait() +} + // Write is called in newStep and for each receive on the // peerMsgQueue and the timeoutTicker. // NOTE: does not call fsync() diff --git a/consensus/wal_test.go b/consensus/wal_test.go index c2a7d8bba..93beb68bb 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -39,7 +39,12 @@ func TestWALTruncate(t *testing.T) { wal.SetLogger(log.TestingLogger()) err = wal.Start() require.NoError(t, err) - defer wal.Stop() + defer func() { + wal.Stop() + // wait for the wal to finish shutting down so we + // can safely remove the directory + wal.Wait() + }() //60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), when headBuf is full, truncate content will Flush to the file. //at this time, RotateFile is called, truncate content exist in each file. diff --git a/libs/autofile/group.go b/libs/autofile/group.go index 1ec6b240d..7e9269461 100644 --- a/libs/autofile/group.go +++ b/libs/autofile/group.go @@ -67,6 +67,11 @@ type Group struct { minIndex int // Includes head maxIndex int // Includes head, where Head will move to + // close this when the processTicks routine is done. + // this ensures we can cleanup the dir after calling Stop + // and the routine won't be trying to access it anymore + doneProcessTicks chan struct{} + // TODO: When we start deleting files, we need to start tracking GroupReaders // and their dependencies. } @@ -90,6 +95,7 @@ func OpenGroup(headPath string, groupOptions ...func(*Group)) (g *Group, err err groupCheckDuration: defaultGroupCheckDuration, minIndex: 0, maxIndex: 0, + doneProcessTicks: make(chan struct{}), } for _, option := range groupOptions { @@ -140,6 +146,11 @@ func (g *Group) OnStop() { g.Flush() // flush any uncommitted data } +func (g *Group) Wait() { + // wait for processTicks routine to finish + <-g.doneProcessTicks +} + // Close closes the head file. The group must be stopped by this moment. func (g *Group) Close() { g.Flush() // flush any uncommitted data @@ -211,6 +222,7 @@ func (g *Group) Flush() error { } func (g *Group) processTicks() { + defer close(g.doneProcessTicks) for { select { case <-g.ticker.C: diff --git a/node/node_test.go b/node/node_test.go index 06561e07d..3218c8327 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -88,13 +88,13 @@ func TestSplitAndTrimEmpty(t *testing.T) { } } -func TestNodeDelayedStop(t *testing.T) { - config := cfg.ResetTestRoot("node_delayed_node_test") +func TestNodeDelayedStart(t *testing.T) { + config := cfg.ResetTestRoot("node_delayed_start_test") now := tmtime.Now() // create & start node n, err := DefaultNewNode(config, log.TestingLogger()) - n.GenesisDoc().GenesisTime = now.Add(5 * time.Second) + n.GenesisDoc().GenesisTime = now.Add(2 * time.Second) require.NoError(t, err) n.Start() @@ -133,6 +133,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { types.NewMockPV(), dialer, ) + privval.RemoteSignerConnDeadline(100 * time.Millisecond)(pvsc) go func() { err := pvsc.Start() @@ -172,20 +173,18 @@ func TestNodeSetPrivValIPC(t *testing.T) { types.NewMockPV(), dialer, ) + privval.RemoteSignerConnDeadline(100 * time.Millisecond)(pvsc) - done := make(chan struct{}) go func() { - defer close(done) - n, err := DefaultNewNode(config, log.TestingLogger()) + err := pvsc.Start() require.NoError(t, err) - assert.IsType(t, &privval.SocketVal{}, n.PrivValidator()) }() - - err := pvsc.Start() - require.NoError(t, err) defer pvsc.Stop() - <-done + n, err := DefaultNewNode(config, log.TestingLogger()) + require.NoError(t, err) + assert.IsType(t, &privval.SocketVal{}, n.PrivValidator()) + } // testFreeAddr claims a free port so we don't block on listener being ready. diff --git a/p2p/test_util.go b/p2p/test_util.go index aad32ef8f..2d320df85 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -125,7 +125,7 @@ func (sw *Switch) addPeerWithConnection(conn net.Conn) error { return err } - ni, err := handshake(conn, 50*time.Millisecond, sw.nodeInfo) + ni, err := handshake(conn, time.Second, sw.nodeInfo) if err != nil { if err := conn.Close(); err != nil { sw.Logger.Error("Error closing connection", "err", err) diff --git a/privval/client.go b/privval/client.go index 1ad104d8d..11151fee3 100644 --- a/privval/client.go +++ b/privval/client.go @@ -53,9 +53,11 @@ type SocketVal struct { // reset if the connection fails. // failures are detected by a background // ping routine. + // All messages are request/response, so we hold the mutex + // so only one request/response pair can happen at a time. // Methods on the underlying net.Conn itself // are already gorountine safe. - mtx sync.RWMutex + mtx sync.Mutex signer *RemoteSignerClient } @@ -82,22 +84,22 @@ func NewSocketVal( // GetPubKey implements PrivValidator. func (sc *SocketVal) GetPubKey() crypto.PubKey { - sc.mtx.RLock() - defer sc.mtx.RUnlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() return sc.signer.GetPubKey() } // SignVote implements PrivValidator. func (sc *SocketVal) SignVote(chainID string, vote *types.Vote) error { - sc.mtx.RLock() - defer sc.mtx.RUnlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() return sc.signer.SignVote(chainID, vote) } // SignProposal implements PrivValidator. func (sc *SocketVal) SignProposal(chainID string, proposal *types.Proposal) error { - sc.mtx.RLock() - defer sc.mtx.RUnlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() return sc.signer.SignProposal(chainID, proposal) } @@ -106,15 +108,15 @@ func (sc *SocketVal) SignProposal(chainID string, proposal *types.Proposal) erro // Ping is used to check connection health. func (sc *SocketVal) Ping() error { - sc.mtx.RLock() - defer sc.mtx.RUnlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() return sc.signer.Ping() } // Close closes the underlying net.Conn. func (sc *SocketVal) Close() { - sc.mtx.RLock() - defer sc.mtx.RUnlock() + sc.mtx.Lock() + defer sc.mtx.Unlock() if sc.signer != nil { if err := sc.signer.Close(); err != nil { sc.Logger.Error("OnStop", "err", err) From 9e9026452cb337fa7110ccc62fd3f6707894e37f Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 6 Feb 2019 10:29:51 -0500 Subject: [PATCH 032/259] p2p/conn: don't hold stopMtx while waiting (#3254) * p2p/conn: fix deadlock in FlushStop/OnStop * makefile: set_with_deadlock * close doneSendRoutine at end of sendRoutine * conn: initialize channs in OnStart --- Makefile | 7 ++-- p2p/conn/connection.go | 79 ++++++++++++++++++++++-------------------- 2 files changed, 46 insertions(+), 40 deletions(-) diff --git a/Makefile b/Makefile index 8c0928d04..57e465439 100644 --- a/Makefile +++ b/Makefile @@ -228,11 +228,14 @@ test_race: # uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks test_with_deadlock: + make set_with_deadlock + make test + make cleanup_after_test_with_deadlock + +set_with_deadlock: find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.RWMutex/deadlock.RWMutex/' find . -name "*.go" | grep -v "vendor/" | xargs -n 1 sed -i.bak 's/sync.Mutex/deadlock.Mutex/' find . -name "*.go" | grep -v "vendor/" | xargs -n 1 goimports -w - make test - make cleanup_after_test_with_deadlock # cleanes up after you ran test_with_deadlock cleanup_after_test_with_deadlock: diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index 920734914..c1e90ab76 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -85,8 +85,8 @@ type MConnection struct { errored uint32 config MConnConfig - // Closing quitSendRoutine will cause - // doneSendRoutine to close. + // Closing quitSendRoutine will cause the sendRoutine to eventually quit. + // doneSendRoutine is closed when the sendRoutine actually quits. quitSendRoutine chan struct{} doneSendRoutine chan struct{} @@ -200,29 +200,28 @@ func (c *MConnection) OnStart() error { if err := c.BaseService.OnStart(); err != nil { return err } - c.quitSendRoutine = make(chan struct{}) - c.doneSendRoutine = make(chan struct{}) c.flushTimer = cmn.NewThrottleTimer("flush", c.config.FlushThrottle) c.pingTimer = cmn.NewRepeatTimer("ping", c.config.PingInterval) c.pongTimeoutCh = make(chan bool, 1) c.chStatsTimer = cmn.NewRepeatTimer("chStats", updateStats) + c.quitSendRoutine = make(chan struct{}) + c.doneSendRoutine = make(chan struct{}) go c.sendRoutine() go c.recvRoutine() return nil } -// FlushStop replicates the logic of OnStop. -// It additionally ensures that all successful -// .Send() calls will get flushed before closing -// the connection. -func (c *MConnection) FlushStop() { +// stopServices stops the BaseService and timers and closes the quitSendRoutine. +// if the quitSendRoutine was already closed, it returns true, otherwise it returns false. +// It uses the stopMtx to ensure only one of FlushStop and OnStop can do this at a time. +func (c *MConnection) stopServices() (alreadyStopped bool) { c.stopMtx.Lock() defer c.stopMtx.Unlock() select { case <-c.quitSendRoutine: - // already quit via OnStop - return + // already quit via FlushStop or OnStop + return true default: } @@ -230,25 +229,40 @@ func (c *MConnection) FlushStop() { c.flushTimer.Stop() c.pingTimer.Stop() c.chStatsTimer.Stop() - if c.quitSendRoutine != nil { - close(c.quitSendRoutine) + + close(c.quitSendRoutine) + return false +} + +// FlushStop replicates the logic of OnStop. +// It additionally ensures that all successful +// .Send() calls will get flushed before closing +// the connection. +func (c *MConnection) FlushStop() { + if c.stopServices() { + return + } + + // this block is unique to FlushStop + { // wait until the sendRoutine exits // so we dont race on calling sendSomePacketMsgs <-c.doneSendRoutine + + // Send and flush all pending msgs. + // By now, IsRunning == false, + // so any concurrent attempts to send will fail. + // Since sendRoutine has exited, we can call this + // safely + eof := c.sendSomePacketMsgs() + for !eof { + eof = c.sendSomePacketMsgs() + } + c.flush() + + // Now we can close the connection } - // Send and flush all pending msgs. - // By now, IsRunning == false, - // so any concurrent attempts to send will fail. - // Since sendRoutine has exited, we can call this - // safely - eof := c.sendSomePacketMsgs() - for !eof { - eof = c.sendSomePacketMsgs() - } - c.flush() - - // Now we can close the connection c.conn.Close() // nolint: errcheck // We can't close pong safely here because @@ -261,21 +275,10 @@ func (c *MConnection) FlushStop() { // OnStop implements BaseService func (c *MConnection) OnStop() { - c.stopMtx.Lock() - defer c.stopMtx.Unlock() - - select { - case <-c.quitSendRoutine: - // already quit via FlushStop + if c.stopServices() { return - default: } - c.BaseService.OnStop() - c.flushTimer.Stop() - c.pingTimer.Stop() - c.chStatsTimer.Stop() - close(c.quitSendRoutine) c.conn.Close() // nolint: errcheck // We can't close pong safely here because @@ -433,7 +436,6 @@ FOR_LOOP: c.sendMonitor.Update(int(_n)) c.flush() case <-c.quitSendRoutine: - close(c.doneSendRoutine) break FOR_LOOP case <-c.send: // Send some PacketMsgs @@ -459,6 +461,7 @@ FOR_LOOP: // Cleanup c.stopPongTimer() + close(c.doneSendRoutine) } // Returns true if messages from channels were exhausted. From e70f27c8e45848687fb1b5ee73b6e1f4ae765262 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Thu, 7 Feb 2019 08:32:32 +0200 Subject: [PATCH 033/259] Add remote signer test harness (KMS) (#3149) * WIP: Starts adding remote signer test harness This commit adds a new command to Tendermint to allow for us to build a standalone binary to test remote signers such as KMS (https://github.com/tendermint/kms). Right now, all it does is test that the local public key matches the public key reported by the client, and fails at the point where it attempts to get the client to sign a proposal. * Fixes typo * Fixes proposal validation test This commit fixes the proposal validation test as per #3149. It also moves the test harness into its own internal package to isolate its exports from the `privval` package. * Adds vote signing validation * Applying recommendations from #3149 * Adds function descriptions for test harness * Adds ability to ask remote signer to shut down Prior to this commit, the remote signer needs to manually be shut down, which is not ideal for automated testing. This commit allows us to send a poison pill message to the KMS to let it shut down gracefully once testing is done (whether the tests pass or fail). * Adds tests for remote signer test harness This commit makes some minor modifications to a few files to allow for testing of the remote signer test harness. Two tests are added here: checking for a fully successful (the ideal) case, and for the case where the maximum number of retries has been reached when attempting to accept incoming connections from the remote signer. * Condenses serialization of proposals and votes using existing Tendermint functions * Removes now-unnecessary amino import and codec * Adds error message for vote signing failure * Adds key extraction command for integration test Took the code from here: https://gist.github.com/Liamsi/a80993f24bff574bbfdbbfa9efa84bc7 to create a simple utility command to extract a key from a local Tendermint validator for use in KMS integration testing. * Makes path expansion success non-compulsory * Fixes segfault on SIGTERM We need an additional variable to keep track of whether we're successfully connected, otherwise hitting Ctrl+Break during execution causes a segmentation fault. This now allows for a clean shutdown. * Consolidates shutdown checks * Adds comments indicating codes for easy lookup * Adds Docker build for remote signer harness Updates the `DOCKER/build.sh` and `DOCKER/push.sh` files to allow one to override the image name and Dockerfile using environment variables. Updates the primary `Makefile` as well as the `DOCKER/Makefile` to allow for building the `remote_val_harness` Docker image. * Adds build_remote_val_harness_docker_image to .PHONY * Removes remote signer poison pill messaging functionality * Reduces fluff code in command line parsing As per https://github.com/tendermint/tendermint/pull/3149#pullrequestreview-196171788, this reduces the amount of fluff code in the PR down to the bare minimum. * Fixes ordering of error check and info log * Moves remove_val_harness cmd into tools folder It seems to make sense to rather keep the remote signer test harness in its own tool folder (now rather named `tm-signer-harness` to keep with the tool naming convention). It is actually a separate tool, not meant to be one of the core binaries, but supplementary and supportive. * Updates documentation for tm-signer-harness * Refactors flag parsing to be more compact and less redundant * Adds version sub-command help * Removes extraneous flags parsing * Adds CHANGELOG_PENDING entry for tm-signer-harness * Improves test coverage Adds a few extra parameters to the `MockPV` type to fake broken vote and proposal signing. Also adds some more tests for the test harness so as to increase coverage for failed cases. * Fixes formatting for CHANGELOG_PENDING.md * Fix formatting for documentation config * Point users towards official Tendermint docs for tools documentation * Point users towards official Tendermint docs for tm-signer-harness * Remove extraneous constant * Rename TestHarness.sc to TestHarness.spv for naming consistency * Refactor to remove redundant goroutine * Refactor conditional to cleaner switch statement and better error handling for listener protocol * Remove extraneous goroutine * Add note about installing tmkms via Cargo * Fix typo in naming of output signing key * Add note about where to find chain ID * Replace /home/user with ~/ for brevity * Fixes "signer.key" typo * Minor edits for clarification for tm-signer-harness bulid/setup process --- CHANGELOG_PENDING.md | 7 +- docs/.vuepress/config.js | 9 +- docs/tools/README.md | 7 +- docs/tools/remote-signer-validation.md | 146 +++++++ tools/README.md | 4 +- tools/tm-signer-harness/Dockerfile | 4 + tools/tm-signer-harness/Makefile | 20 + tools/tm-signer-harness/README.md | 5 + .../internal/test_harness.go | 392 ++++++++++++++++++ .../internal/test_harness_test.go | 201 +++++++++ tools/tm-signer-harness/internal/utils.go | 25 ++ tools/tm-signer-harness/main.go | 174 ++++++++ types/priv_validator.go | 27 +- 13 files changed, 1006 insertions(+), 15 deletions(-) create mode 100644 docs/tools/remote-signer-validation.md create mode 100644 tools/tm-signer-harness/Dockerfile create mode 100644 tools/tm-signer-harness/Makefile create mode 100644 tools/tm-signer-harness/README.md create mode 100644 tools/tm-signer-harness/internal/test_harness.go create mode 100644 tools/tm-signer-harness/internal/test_harness_test.go create mode 100644 tools/tm-signer-harness/internal/utils.go create mode 100644 tools/tm-signer-harness/main.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index daa42654c..2c93469da 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,9 +20,10 @@ Special thanks to external contributors on this release: ### FEATURES: ### IMPROVEMENTS: -- [tools] add go-deadlock tool to help detect deadlocks -- [crypto] \#3163 use ethereum's libsecp256k1 go-wrapper for signatures when cgo is available -- [crypto] \#3162 wrap btcd instead of forking it to keep up with fixes (used if cgo is not available) +- [tools] Add go-deadlock tool to help detect deadlocks +- [tools] \#3106 Add tm-signer-harness test harness for remote signers +- [crypto] \#3163 Use ethereum's libsecp256k1 go-wrapper for signatures when cgo is available +- [crypto] \#3162 Wrap btcd instead of forking it to keep up with fixes (used if cgo is not available) ### BUG FIXES: - [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 3e55a2404..fb3620e34 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -79,10 +79,11 @@ module.exports = { title: "Tools", collapsable: false, children: [ - "/tools/", - "/tools/benchmarking", - "/tools/monitoring" - ] + "/tools/", + "/tools/benchmarking", + "/tools/monitoring", + "/tools/remote-signer-validation" + ] }, { title: "Tendermint Spec", diff --git a/docs/tools/README.md b/docs/tools/README.md index ef1ae7c22..0b861621a 100644 --- a/docs/tools/README.md +++ b/docs/tools/README.md @@ -1,4 +1,7 @@ # Overview -Tendermint comes with some tools for [benchmarking](./benchmarking.md) -and [monitoring](./monitoring.md). +Tendermint comes with some tools for: + +* [Benchmarking](./benchmarking.md) +* [Monitoring](./monitoring.md) +* [Validation of remote signers](./remote-signer-validation.md) diff --git a/docs/tools/remote-signer-validation.md b/docs/tools/remote-signer-validation.md new file mode 100644 index 000000000..c8a948e3e --- /dev/null +++ b/docs/tools/remote-signer-validation.md @@ -0,0 +1,146 @@ +# tm-signer-harness + +Located under the `tools/tm-signer-harness` folder in the [Tendermint +repository](https://github.com/tendermint/tendermint). + +The Tendermint remote signer test harness facilitates integration testing +between Tendermint and remote signers such as +[KMS](https://github.com/tendermint/kms). Such remote signers allow for signing +of important Tendermint messages using +[HSMs](https://en.wikipedia.org/wiki/Hardware_security_module), providing +additional security. + +When executed, `tm-signer-harness`: + +1. Runs a listener (either TCP or Unix sockets). +2. Waits for a connection from the remote signer. +3. Upon connection from the remote signer, executes a number of automated tests + to ensure compatibility. +4. Upon successful validation, the harness process exits with a 0 exit code. + Upon validation failure, it exits with a particular exit code related to the + error. + +## Prerequisites +Requires the same prerequisites as for building +[Tendermint](https://github.com/tendermint/tendermint). + +## Building +From the `tools/tm-signer-harness` directory in your Tendermint source +repository, simply run: + +```bash +make + +# To have global access to this executable +make install +``` + +## Docker Image +To build a Docker image containing the `tm-signer-harness`, also from the +`tools/tm-signer-harness` directory of your Tendermint source repo, simply run: + +```bash +make docker-image +``` + +## Running against KMS +As an example of how to use `tm-signer-harness`, the following instructions show +you how to execute its tests against [KMS](https://github.com/tendermint/kms). +For this example, we will make use of the **software signing module in KMS**, as +the hardware signing module requires a physical +[YubiHSM](https://www.yubico.com/products/yubihsm/) device. + +### Step 1: Install KMS on your local machine +See the [KMS repo](https://github.com/tendermint/kms) for details on how to set +KMS up on your local machine. + +If you have [Rust](https://www.rust-lang.org/) installed on your local machine, +you can simply install KMS by: + +```bash +cargo install tmkms +``` + +### Step 2: Make keys for KMS +The KMS software signing module needs a key with which to sign messages. In our +example, we will simply export a signing key from our local Tendermint instance. + +```bash +# Will generate all necessary Tendermint configuration files, including: +# - ~/.tendermint/config/priv_validator_key.json +# - ~/.tendermint/data/priv_validator_state.json +tendermint init + +# Extract the signing key from our local Tendermint instance +tm-signer-harness extract_key \ # Use the "extract_key" command + -tmhome ~/.tendermint \ # Where to find the Tendermint home directory + -output ./signing.key # Where to write the key +``` + +Also, because we want KMS to connect to `tm-signer-harness`, we will need to +provide a secret connection key from KMS' side: + +```bash +tmkms keygen secret_connection.key +``` + +### Step 3: Configure and run KMS +KMS needs some configuration to tell it to use the softer signing module as well +as the `signing.key` file we just generated. Save the following to a file called +`tmkms.toml`: + +```toml +[[validator]] +addr = "tcp://127.0.0.1:61219" # This is where we will find tm-signer-harness. +chain_id = "test-chain-0XwP5E" # The Tendermint chain ID for which KMS will be signing (found in ~/.tendermint/config/genesis.json). +reconnect = true # true is the default +secret_key = "./secret_connection.key" # Where to find our secret connection key. + +[[providers.softsign]] +id = "test-chain-0XwP5E" # The Tendermint chain ID for which KMS will be signing (same as validator.chain_id above). +path = "./signing.key" # The signing key we extracted earlier. +``` + +Then run KMS with this configuration: + +```bash +tmkms start -c tmkms.toml +``` + +This will start KMS, which will repeatedly try to connect to +`tcp://127.0.0.1:61219` until it is successful. + +### Step 4: Run tm-signer-harness +Now we get to run the signer test harness: + +```bash +tm-signer-harness run \ # The "run" command executes the tests + -addr tcp://127.0.0.1:61219 \ # The address we promised KMS earlier + -tmhome ~/.tendermint # Where to find our Tendermint configuration/data files. +``` + +If the current version of Tendermint and KMS are compatible, `tm-signer-harness` +should now exit with a 0 exit code. If they are somehow not compatible, it +should exit with a meaningful non-zero exit code (see the exit codes below). + +### Step 5: Shut down KMS +Simply hit Ctrl+Break on your KMS instance (or use the `kill` command in Linux) +to terminate it gracefully. + +## Exit Code Meanings +The following list shows the various exit codes from `tm-signer-harness` and +their meanings: + +| Exit Code | Description | +| --- | --- | +| 0 | Success! | +| 1 | Invalid command line parameters supplied to `tm-signer-harness` | +| 2 | Maximum number of accept retries reached (the `-accept-retries` parameter) | +| 3 | Failed to load `${TMHOME}/config/genesis.json` | +| 4 | Failed to create listener specified by `-addr` parameter | +| 5 | Failed to start listener | +| 6 | Interrupted by `SIGINT` (e.g. when hitting Ctrl+Break or Ctrl+C) | +| 7 | Other unknown error | +| 8 | Test 1 failed: public key mismatch | +| 9 | Test 2 failed: signing of proposals failed | +| 10 | Test 3 failed: signing of votes failed | diff --git a/tools/README.md b/tools/README.md index aeb411410..041067e74 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,3 +1,5 @@ # tools -Tools for working with tendermint and associated technologies. Documentation can be found in the `README.md` of each the `tm-bench/` and `tm-monitor/` directories. +Tools for working with Tendermint and associated technologies. Documentation for +these tools can be found online in the [Tendermint tools +documentation](https://tendermint.com/docs/tools/). diff --git a/tools/tm-signer-harness/Dockerfile b/tools/tm-signer-harness/Dockerfile new file mode 100644 index 000000000..83f57a3d5 --- /dev/null +++ b/tools/tm-signer-harness/Dockerfile @@ -0,0 +1,4 @@ +ARG TENDERMINT_VERSION=latest +FROM tendermint/tendermint:${TENDERMINT_VERSION} + +COPY tm-signer-harness /usr/bin/tm-signer-harness diff --git a/tools/tm-signer-harness/Makefile b/tools/tm-signer-harness/Makefile new file mode 100644 index 000000000..47cd03650 --- /dev/null +++ b/tools/tm-signer-harness/Makefile @@ -0,0 +1,20 @@ +.PHONY: build install docker-image + +TENDERMINT_VERSION?=latest +BUILD_TAGS?='tendermint' +BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" + +.DEFAULT_GOAL := build + +build: + CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o ../../build/tm-signer-harness main.go + +install: + CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) . + +docker-image: + GOOS=linux GOARCH=amd64 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o tm-signer-harness main.go + docker build \ + --build-arg TENDERMINT_VERSION=$(TENDERMINT_VERSION) \ + -t tendermint/tm-signer-harness:$(TENDERMINT_VERSION) . + rm -rf tm-signer-harness diff --git a/tools/tm-signer-harness/README.md b/tools/tm-signer-harness/README.md new file mode 100644 index 000000000..7add3a997 --- /dev/null +++ b/tools/tm-signer-harness/README.md @@ -0,0 +1,5 @@ +# tm-signer-harness + +See the [`tm-signer-harness` +documentation](https://tendermint.com/docs/tools/remote-signer-validation.html) +for more details. diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go new file mode 100644 index 000000000..b961f2384 --- /dev/null +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -0,0 +1,392 @@ +package internal + +import ( + "fmt" + "net" + "os" + "os/signal" + "time" + + "github.com/tendermint/tendermint/crypto/tmhash" + + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/state" + + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" +) + +// Test harness error codes (which act as exit codes when the test harness fails). +const ( + NoError int = iota // 0 + ErrInvalidParameters // 1 + ErrMaxAcceptRetriesReached // 2 + ErrFailedToLoadGenesisFile // 3 + ErrFailedToCreateListener // 4 + ErrFailedToStartListener // 5 + ErrInterrupted // 6 + ErrOther // 7 + ErrTestPublicKeyFailed // 8 + ErrTestSignProposalFailed // 9 + ErrTestSignVoteFailed // 10 +) + +var voteTypes = []types.SignedMsgType{types.PrevoteType, types.PrecommitType} + +// TestHarnessError allows us to keep track of which exit code should be used +// when exiting the main program. +type TestHarnessError struct { + Code int // The exit code to return + Err error // The original error + Info string // Any additional information +} + +var _ error = (*TestHarnessError)(nil) + +// TestHarness allows for testing of a remote signer to ensure compatibility +// with this version of Tendermint. +type TestHarness struct { + addr string + spv *privval.SocketVal + fpv *privval.FilePV + chainID string + acceptRetries int + logger log.Logger + exitWhenComplete bool + exitCode int +} + +// TestHarnessConfig provides configuration to set up a remote signer test +// harness. +type TestHarnessConfig struct { + BindAddr string + + KeyFile string + StateFile string + GenesisFile string + + AcceptDeadline time.Duration + ConnDeadline time.Duration + AcceptRetries int + + SecretConnKey ed25519.PrivKeyEd25519 + + ExitWhenComplete bool // Whether or not to call os.Exit when the harness has completed. +} + +// timeoutError can be used to check if an error returned from the netp package +// was due to a timeout. +type timeoutError interface { + Timeout() bool +} + +// NewTestHarness will load Tendermint data from the given files (including +// validator public/private keypairs and chain details) and create a new +// harness. +func NewTestHarness(logger log.Logger, cfg TestHarnessConfig) (*TestHarness, error) { + keyFile := ExpandPath(cfg.KeyFile) + stateFile := ExpandPath(cfg.StateFile) + logger.Info("Loading private validator configuration", "keyFile", keyFile, "stateFile", stateFile) + // NOTE: LoadFilePV ultimately calls os.Exit on failure. No error will be + // returned if this call fails. + fpv := privval.LoadFilePV(keyFile, stateFile) + + genesisFile := ExpandPath(cfg.GenesisFile) + logger.Info("Loading chain ID from genesis file", "genesisFile", genesisFile) + st, err := state.MakeGenesisDocFromFile(genesisFile) + if err != nil { + return nil, newTestHarnessError(ErrFailedToLoadGenesisFile, err, genesisFile) + } + logger.Info("Loaded genesis file", "chainID", st.ChainID) + + spv, err := newTestHarnessSocketVal(logger, cfg) + if err != nil { + return nil, newTestHarnessError(ErrFailedToCreateListener, err, "") + } + + return &TestHarness{ + addr: cfg.BindAddr, + spv: spv, + fpv: fpv, + chainID: st.ChainID, + acceptRetries: cfg.AcceptRetries, + logger: logger, + exitWhenComplete: cfg.ExitWhenComplete, + exitCode: 0, + }, nil +} + +// Run will execute the tests associated with this test harness. The intention +// here is to call this from one's `main` function, as the way it succeeds or +// fails at present is to call os.Exit() with an exit code related to the error +// that caused the tests to fail, or exit code 0 on success. +func (th *TestHarness) Run() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + go func() { + for sig := range c { + th.logger.Info("Caught interrupt, terminating...", "sig", sig) + th.Shutdown(newTestHarnessError(ErrInterrupted, nil, "")) + } + }() + + th.logger.Info("Starting test harness") + accepted := false + var startErr error + for acceptRetries := th.acceptRetries; acceptRetries > 0; acceptRetries-- { + th.logger.Info("Attempting to accept incoming connection", "acceptRetries", acceptRetries) + if err := th.spv.Start(); err != nil { + // if it wasn't a timeout error + if _, ok := err.(timeoutError); !ok { + th.logger.Error("Failed to start listener", "err", err) + th.Shutdown(newTestHarnessError(ErrFailedToStartListener, err, "")) + // we need the return statements in case this is being run + // from a unit test - otherwise this function will just die + // when os.Exit is called + return + } + startErr = err + } else { + accepted = true + break + } + } + if !accepted { + th.logger.Error("Maximum accept retries reached", "acceptRetries", th.acceptRetries) + th.Shutdown(newTestHarnessError(ErrMaxAcceptRetriesReached, startErr, "")) + return + } + + // Run the tests + if err := th.TestPublicKey(); err != nil { + th.Shutdown(err) + return + } + if err := th.TestSignProposal(); err != nil { + th.Shutdown(err) + return + } + if err := th.TestSignVote(); err != nil { + th.Shutdown(err) + return + } + th.logger.Info("SUCCESS! All tests passed.") + th.Shutdown(nil) +} + +// TestPublicKey just validates that we can (1) fetch the public key from the +// remote signer, and (2) it matches the public key we've configured for our +// local Tendermint version. +func (th *TestHarness) TestPublicKey() error { + th.logger.Info("TEST: Public key of remote signer") + th.logger.Info("Local", "pubKey", th.fpv.GetPubKey()) + th.logger.Info("Remote", "pubKey", th.spv.GetPubKey()) + if th.fpv.GetPubKey() != th.spv.GetPubKey() { + th.logger.Error("FAILED: Local and remote public keys do not match") + return newTestHarnessError(ErrTestPublicKeyFailed, nil, "") + } + return nil +} + +// TestSignProposal makes sure the remote signer can successfully sign +// proposals. +func (th *TestHarness) TestSignProposal() error { + th.logger.Info("TEST: Signing of proposals") + // sha256 hash of "hash" + hash := tmhash.Sum([]byte("hash")) + prop := &types.Proposal{ + Type: types.ProposalType, + Height: 12345, + Round: 23456, + POLRound: -1, + BlockID: types.BlockID{ + Hash: hash, + PartsHeader: types.PartSetHeader{ + Hash: hash, + Total: 1000000, + }, + }, + Timestamp: time.Now(), + } + propBytes := prop.SignBytes(th.chainID) + if err := th.spv.SignProposal(th.chainID, prop); err != nil { + th.logger.Error("FAILED: Signing of proposal", "err", err) + return newTestHarnessError(ErrTestSignProposalFailed, err, "") + } + th.logger.Debug("Signed proposal", "prop", prop) + // first check that it's a basically valid proposal + if err := prop.ValidateBasic(); err != nil { + th.logger.Error("FAILED: Signed proposal is invalid", "err", err) + return newTestHarnessError(ErrTestSignProposalFailed, err, "") + } + // now validate the signature on the proposal + if th.spv.GetPubKey().VerifyBytes(propBytes, prop.Signature) { + th.logger.Info("Successfully validated proposal signature") + } else { + th.logger.Error("FAILED: Proposal signature validation failed") + return newTestHarnessError(ErrTestSignProposalFailed, nil, "signature validation failed") + } + return nil +} + +// TestSignVote makes sure the remote signer can successfully sign all kinds of +// votes. +func (th *TestHarness) TestSignVote() error { + th.logger.Info("TEST: Signing of votes") + for _, voteType := range voteTypes { + th.logger.Info("Testing vote type", "type", voteType) + hash := tmhash.Sum([]byte("hash")) + vote := &types.Vote{ + Type: voteType, + Height: 12345, + Round: 23456, + BlockID: types.BlockID{ + Hash: hash, + PartsHeader: types.PartSetHeader{ + Hash: hash, + Total: 1000000, + }, + }, + ValidatorIndex: 0, + ValidatorAddress: tmhash.SumTruncated([]byte("addr")), + Timestamp: time.Now(), + } + voteBytes := vote.SignBytes(th.chainID) + // sign the vote + if err := th.spv.SignVote(th.chainID, vote); err != nil { + th.logger.Error("FAILED: Signing of vote", "err", err) + return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType)) + } + th.logger.Debug("Signed vote", "vote", vote) + // validate the contents of the vote + if err := vote.ValidateBasic(); err != nil { + th.logger.Error("FAILED: Signed vote is invalid", "err", err) + return newTestHarnessError(ErrTestSignVoteFailed, err, fmt.Sprintf("voteType=%d", voteType)) + } + // now validate the signature on the proposal + if th.spv.GetPubKey().VerifyBytes(voteBytes, vote.Signature) { + th.logger.Info("Successfully validated vote signature", "type", voteType) + } else { + th.logger.Error("FAILED: Vote signature validation failed", "type", voteType) + return newTestHarnessError(ErrTestSignVoteFailed, nil, "signature validation failed") + } + } + return nil +} + +// Shutdown will kill the test harness and attempt to close all open sockets +// gracefully. If the supplied error is nil, it is assumed that the exit code +// should be 0. If err is not nil, it will exit with an exit code related to the +// error. +func (th *TestHarness) Shutdown(err error) { + var exitCode int + + if err == nil { + exitCode = NoError + } else if therr, ok := err.(*TestHarnessError); ok { + exitCode = therr.Code + } else { + exitCode = ErrOther + } + th.exitCode = exitCode + + // in case sc.Stop() takes too long + if th.exitWhenComplete { + go func() { + time.Sleep(time.Duration(5) * time.Second) + th.logger.Error("Forcibly exiting program after timeout") + os.Exit(exitCode) + }() + } + + if th.spv.IsRunning() { + if err := th.spv.Stop(); err != nil { + th.logger.Error("Failed to cleanly stop listener: %s", err.Error()) + } + } + + if th.exitWhenComplete { + os.Exit(exitCode) + } +} + +// newTestHarnessSocketVal creates our client instance which we will use for +// testing. +func newTestHarnessSocketVal(logger log.Logger, cfg TestHarnessConfig) (*privval.SocketVal, error) { + proto, addr := cmn.ProtocolAndAddress(cfg.BindAddr) + if proto == "unix" { + // make sure the socket doesn't exist - if so, try to delete it + if cmn.FileExists(addr) { + if err := os.Remove(addr); err != nil { + logger.Error("Failed to remove existing Unix domain socket", "addr", addr) + return nil, err + } + } + } + ln, err := net.Listen(proto, addr) + if err != nil { + return nil, err + } + logger.Info("Listening at", "proto", proto, "addr", addr) + var svln net.Listener + switch proto { + case "unix": + unixLn := privval.NewUnixListener(ln) + privval.UnixListenerAcceptDeadline(cfg.AcceptDeadline)(unixLn) + privval.UnixListenerConnDeadline(cfg.ConnDeadline)(unixLn) + svln = unixLn + case "tcp": + tcpLn := privval.NewTCPListener(ln, cfg.SecretConnKey) + privval.TCPListenerAcceptDeadline(cfg.AcceptDeadline)(tcpLn) + privval.TCPListenerConnDeadline(cfg.ConnDeadline)(tcpLn) + logger.Info("Resolved TCP address for listener", "addr", tcpLn.Addr()) + svln = tcpLn + default: + logger.Error("Unsupported protocol (must be unix:// or tcp://)", "proto", proto) + return nil, newTestHarnessError(ErrInvalidParameters, nil, fmt.Sprintf("Unsupported protocol: %s", proto)) + } + return privval.NewSocketVal(logger, svln), nil +} + +func newTestHarnessError(code int, err error, info string) *TestHarnessError { + return &TestHarnessError{ + Code: code, + Err: err, + Info: info, + } +} + +func (e *TestHarnessError) Error() string { + var msg string + switch e.Code { + case ErrInvalidParameters: + msg = "Invalid parameters supplied to application" + case ErrMaxAcceptRetriesReached: + msg = "Maximum accept retries reached" + case ErrFailedToLoadGenesisFile: + msg = "Failed to load genesis file" + case ErrFailedToCreateListener: + msg = "Failed to create listener" + case ErrFailedToStartListener: + msg = "Failed to start listener" + case ErrInterrupted: + msg = "Interrupted" + case ErrTestPublicKeyFailed: + msg = "Public key validation test failed" + case ErrTestSignProposalFailed: + msg = "Proposal signing validation test failed" + case ErrTestSignVoteFailed: + msg = "Vote signing validation test failed" + default: + msg = "Unknown error" + } + if len(e.Info) > 0 { + msg = fmt.Sprintf("%s: %s", msg, e.Info) + } + if e.Err != nil { + msg = fmt.Sprintf("%s (original error: %s)", msg, e.Err.Error()) + } + return msg +} diff --git a/tools/tm-signer-harness/internal/test_harness_test.go b/tools/tm-signer-harness/internal/test_harness_test.go new file mode 100644 index 000000000..804aca45e --- /dev/null +++ b/tools/tm-signer-harness/internal/test_harness_test.go @@ -0,0 +1,201 @@ +package internal + +import ( + "fmt" + "io/ioutil" + "net" + "os" + "testing" + "time" + + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/types" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/log" +) + +const ( + keyFileContents = `{ + "address": "D08FCA3BA74CF17CBFC15E64F9505302BB0E2748", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "ZCsuTjaczEyon70nmKxwvwu+jqrbq5OH3yQjcK0SFxc=" + }, + "priv_key": { + "type": "tendermint/PrivKeyEd25519", + "value": "8O39AkQsoe1sBQwud/Kdul8lg8K9SFsql9aZvwXQSt1kKy5ONpzMTKifvSeYrHC/C76Oqturk4ffJCNwrRIXFw==" + } +}` + + stateFileContents = `{ + "height": "0", + "round": "0", + "step": 0 +}` + + genesisFileContents = `{ + "genesis_time": "2019-01-15T11:56:34.8963Z", + "chain_id": "test-chain-0XwP5E", + "consensus_params": { + "block_size": { + "max_bytes": "22020096", + "max_gas": "-1" + }, + "evidence": { + "max_age": "100000" + }, + "validator": { + "pub_key_types": [ + "ed25519" + ] + } + }, + "validators": [ + { + "address": "D08FCA3BA74CF17CBFC15E64F9505302BB0E2748", + "pub_key": { + "type": "tendermint/PubKeyEd25519", + "value": "ZCsuTjaczEyon70nmKxwvwu+jqrbq5OH3yQjcK0SFxc=" + }, + "power": "10", + "name": "" + } + ], + "app_hash": "" +}` + + defaultConnDeadline = 100 +) + +func TestRemoteSignerTestHarnessMaxAcceptRetriesReached(t *testing.T) { + cfg := makeConfig(t, 1, 2) + defer cleanup(cfg) + + th, err := NewTestHarness(log.TestingLogger(), cfg) + require.NoError(t, err) + th.Run() + assert.Equal(t, ErrMaxAcceptRetriesReached, th.exitCode) +} + +func TestRemoteSignerTestHarnessSuccessfulRun(t *testing.T) { + harnessTest( + t, + func(th *TestHarness) *privval.RemoteSigner { + return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, false, false) + }, + NoError, + ) +} + +func TestRemoteSignerPublicKeyCheckFailed(t *testing.T) { + harnessTest( + t, + func(th *TestHarness) *privval.RemoteSigner { + return newMockRemoteSigner(t, th, ed25519.GenPrivKey(), false, false) + }, + ErrTestPublicKeyFailed, + ) +} + +func TestRemoteSignerProposalSigningFailed(t *testing.T) { + harnessTest( + t, + func(th *TestHarness) *privval.RemoteSigner { + return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, true, false) + }, + ErrTestSignProposalFailed, + ) +} + +func TestRemoteSignerVoteSigningFailed(t *testing.T) { + harnessTest( + t, + func(th *TestHarness) *privval.RemoteSigner { + return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, false, true) + }, + ErrTestSignVoteFailed, + ) +} + +func newMockRemoteSigner(t *testing.T, th *TestHarness, privKey crypto.PrivKey, breakProposalSigning bool, breakVoteSigning bool) *privval.RemoteSigner { + return privval.NewRemoteSigner( + th.logger, + th.chainID, + types.NewMockPVWithParams(privKey, breakProposalSigning, breakVoteSigning), + privval.DialTCPFn( + th.addr, + time.Duration(defaultConnDeadline)*time.Millisecond, + ed25519.GenPrivKey(), + ), + ) +} + +// For running relatively standard tests. +func harnessTest(t *testing.T, rsMaker func(th *TestHarness) *privval.RemoteSigner, expectedExitCode int) { + cfg := makeConfig(t, 100, 3) + defer cleanup(cfg) + + th, err := NewTestHarness(log.TestingLogger(), cfg) + require.NoError(t, err) + donec := make(chan struct{}) + go func() { + defer close(donec) + th.Run() + }() + + rs := rsMaker(th) + require.NoError(t, rs.Start()) + assert.True(t, rs.IsRunning()) + defer rs.Stop() + + <-donec + assert.Equal(t, expectedExitCode, th.exitCode) +} + +func makeConfig(t *testing.T, acceptDeadline, acceptRetries int) TestHarnessConfig { + return TestHarnessConfig{ + BindAddr: testFreeTCPAddr(t), + KeyFile: makeTempFile("tm-testharness-keyfile", keyFileContents), + StateFile: makeTempFile("tm-testharness-statefile", stateFileContents), + GenesisFile: makeTempFile("tm-testharness-genesisfile", genesisFileContents), + AcceptDeadline: time.Duration(acceptDeadline) * time.Millisecond, + ConnDeadline: time.Duration(defaultConnDeadline) * time.Millisecond, + AcceptRetries: acceptRetries, + SecretConnKey: ed25519.GenPrivKey(), + ExitWhenComplete: false, + } +} + +func cleanup(cfg TestHarnessConfig) { + os.Remove(cfg.KeyFile) + os.Remove(cfg.StateFile) + os.Remove(cfg.GenesisFile) +} + +func makeTempFile(name, content string) string { + tempFile, err := ioutil.TempFile("", fmt.Sprintf("%s-*", name)) + if err != nil { + panic(err) + } + if _, err := tempFile.Write([]byte(content)); err != nil { + tempFile.Close() + panic(err) + } + if err := tempFile.Close(); err != nil { + panic(err) + } + return tempFile.Name() +} + +// testFreeTCPAddr claims a free port so we don't block on listener being ready. +func testFreeTCPAddr(t *testing.T) string { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + defer ln.Close() + + return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port) +} diff --git a/tools/tm-signer-harness/internal/utils.go b/tools/tm-signer-harness/internal/utils.go new file mode 100644 index 000000000..9783ca95b --- /dev/null +++ b/tools/tm-signer-harness/internal/utils.go @@ -0,0 +1,25 @@ +package internal + +import ( + "os/user" + "path/filepath" + "strings" +) + +// ExpandPath will check if the given path begins with a "~" symbol, and if so, +// will expand it to become the user's home directory. If it fails to expand the +// path it will automatically return the original path itself. +func ExpandPath(path string) string { + usr, err := user.Current() + if err != nil { + return path + } + + if path == "~" { + return usr.HomeDir + } else if strings.HasPrefix(path, "~/") { + return filepath.Join(usr.HomeDir, path[2:]) + } + + return path +} diff --git a/tools/tm-signer-harness/main.go b/tools/tm-signer-harness/main.go new file mode 100644 index 000000000..13aaf03a1 --- /dev/null +++ b/tools/tm-signer-harness/main.go @@ -0,0 +1,174 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "time" + + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/privval" + "github.com/tendermint/tendermint/tools/tm-signer-harness/internal" + "github.com/tendermint/tendermint/version" +) + +const ( + defaultAcceptRetries = 100 + defaultBindAddr = "tcp://127.0.0.1:0" + defaultTMHome = "~/.tendermint" + defaultAcceptDeadline = 1 + defaultConnDeadline = 3 + defaultExtractKeyOutput = "./signing.key" +) + +var logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + +// Command line flags +var ( + flagAcceptRetries int + flagBindAddr string + flagTMHome string + flagKeyOutputPath string +) + +// Command line commands +var ( + rootCmd *flag.FlagSet + runCmd *flag.FlagSet + extractKeyCmd *flag.FlagSet + versionCmd *flag.FlagSet +) + +func init() { + rootCmd = flag.NewFlagSet("root", flag.ExitOnError) + rootCmd.Usage = func() { + fmt.Println(`Remote signer test harness for Tendermint. + +Usage: + tm-signer-harness [flags] + +Available Commands: + extract_key Extracts a signing key from a local Tendermint instance + help Help on the available commands + run Runs the test harness + version Display version information and exit + +Use "tm-signer-harness help " for more information about that command.`) + fmt.Println("") + } + + runCmd = flag.NewFlagSet("run", flag.ExitOnError) + runCmd.IntVar(&flagAcceptRetries, "accept-retries", defaultAcceptRetries, "The number of attempts to listen for incoming connections") + runCmd.StringVar(&flagBindAddr, "addr", defaultBindAddr, "Bind to this address for the testing") + runCmd.StringVar(&flagTMHome, "tmhome", defaultTMHome, "Path to the Tendermint home directory") + runCmd.Usage = func() { + fmt.Println(`Runs the remote signer test harness for Tendermint. + +Usage: + tm-signer-harness run [flags] + +Flags:`) + runCmd.PrintDefaults() + fmt.Println("") + } + + extractKeyCmd = flag.NewFlagSet("extract_key", flag.ExitOnError) + extractKeyCmd.StringVar(&flagKeyOutputPath, "output", defaultExtractKeyOutput, "Path to which signing key should be written") + extractKeyCmd.StringVar(&flagTMHome, "tmhome", defaultTMHome, "Path to the Tendermint home directory") + extractKeyCmd.Usage = func() { + fmt.Println(`Extracts a signing key from a local Tendermint instance for use in the remote +signer under test. + +Usage: + tm-signer-harness extract_key [flags] + +Flags:`) + extractKeyCmd.PrintDefaults() + fmt.Println("") + } + + versionCmd = flag.NewFlagSet("version", flag.ExitOnError) + versionCmd.Usage = func() { + fmt.Println(` +Prints the Tendermint version for which this remote signer harness was built. + +Usage: + tm-signer-harness version`) + fmt.Println("") + } +} + +func runTestHarness(acceptRetries int, bindAddr, tmhome string) { + tmhome = internal.ExpandPath(tmhome) + cfg := internal.TestHarnessConfig{ + BindAddr: bindAddr, + KeyFile: filepath.Join(tmhome, "config", "priv_validator_key.json"), + StateFile: filepath.Join(tmhome, "data", "priv_validator_state.json"), + GenesisFile: filepath.Join(tmhome, "config", "genesis.json"), + AcceptDeadline: time.Duration(defaultAcceptDeadline) * time.Second, + AcceptRetries: acceptRetries, + ConnDeadline: time.Duration(defaultConnDeadline) * time.Second, + SecretConnKey: ed25519.GenPrivKey(), + ExitWhenComplete: true, + } + harness, err := internal.NewTestHarness(logger, cfg) + if err != nil { + logger.Error(err.Error()) + if therr, ok := err.(*internal.TestHarnessError); ok { + os.Exit(therr.Code) + } + os.Exit(internal.ErrOther) + } + harness.Run() +} + +func extractKey(tmhome, outputPath string) { + keyFile := filepath.Join(internal.ExpandPath(tmhome), "config", "priv_validator_key.json") + stateFile := filepath.Join(internal.ExpandPath(tmhome), "data", "priv_validator_state.json") + fpv := privval.LoadFilePV(keyFile, stateFile) + pkb := [64]byte(fpv.Key.PrivKey.(ed25519.PrivKeyEd25519)) + if err := ioutil.WriteFile(internal.ExpandPath(outputPath), pkb[:32], 0644); err != nil { + logger.Info("Failed to write private key", "output", outputPath, "err", err) + os.Exit(1) + } + logger.Info("Successfully wrote private key", "output", outputPath) +} + +func main() { + rootCmd.Parse(os.Args[1:]) + if rootCmd.NArg() == 0 || (rootCmd.NArg() == 1 && rootCmd.Arg(0) == "help") { + rootCmd.Usage() + os.Exit(0) + } + + logger = log.NewFilter(logger, log.AllowInfo()) + + switch rootCmd.Arg(0) { + case "help": + switch rootCmd.Arg(1) { + case "run": + runCmd.Usage() + case "extract_key": + extractKeyCmd.Usage() + case "version": + versionCmd.Usage() + default: + fmt.Printf("Unrecognized command: %s\n", rootCmd.Arg(1)) + os.Exit(1) + } + case "run": + runCmd.Parse(os.Args[2:]) + runTestHarness(flagAcceptRetries, flagBindAddr, flagTMHome) + case "extract_key": + extractKeyCmd.Parse(os.Args[2:]) + extractKey(flagTMHome, flagKeyOutputPath) + case "version": + fmt.Println(version.Version) + default: + fmt.Printf("Unrecognized command: %s\n", flag.Arg(0)) + os.Exit(1) + } +} diff --git a/types/priv_validator.go b/types/priv_validator.go index f0a19f401..8acab243a 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -43,11 +43,20 @@ func (pvs PrivValidatorsByAddress) Swap(i, j int) { // MockPV implements PrivValidator without any safety or persistence. // Only use it for testing. type MockPV struct { - privKey crypto.PrivKey + privKey crypto.PrivKey + breakProposalSigning bool + breakVoteSigning bool } func NewMockPV() *MockPV { - return &MockPV{ed25519.GenPrivKey()} + return &MockPV{ed25519.GenPrivKey(), false, false} +} + +// NewMockPVWithParams allows one to create a MockPV instance, but with finer +// grained control over the operation of the mock validator. This is useful for +// mocking test failures. +func NewMockPVWithParams(privKey crypto.PrivKey, breakProposalSigning, breakVoteSigning bool) *MockPV { + return &MockPV{privKey, breakProposalSigning, breakVoteSigning} } // Implements PrivValidator. @@ -57,7 +66,11 @@ func (pv *MockPV) GetPubKey() crypto.PubKey { // Implements PrivValidator. func (pv *MockPV) SignVote(chainID string, vote *Vote) error { - signBytes := vote.SignBytes(chainID) + useChainID := chainID + if pv.breakVoteSigning { + useChainID = "incorrect-chain-id" + } + signBytes := vote.SignBytes(useChainID) sig, err := pv.privKey.Sign(signBytes) if err != nil { return err @@ -68,7 +81,11 @@ func (pv *MockPV) SignVote(chainID string, vote *Vote) error { // Implements PrivValidator. func (pv *MockPV) SignProposal(chainID string, proposal *Proposal) error { - signBytes := proposal.SignBytes(chainID) + useChainID := chainID + if pv.breakProposalSigning { + useChainID = "incorrect-chain-id" + } + signBytes := proposal.SignBytes(useChainID) sig, err := pv.privKey.Sign(signBytes) if err != nil { return err @@ -107,5 +124,5 @@ func (pv *erroringMockPV) SignProposal(chainID string, proposal *Proposal) error // NewErroringMockPV returns a MockPV that fails on each signing request. Again, for testing only. func NewErroringMockPV() *erroringMockPV { - return &erroringMockPV{&MockPV{ed25519.GenPrivKey()}} + return &erroringMockPV{&MockPV{ed25519.GenPrivKey(), false, false}} } From 354a08c25afbd7c3971ffa5ac2dac3d2ef0a8c07 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 7 Feb 2019 06:58:23 -0500 Subject: [PATCH 034/259] p2p: fix infinite loop in addrbook (#3232) * failing test * fix infinite loop in addrbook There are cases where we only have a small number of addresses marked good ("old"), but the selection mechanism keeps trying to select more of these addresses, and hence ends up in an infinite loop. Here we fix this to only try and select such "old" addresses if we have enough of them. Note this means, if we don't have enough of them, we may return more "new" addresses than otherwise expected by the newSelectionBias. This whole GetSelectionWithBias method probably needs to be rewritten, but this is a quick fix for the issue. * changelog * fix infinite loop if not enough new addrs * fix another potential infinite loop if a.nNew == 0 -> pickFromOldBucket=true, but we don't have enough items (a.nOld > len(oldBucketToAddrsMap) false) * Revert "fix another potential infinite loop" This reverts commit 146540c1125597162bd89820d611f6531f5e5e4b. * check num addresses instead of buckets, new test * fixed the int division * add slack to bias % in test, lint fixes * Added checks for selection content in test * test cleanup * Apply suggestions from code review Co-Authored-By: ebuchman * address review comments * change after Anton's review comments * use the same docker image we use for testing when building a binary for localnet * switch back to circleci classic * more review comments * more review comments * refactor addrbook_test * build linux binary inside docker in attempt to fix ``` --> Running dep + make build-linux GOOS=linux GOARCH=amd64 make build make[1]: Entering directory `/home/circleci/.go_workspace/src/github.com/tendermint/tendermint' CGO_ENABLED=0 go build -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" -tags 'tendermint' -o build/tendermint ./cmd/tendermint/ p2p/pex/addrbook.go:373:13: undefined: math.Round ``` * change dir from /usr to /go * use concrete Go version for localnet binary * check for nil addresses just to be sure --- .circleci/config.yml | 4 +- CHANGELOG_PENDING.md | 1 + Makefile | 2 +- p2p/pex/addrbook.go | 27 +++- p2p/pex/addrbook_test.go | 259 +++++++++++++++++++++++++++++++++++---- 5 files changed, 263 insertions(+), 30 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d29680945..d6440de1e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -192,9 +192,7 @@ jobs: name: run localnet and exit on failure command: | set -x - make get_tools - make get_vendor_deps - make build-linux + docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.11.4 make build-linux make localnet-start & ./scripts/localnet-blocks-test.sh 40 5 10 localhost diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2c93469da..2c70ee5aa 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -27,5 +27,6 @@ Special thanks to external contributors on this release: ### BUG FIXES: - [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution +- [p2p] \#3232 Fix infinite loop leading to addrbook deadlock for seed nodes - [p2p] \#3247 Fix panic in SeedMode when calling FlushStop and OnStop concurrently diff --git a/Makefile b/Makefile index 4ebb88b06..a1ba06aa9 100644 --- a/Makefile +++ b/Makefile @@ -269,7 +269,7 @@ build-docker: ### Local testnet using docker # Build linux binary on other platforms -build-linux: +build-linux: get_tools get_vendor_deps GOOS=linux GOARCH=amd64 $(MAKE) build build-docker-localnode: diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index cfeefb343..3cda9ac74 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -369,6 +369,10 @@ func (a *addrBook) GetSelection() []*p2p.NetAddress { return allAddr[:numAddresses] } +func percentageOfNum(p, n int) int { + return int(math.Round((float64(p) / float64(100)) * float64(n))) +} + // GetSelectionWithBias implements AddrBook. // It randomly selects some addresses (old & new). Suitable for peer-exchange protocols. // Must never return a nil address. @@ -408,11 +412,28 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre newBucketToAddrsMap := make(map[int]map[string]struct{}) var newIndex int + // initialize counters used to count old and new added addresses. + // len(oldBucketToAddrsMap) cannot be used as multiple addresses can endup in the same bucket. + var oldAddressesAdded int + var newAddressesAdded int + + // number of new addresses that, if possible, should be in the beginning of the selection + numRequiredNewAdd := percentageOfNum(biasTowardsNewAddrs, numAddresses) + selectionIndex := 0 ADDRS_LOOP: for selectionIndex < numAddresses { - pickFromOldBucket := int((float64(selectionIndex)/float64(numAddresses))*100) >= biasTowardsNewAddrs - pickFromOldBucket = (pickFromOldBucket && a.nOld > 0) || a.nNew == 0 + // biasedTowardsOldAddrs indicates if the selection can switch to old addresses + biasedTowardsOldAddrs := selectionIndex >= numRequiredNewAdd + // An old addresses is selected if: + // - the bias is for old and old addressees are still available or, + // - there are no new addresses or all new addresses have been selected. + // numAddresses <= a.nOld + a.nNew therefore it is guaranteed that there are enough + // addresses to fill the selection + pickFromOldBucket := + (biasedTowardsOldAddrs && oldAddressesAdded < a.nOld) || + a.nNew == 0 || newAddressesAdded >= a.nNew + bucket := make(map[string]*knownAddress) // loop until we pick a random non-empty bucket @@ -450,6 +471,7 @@ ADDRS_LOOP: oldBucketToAddrsMap[oldIndex] = make(map[string]struct{}) } oldBucketToAddrsMap[oldIndex][selectedAddr.String()] = struct{}{} + oldAddressesAdded++ } else { if addrsMap, ok := newBucketToAddrsMap[newIndex]; ok { if _, ok = addrsMap[selectedAddr.String()]; ok { @@ -459,6 +481,7 @@ ADDRS_LOOP: newBucketToAddrsMap[newIndex] = make(map[string]struct{}) } newBucketToAddrsMap[newIndex][selectedAddr.String()] = struct{}{} + newAddressesAdded++ } selection[selectionIndex] = selectedAddr diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index ade02d490..bac418ff9 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -4,38 +4,18 @@ import ( "encoding/hex" "fmt" "io/ioutil" + "math" "os" "testing" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" ) -func createTempFileName(prefix string) string { - f, err := ioutil.TempFile("", prefix) - if err != nil { - panic(err) - } - fname := f.Name() - err = f.Close() - if err != nil { - panic(err) - } - return fname -} - -func deleteTempFile(fname string) { - err := os.Remove(fname) - if err != nil { - panic(err) - } -} - func TestAddrBookPickAddress(t *testing.T) { fname := createTempFileName("addrbook_test") defer deleteTempFile(fname) @@ -239,6 +219,34 @@ func TestAddrBookRemoveAddress(t *testing.T) { assert.Equal(t, 0, book.Size()) } +func TestAddrBookGetSelectionWithOneMarkedGood(t *testing.T) { + // create a book with 10 addresses, 1 good/old and 9 new + book, fname := createAddrBookWithMOldAndNNewAddrs(t, 1, 9) + defer deleteTempFile(fname) + + addrs := book.GetSelectionWithBias(biasToSelectNewPeers) + assert.NotNil(t, addrs) + assertMOldAndNNewAddrsInSelection(t, 1, 9, addrs, book) +} + +func TestAddrBookGetSelectionWithOneNotMarkedGood(t *testing.T) { + // create a book with 10 addresses, 9 good/old and 1 new + book, fname := createAddrBookWithMOldAndNNewAddrs(t, 9, 1) + defer deleteTempFile(fname) + + addrs := book.GetSelectionWithBias(biasToSelectNewPeers) + assert.NotNil(t, addrs) + assertMOldAndNNewAddrsInSelection(t, 9, 1, addrs, book) +} + +func TestAddrBookGetSelectionReturnsNilWhenAddrBookIsEmpty(t *testing.T) { + book, fname := createAddrBookWithMOldAndNNewAddrs(t, 0, 0) + defer deleteTempFile(fname) + + addrs := book.GetSelectionWithBias(biasToSelectNewPeers) + assert.Nil(t, addrs) +} + func TestAddrBookGetSelection(t *testing.T) { fname := createTempFileName("addrbook_test") defer deleteTempFile(fname) @@ -335,9 +343,16 @@ func TestAddrBookGetSelectionWithBias(t *testing.T) { good++ } } + got, expected := int((float64(good)/float64(len(selection)))*100), (100 - biasTowardsNewAddrs) - if got >= expected { - t.Fatalf("expected more good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)", got, expected, good, len(selection)) + + // compute some slack to protect against small differences due to rounding: + slack := int(math.Round(float64(100) / float64(len(selection)))) + if got > expected+slack { + t.Fatalf("got more good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)", got, expected, good, len(selection)) + } + if got < expected-slack { + t.Fatalf("got fewer good peers (%% got: %d, %% expected: %d, number of good addrs: %d, total: %d)", got, expected, good, len(selection)) } } @@ -417,3 +432,199 @@ func TestPrivatePeers(t *testing.T) { assert.True(t, ok) } } + +func testAddrBookAddressSelection(t *testing.T, bookSize int) { + // generate all combinations of old (m) and new addresses + for nOld := 0; nOld <= bookSize; nOld++ { + nNew := bookSize - nOld + dbgStr := fmt.Sprintf("book of size %d (new %d, old %d)", bookSize, nNew, nOld) + + // create book and get selection + book, fname := createAddrBookWithMOldAndNNewAddrs(t, nOld, nNew) + defer deleteTempFile(fname) + addrs := book.GetSelectionWithBias(biasToSelectNewPeers) + assert.NotNil(t, addrs, "%s - expected a non-nil selection", dbgStr) + nAddrs := len(addrs) + assert.NotZero(t, nAddrs, "%s - expected at least one address in selection", dbgStr) + + // check there's no nil addresses + for _, addr := range addrs { + if addr == nil { + t.Fatalf("%s - got nil address in selection %v", dbgStr, addrs) + } + } + + // XXX: shadowing + nOld, nNew := countOldAndNewAddrsInSelection(addrs, book) + + // Given: + // n - num new addrs, m - num old addrs + // k - num new addrs expected in the beginning (based on bias %) + // i=min(n, k), aka expFirstNew + // j=min(m, r-i), aka expOld + // + // We expect this layout: + // indices: 0...i-1 i...i+j-1 i+j...r + // addresses: N0..Ni-1 O0..Oj-1 Ni... + // + // There is at least one partition and at most three. + var ( + k = percentageOfNum(biasToSelectNewPeers, nAddrs) + expFirstNew = cmn.MinInt(nNew, k) + expOld = cmn.MinInt(nOld, nAddrs-expFirstNew) + expNew = nAddrs - expOld + expLastNew = expNew - expFirstNew + ) + + // Verify that the number of old and new addresses are as expected + if nNew < expNew || nNew > expNew { + t.Fatalf("%s - expected new addrs %d, got %d", dbgStr, expNew, nNew) + } + if nOld < expOld || nOld > expOld { + t.Fatalf("%s - expected old addrs %d, got %d", dbgStr, expOld, nOld) + } + + // Verify that the order of addresses is as expected + // Get the sequence types and lengths of the selection + seqLens, seqTypes, err := analyseSelectionLayout(book, addrs) + assert.NoError(t, err, "%s", dbgStr) + + // Build a list with the expected lengths of partitions and another with the expected types, e.g.: + // expSeqLens = [10, 22], expSeqTypes = [1, 2] + // means we expect 10 new (type 1) addresses followed by 22 old (type 2) addresses. + var expSeqLens []int + var expSeqTypes []int + + switch { + case expOld == 0: // all new addresses + expSeqLens = []int{nAddrs} + expSeqTypes = []int{1} + case expFirstNew == 0: // all old addresses + expSeqLens = []int{nAddrs} + expSeqTypes = []int{2} + case nAddrs-expFirstNew-expOld == 0: // new addresses, old addresses + expSeqLens = []int{expFirstNew, expOld} + expSeqTypes = []int{1, 2} + default: // new addresses, old addresses, new addresses + expSeqLens = []int{expFirstNew, expOld, expLastNew} + expSeqTypes = []int{1, 2, 1} + } + + assert.Equal(t, expSeqLens, seqLens, + "%s - expected sequence lengths of old/new %v, got %v", + dbgStr, expSeqLens, seqLens) + assert.Equal(t, expSeqTypes, seqTypes, + "%s - expected sequence types (1-new, 2-old) was %v, got %v", + dbgStr, expSeqTypes, seqTypes) + } +} + +func TestMultipleAddrBookAddressSelection(t *testing.T) { + // test books with smaller size, < N + const N = 32 + for bookSize := 1; bookSize < N; bookSize++ { + testAddrBookAddressSelection(t, bookSize) + } + + // Test for two books with sizes from following ranges + ranges := [...][]int{{33, 100}, {100, 175}} + var bookSizes []int + for _, r := range ranges { + bookSizes = append(bookSizes, cmn.RandIntn(r[1]-r[0])+r[0]) + } + t.Logf("Testing address selection for the following book sizes %v\n", bookSizes) + for _, bookSize := range bookSizes { + testAddrBookAddressSelection(t, bookSize) + } +} + +func assertMOldAndNNewAddrsInSelection(t *testing.T, m, n int, addrs []*p2p.NetAddress, book *addrBook) { + nOld, nNew := countOldAndNewAddrsInSelection(addrs, book) + assert.Equal(t, m, nOld, "old addresses") + assert.Equal(t, n, nNew, "new addresses") +} + +func createTempFileName(prefix string) string { + f, err := ioutil.TempFile("", prefix) + if err != nil { + panic(err) + } + fname := f.Name() + err = f.Close() + if err != nil { + panic(err) + } + return fname +} + +func deleteTempFile(fname string) { + err := os.Remove(fname) + if err != nil { + panic(err) + } +} + +func createAddrBookWithMOldAndNNewAddrs(t *testing.T, nOld, nNew int) (book *addrBook, fname string) { + fname = createTempFileName("addrbook_test") + + book = NewAddrBook(fname, true) + book.SetLogger(log.TestingLogger()) + assert.Zero(t, book.Size()) + + randAddrs := randNetAddressPairs(t, nOld) + for _, addr := range randAddrs { + book.AddAddress(addr.addr, addr.src) + book.MarkGood(addr.addr) + } + + randAddrs = randNetAddressPairs(t, nNew) + for _, addr := range randAddrs { + book.AddAddress(addr.addr, addr.src) + } + + return +} + +func countOldAndNewAddrsInSelection(addrs []*p2p.NetAddress, book *addrBook) (nOld, nNew int) { + for _, addr := range addrs { + if book.IsGood(addr) { + nOld++ + } else { + nNew++ + } + } + return +} + +// Analyse the layout of the selection specified by 'addrs' +// Returns: +// - seqLens - the lengths of the sequences of addresses of same type +// - seqTypes - the types of sequences in selection +func analyseSelectionLayout(book *addrBook, addrs []*p2p.NetAddress) (seqLens, seqTypes []int, err error) { + // address types are: 0 - nil, 1 - new, 2 - old + var ( + prevType = 0 + currentSeqLen = 0 + ) + + for _, addr := range addrs { + addrType := 0 + if book.IsGood(addr) { + addrType = 2 + } else { + addrType = 1 + } + if addrType != prevType && prevType != 0 { + seqLens = append(seqLens, currentSeqLen) + seqTypes = append(seqTypes, prevType) + currentSeqLen = 0 + } + currentSeqLen++ + prevType = addrType + } + + seqLens = append(seqLens, currentSeqLen) + seqTypes = append(seqTypes, prevType) + + return +} From 11e36d0bfb6e62bb3b758aa4a4cdf90fa2e89fdb Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 7 Feb 2019 17:16:31 +0400 Subject: [PATCH 035/259] addrbook_test: preallocate memory for bookSizes (#3268) Fixes https://circleci.com/gh/tendermint/tendermint/44901 --- p2p/pex/addrbook_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index bac418ff9..9effa5d0e 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -528,7 +528,7 @@ func TestMultipleAddrBookAddressSelection(t *testing.T) { // Test for two books with sizes from following ranges ranges := [...][]int{{33, 100}, {100, 175}} - var bookSizes []int + bookSizes := make([]int, 0, len(ranges)) for _, r := range ranges { bookSizes = append(bookSizes, cmn.RandIntn(r[1]-r[0])+r[0]) } From f571ee8876d56d3b80a96019ba326fe8932ef116 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 7 Feb 2019 19:34:01 -0500 Subject: [PATCH 036/259] prepare v0.29.2 (#3272) * update changelog * linkify * bump version * update main changelog * final fixes * entry for wal fix * changelog preamble * remove a line --- CHANGELOG.md | 41 +++++++++++++++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 23 ++--------------------- version/version.go | 2 +- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 437b7970d..779cb886f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,46 @@ # Changelog +## v0.29.2 + +*February 7th, 2019* + +Special thanks to external contributors on this release: +@ackratos, @rickyyangz + +**Note**: This release contains security sensitive patches in the `p2p` and +`crypto` packages: +- p2p: + - Partial fix for MITM attacks on the p2p connection. MITM conditions may + still exist. See \#3010. +- crypto: + - Eliminate our fork of `btcd` and use the `btcd/btcec` library directly for + native secp256k1 signing. Note we still modify the signature encoding to + prevent malleability. + - Support the libsecp256k1 library via CGo through the `go-ethereum/crypto/secp256k1` package. + +### BREAKING CHANGES: + +* Go API + - [types] [\#3245](https://github.com/tendermint/tendermint/issues/3245) Commit uses `type CommitSig Vote` instead of `Vote` directly. + In preparation for removing redundant fields from the commit [\#1648](https://github.com/tendermint/tendermint/issues/1648) + +### IMPROVEMENTS: +- [consensus] [\#3246](https://github.com/tendermint/tendermint/issues/3246) Better logging and notes on recovery for corrupted WAL file +- [crypto] [\#3163](https://github.com/tendermint/tendermint/issues/3163) Use ethereum's libsecp256k1 go-wrapper for signatures when cgo is available +- [crypto] [\#3162](https://github.com/tendermint/tendermint/issues/3162) Wrap btcd instead of forking it to keep up with fixes (used if cgo is not available) +- [makefile] [\#3233](https://github.com/tendermint/tendermint/issues/3233) Use golangci-lint instead of go-metalinter +- [tools] [\#3218](https://github.com/tendermint/tendermint/issues/3218) Add go-deadlock tool to help detect deadlocks +- [tools] [\#3106](https://github.com/tendermint/tendermint/issues/3106) Add tm-signer-harness test harness for remote signers +- [tests] [\#3258](https://github.com/tendermint/tendermint/issues/3258) Fixed a bunch of non-deterministic test failures + +### BUG FIXES: +- [node] [\#3186](https://github.com/tendermint/tendermint/issues/3186) EventBus and indexerService should be started before first block (for replay last block on handshake) execution (@ackratos) +- [p2p] [\#3232](https://github.com/tendermint/tendermint/issues/3232) Fix infinite loop leading to addrbook deadlock for seed nodes +- [p2p] [\#3247](https://github.com/tendermint/tendermint/issues/3247) Fix panic in SeedMode when calling FlushStop and OnStop + concurrently +- [p2p] [\#3040](https://github.com/tendermint/tendermint/issues/3040) Fix MITM on secret connection by checking low-order points +- [privval] [\#3258](https://github.com/tendermint/tendermint/issues/3258) Fix race between sign requests and ping requests in socket + ## v0.29.1 *January 24, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2c70ee5aa..4548eb1eb 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,32 +1,13 @@ -## v0.30.0 +## v0.30 -*TBD* +** Special thanks to external contributors on this release: ### BREAKING CHANGES: -* CLI/RPC/Config - -* Apps - -* Go API - - [types] \#3245 Commit uses `type CommitSig Vote` instead of `Vote` directly. - -* Blockchain Protocol - -* P2P Protocol - ### FEATURES: ### IMPROVEMENTS: -- [tools] Add go-deadlock tool to help detect deadlocks -- [tools] \#3106 Add tm-signer-harness test harness for remote signers -- [crypto] \#3163 Use ethereum's libsecp256k1 go-wrapper for signatures when cgo is available -- [crypto] \#3162 Wrap btcd instead of forking it to keep up with fixes (used if cgo is not available) ### BUG FIXES: -- [node] \#3186 EventBus and indexerService should be started before first block (for replay last block on handshake) execution -- [p2p] \#3232 Fix infinite loop leading to addrbook deadlock for seed nodes -- [p2p] \#3247 Fix panic in SeedMode when calling FlushStop and OnStop - concurrently diff --git a/version/version.go b/version/version.go index 86c38c033..b20223c24 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.29.1" + TMCoreSemVer = "0.29.2" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From ad4bd92fec3bcba8715935c2c42eb27f68f5109a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 7 Feb 2019 19:57:30 -0500 Subject: [PATCH 037/259] secp256k1: change build tags (#3277) --- crypto/secp256k1/secp256k1_cgo.go | 2 +- crypto/secp256k1/secp256k1_nocgo.go | 2 +- crypto/secp256k1/secp256k1_nocgo_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crypto/secp256k1/secp256k1_cgo.go b/crypto/secp256k1/secp256k1_cgo.go index 30414d2b7..3e5b1ddd2 100644 --- a/crypto/secp256k1/secp256k1_cgo.go +++ b/crypto/secp256k1/secp256k1_cgo.go @@ -1,4 +1,4 @@ -// +build cgo +// +build libsecp256k1 package secp256k1 diff --git a/crypto/secp256k1/secp256k1_nocgo.go b/crypto/secp256k1/secp256k1_nocgo.go index 34b006faa..052c3d14d 100644 --- a/crypto/secp256k1/secp256k1_nocgo.go +++ b/crypto/secp256k1/secp256k1_nocgo.go @@ -1,4 +1,4 @@ -// +build !cgo +// +build !libsecp256k1 package secp256k1 diff --git a/crypto/secp256k1/secp256k1_nocgo_test.go b/crypto/secp256k1/secp256k1_nocgo_test.go index 95966478b..a06a0e3d1 100644 --- a/crypto/secp256k1/secp256k1_nocgo_test.go +++ b/crypto/secp256k1/secp256k1_nocgo_test.go @@ -1,4 +1,4 @@ -// +build !cgo +// +build !libsecp256k1 package secp256k1 From af6e6cd350541147aed50c4e936f6ba596231027 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Thu, 7 Feb 2019 20:12:57 -0500 Subject: [PATCH 038/259] remove MixEntropy (#3278) * remove MixEntropy * changelog --- CHANGELOG.md | 3 + README.md | 1 + crypto/random.go | 104 --------------------- crypto/xsalsa20symmetric/symmetric.go | 1 - crypto/xsalsa20symmetric/symmetric_test.go | 4 - 5 files changed, 4 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 779cb886f..a0e736bcf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,10 +17,13 @@ Special thanks to external contributors on this release: native secp256k1 signing. Note we still modify the signature encoding to prevent malleability. - Support the libsecp256k1 library via CGo through the `go-ethereum/crypto/secp256k1` package. + - Eliminate MixEntropy functions ### BREAKING CHANGES: * Go API + - [crypto] [\#3278](https://github.com/tendermint/tendermint/issues/3278) Remove + MixEntropy functions - [types] [\#3245](https://github.com/tendermint/tendermint/issues/3245) Commit uses `type CommitSig Vote` instead of `Vote` directly. In preparation for removing redundant fields from the commit [\#1648](https://github.com/tendermint/tendermint/issues/1648) diff --git a/README.md b/README.md index 601e3830e..9251e3ca5 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,7 @@ include the in-process Go APIs. That said, breaking changes in the following packages will be documented in the CHANGELOG even if they don't lead to MINOR version bumps: +- crypto - types - rpc/client - config diff --git a/crypto/random.go b/crypto/random.go index 914c321b7..275fb1044 100644 --- a/crypto/random.go +++ b/crypto/random.go @@ -1,42 +1,11 @@ package crypto import ( - "crypto/cipher" crand "crypto/rand" - "crypto/sha256" "encoding/hex" "io" - "sync" - - "golang.org/x/crypto/chacha20poly1305" ) -// NOTE: This is ignored for now until we have time -// to properly review the MixEntropy function - https://github.com/tendermint/tendermint/issues/2099. -// -// The randomness here is derived from xoring a chacha20 keystream with -// output from crypto/rand's OS Entropy Reader. (Due to fears of the OS' -// entropy being backdoored) -// -// For forward secrecy of produced randomness, the internal chacha key is hashed -// and thereby rotated after each call. -var gRandInfo *randInfo - -func init() { - gRandInfo = &randInfo{} - - // TODO: uncomment after reviewing MixEntropy - - // https://github.com/tendermint/tendermint/issues/2099 - // gRandInfo.MixEntropy(randBytes(32)) // Init -} - -// WARNING: This function needs review - https://github.com/tendermint/tendermint/issues/2099. -// Mix additional bytes of randomness, e.g. from hardware, user-input, etc. -// It is OK to call it multiple times. It does not diminish security. -func MixEntropy(seedBytes []byte) { - gRandInfo.MixEntropy(seedBytes) -} - // This only uses the OS's randomness func randBytes(numBytes int) []byte { b := make([]byte, numBytes) @@ -52,19 +21,6 @@ func CRandBytes(numBytes int) []byte { return randBytes(numBytes) } -/* TODO: uncomment after reviewing MixEntropy - https://github.com/tendermint/tendermint/issues/2099 -// This uses the OS and the Seed(s). -func CRandBytes(numBytes int) []byte { - return randBytes(numBytes) - b := make([]byte, numBytes) - _, err := gRandInfo.Read(b) - if err != nil { - panic(err) - } - return b -} -*/ - // CRandHex returns a hex encoded string that's floor(numDigits/2) * 2 long. // // Note: CRandHex(24) gives 96 bits of randomness that @@ -77,63 +33,3 @@ func CRandHex(numDigits int) string { func CReader() io.Reader { return crand.Reader } - -/* TODO: uncomment after reviewing MixEntropy - https://github.com/tendermint/tendermint/issues/2099 -// Returns a crand.Reader mixed with user-supplied entropy -func CReader() io.Reader { - return gRandInfo -} -*/ - -//-------------------------------------------------------------------------------- - -type randInfo struct { - mtx sync.Mutex - seedBytes [chacha20poly1305.KeySize]byte - chacha cipher.AEAD - reader io.Reader -} - -// You can call this as many times as you'd like. -// XXX/TODO: review - https://github.com/tendermint/tendermint/issues/2099 -func (ri *randInfo) MixEntropy(seedBytes []byte) { - ri.mtx.Lock() - defer ri.mtx.Unlock() - // Make new ri.seedBytes using passed seedBytes and current ri.seedBytes: - // ri.seedBytes = sha256( seedBytes || ri.seedBytes ) - h := sha256.New() - h.Write(seedBytes) - h.Write(ri.seedBytes[:]) - hashBytes := h.Sum(nil) - copy(ri.seedBytes[:], hashBytes) - chacha, err := chacha20poly1305.New(ri.seedBytes[:]) - if err != nil { - panic("Initializing chacha20 failed") - } - ri.chacha = chacha - // Create new reader - ri.reader = &cipher.StreamReader{S: ri, R: crand.Reader} -} - -func (ri *randInfo) XORKeyStream(dst, src []byte) { - // nonce being 0 is safe due to never re-using a key. - emptyNonce := make([]byte, 12) - tmpDst := ri.chacha.Seal([]byte{}, emptyNonce, src, []byte{0}) - // this removes the poly1305 tag as well, since chacha is a stream cipher - // and we truncate at input length. - copy(dst, tmpDst[:len(src)]) - // hash seedBytes for forward secrecy, and initialize new chacha instance - newSeed := sha256.Sum256(ri.seedBytes[:]) - chacha, err := chacha20poly1305.New(newSeed[:]) - if err != nil { - panic("Initializing chacha20 failed") - } - ri.chacha = chacha -} - -func (ri *randInfo) Read(b []byte) (n int, err error) { - ri.mtx.Lock() - n, err = ri.reader.Read(b) - ri.mtx.Unlock() - return -} diff --git a/crypto/xsalsa20symmetric/symmetric.go b/crypto/xsalsa20symmetric/symmetric.go index 3228a935f..10a0f6f33 100644 --- a/crypto/xsalsa20symmetric/symmetric.go +++ b/crypto/xsalsa20symmetric/symmetric.go @@ -17,7 +17,6 @@ const secretLen = 32 // secret must be 32 bytes long. Use something like Sha256(Bcrypt(passphrase)) // The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. -// NOTE: call crypto.MixEntropy() first. func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { if len(secret) != secretLen { cmn.PanicSanity(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) diff --git a/crypto/xsalsa20symmetric/symmetric_test.go b/crypto/xsalsa20symmetric/symmetric_test.go index bca0b336c..160d49a9e 100644 --- a/crypto/xsalsa20symmetric/symmetric_test.go +++ b/crypto/xsalsa20symmetric/symmetric_test.go @@ -13,8 +13,6 @@ import ( func TestSimple(t *testing.T) { - crypto.MixEntropy([]byte("someentropy")) - plaintext := []byte("sometext") secret := []byte("somesecretoflengththirtytwo===32") ciphertext := EncryptSymmetric(plaintext, secret) @@ -26,8 +24,6 @@ func TestSimple(t *testing.T) { func TestSimpleWithKDF(t *testing.T) { - crypto.MixEntropy([]byte("someentropy")) - plaintext := []byte("sometext") secretPass := []byte("somesecret") secret, err := bcrypt.GenerateFromPassword(secretPass, 12) From c1f7399a86f61bcf26791b9e0a6cf30b28e2048c Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Fri, 8 Feb 2019 15:48:09 +0100 Subject: [PATCH 039/259] review comment: cleaner constant for N/2, delete secp256k1N and use (#3279) `secp256k1.S256().N` directly instead --- crypto/secp256k1/secp256k1_nocgo.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crypto/secp256k1/secp256k1_nocgo.go b/crypto/secp256k1/secp256k1_nocgo.go index 052c3d14d..cd1655a5c 100644 --- a/crypto/secp256k1/secp256k1_nocgo.go +++ b/crypto/secp256k1/secp256k1_nocgo.go @@ -14,8 +14,7 @@ import ( // see: // - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/signature_nocgo.go#L90-L93 // - https://github.com/ethereum/go-ethereum/blob/f9401ae011ddf7f8d2d95020b7446c17f8d98dc1/crypto/crypto.go#L39 -var secp256k1N, _ = new(big.Int).SetString("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16) -var secp256k1halfN = new(big.Int).Div(secp256k1N, big.NewInt(2)) +var secp256k1halfN = new(big.Int).Rsh(secp256k1.S256().N, 1) // Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. // The returned signature will be of the form R || S (in lower-S form). From cce4d21ccbaa94163b393dd4dc1fd7c202d4feb7 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Fri, 8 Feb 2019 19:05:09 +0100 Subject: [PATCH 040/259] treat validator updates as set (#3222) * Initial commit for 3181..still early * unit test updates * unit test updates * fix check of dups accross updates and deletes * simplify the processChange() func * added overflow check utest * Added checks for empty valset, new utest * deepcopy changes in processUpdate() * moved to new API, fixed tests * test cleanup * address review comments * make sure votePower > 0 * gofmt fixes * handle duplicates and invalid values * more work on tests, review comments * Renamed and explained K * make TestVal private * split verifyUpdatesAndComputeNewPriorities.., added check for deletes * return error if validator set is empty after processing changes * address review comments * lint err * Fixed the total voting power and added comments * fix lint * fix lint --- state/execution.go | 79 +------- state/execution_test.go | 2 +- state/state_test.go | 137 ++++++++----- types/validator.go | 11 ++ types/validator_set.go | 363 +++++++++++++++++++++++++++------- types/validator_set_test.go | 382 ++++++++++++++++++++++++++++++++++-- 6 files changed, 756 insertions(+), 218 deletions(-) diff --git a/state/execution.go b/state/execution.go index 85477eebf..470e22bc0 100644 --- a/state/execution.go +++ b/state/execution.go @@ -2,7 +2,6 @@ package state import ( "fmt" - "strings" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -143,7 +142,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b return state, err } if len(validatorUpdates) > 0 { - blockExec.logger.Info("Updates to validators", "updates", makeValidatorUpdatesLogString(validatorUpdates)) + blockExec.logger.Info("Updates to validators", "updates", types.ValidatorListString(validatorUpdates)) } // Update the state with the block and responses. @@ -368,70 +367,6 @@ func validateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, return nil } -// If more or equal than 1/3 of total voting power changed in one block, then -// a light client could never prove the transition externally. See -// ./lite/doc.go for details on how a light client tracks validators. -func updateValidators(currentSet *types.ValidatorSet, updates []*types.Validator) error { - for _, valUpdate := range updates { - // should already have been checked - if valUpdate.VotingPower < 0 { - return fmt.Errorf("Voting power can't be negative %v", valUpdate) - } - - address := valUpdate.Address - _, val := currentSet.GetByAddress(address) - // valUpdate.VotingPower is ensured to be non-negative in validation method - if valUpdate.VotingPower == 0 { // remove val - _, removed := currentSet.Remove(address) - if !removed { - return fmt.Errorf("Failed to remove validator %X", address) - } - } else if val == nil { // add val - // make sure we do not exceed MaxTotalVotingPower by adding this validator: - totalVotingPower := currentSet.TotalVotingPower() - updatedVotingPower := valUpdate.VotingPower + totalVotingPower - overflow := updatedVotingPower > types.MaxTotalVotingPower || updatedVotingPower < 0 - if overflow { - return fmt.Errorf( - "Failed to add new validator %v. Adding it would exceed max allowed total voting power %v", - valUpdate, - types.MaxTotalVotingPower) - } - // TODO: issue #1558 update spec according to the following: - // Set ProposerPriority to -C*totalVotingPower (with C ~= 1.125) to make sure validators can't - // unbond/rebond to reset their (potentially previously negative) ProposerPriority to zero. - // - // Contract: totalVotingPower < MaxTotalVotingPower to ensure ProposerPriority does - // not exceed the bounds of int64. - // - // Compute ProposerPriority = -1.125*totalVotingPower == -(totalVotingPower + (totalVotingPower >> 3)). - valUpdate.ProposerPriority = -(totalVotingPower + (totalVotingPower >> 3)) - added := currentSet.Add(valUpdate) - if !added { - return fmt.Errorf("Failed to add new validator %v", valUpdate) - } - } else { // update val - // make sure we do not exceed MaxTotalVotingPower by updating this validator: - totalVotingPower := currentSet.TotalVotingPower() - curVotingPower := val.VotingPower - updatedVotingPower := totalVotingPower - curVotingPower + valUpdate.VotingPower - overflow := updatedVotingPower > types.MaxTotalVotingPower || updatedVotingPower < 0 - if overflow { - return fmt.Errorf( - "Failed to update existing validator %v. Updating it would exceed max allowed total voting power %v", - valUpdate, - types.MaxTotalVotingPower) - } - - updated := currentSet.Update(valUpdate) - if !updated { - return fmt.Errorf("Failed to update validator %X to %v", address, valUpdate) - } - } - } - return nil -} - // updateState returns a new State updated according to the header and responses. func updateState( state State, @@ -448,7 +383,7 @@ func updateState( // Update the validator set with the latest abciResponses. lastHeightValsChanged := state.LastHeightValidatorsChanged if len(validatorUpdates) > 0 { - err := updateValidators(nValSet, validatorUpdates) + err := nValSet.UpdateWithChangeSet(validatorUpdates) if err != nil { return state, fmt.Errorf("Error changing validator set: %v", err) } @@ -552,13 +487,3 @@ func ExecCommitBlock( // ResponseCommit has no error or log, just data return res.Data, nil } - -// Make pretty string for validatorUpdates logging -func makeValidatorUpdatesLogString(vals []*types.Validator) string { - chunks := make([]string, len(vals)) - for i, val := range vals { - chunks[i] = fmt.Sprintf("%s:%d", val.Address, val.VotingPower) - } - - return strings.Join(chunks, ",") -} diff --git a/state/execution_test.go b/state/execution_test.go index 041fb558e..e0c9b4b9d 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -280,7 +280,7 @@ func TestUpdateValidators(t *testing.T) { t.Run(tc.name, func(t *testing.T) { updates, err := types.PB2TM.ValidatorUpdates(tc.abciUpdates) assert.NoError(t, err) - err = updateValidators(tc.currentSet, updates) + err = tc.currentSet.UpdateWithChangeSet(updates) if tc.shouldErr { assert.Error(t, err) } else { diff --git a/state/state_test.go b/state/state_test.go index 904d7a10b..9cbe83424 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -322,7 +322,8 @@ func TestProposerFrequency(t *testing.T) { vals := make([]*types.Validator, N) totalVotePower := int64(0) for j := 0; j < N; j++ { - votePower := int64(cmn.RandInt() % maxPower) + // make sure votePower > 0 + votePower := int64(cmn.RandInt()%maxPower) + 1 totalVotePower += votePower privVal := types.NewMockPV() pubKey := privVal.GetPubKey() @@ -424,49 +425,71 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { require.Equal(t, len(updatedState2.NextValidators.Validators), 2) _, updatedVal1 := updatedState2.NextValidators.GetByAddress(val1PubKey.Address()) _, addedVal2 := updatedState2.NextValidators.GetByAddress(val2PubKey.Address()) + // adding a validator should not lead to a ProposerPriority equal to zero (unless the combination of averaging and // incrementing would cause so; which is not the case here) - totalPowerBefore2 := curTotal - // while adding we compute prio == -1.125 * total: - wantVal2ProposerPrio := -(totalPowerBefore2 + (totalPowerBefore2 >> 3)) - wantVal2ProposerPrio = wantVal2ProposerPrio + val2VotingPower - // then increment: + // Steps from adding new validator: + // 0 - val1 prio is 0, TVP after add: + wantVal1Prio := int64(0) totalPowerAfter := val1VotingPower + val2VotingPower - // mostest: - wantVal2ProposerPrio = wantVal2ProposerPrio - totalPowerAfter - avg := big.NewInt(0).Add(big.NewInt(val1VotingPower), big.NewInt(wantVal2ProposerPrio)) + // 1. Add - Val2 should be initially added with (-123) => + wantVal2Prio := -(totalPowerAfter + (totalPowerAfter >> 3)) + // 2. Scale - noop + // 3. Center - with avg, resulting val2:-61, val1:62 + avg := big.NewInt(0).Add(big.NewInt(wantVal1Prio), big.NewInt(wantVal2Prio)) avg.Div(avg, big.NewInt(2)) - wantVal2ProposerPrio = wantVal2ProposerPrio - avg.Int64() - wantVal1Prio := 0 + val1VotingPower - avg.Int64() + wantVal2Prio = wantVal2Prio - avg.Int64() // -61 + wantVal1Prio = wantVal1Prio - avg.Int64() // 62 + + // 4. Steps from IncrementProposerPriority + wantVal1Prio = wantVal1Prio + val1VotingPower // 72 + wantVal2Prio = wantVal2Prio + val2VotingPower // 39 + wantVal1Prio = wantVal1Prio - totalPowerAfter // -38 as val1 is proposer + assert.Equal(t, wantVal1Prio, updatedVal1.ProposerPriority) - assert.Equal(t, wantVal2ProposerPrio, addedVal2.ProposerPriority) + assert.Equal(t, wantVal2Prio, addedVal2.ProposerPriority) // Updating a validator does not reset the ProposerPriority to zero: + // 1. Add - Val2 VotingPower change to 1 => updatedVotingPowVal2 := int64(1) updateVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(val2PubKey), Power: updatedVotingPowVal2} validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateVal}) assert.NoError(t, err) - // this will cause the diff of priorities (31) + // this will cause the diff of priorities (77) // to be larger than threshold == 2*totalVotingPower (22): updatedState3, err := updateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState3.NextValidators.Validators), 2) _, prevVal1 := updatedState3.Validators.GetByAddress(val1PubKey.Address()) + _, prevVal2 := updatedState3.Validators.GetByAddress(val2PubKey.Address()) + _, updatedVal1 = updatedState3.NextValidators.GetByAddress(val1PubKey.Address()) _, updatedVal2 := updatedState3.NextValidators.GetByAddress(val2PubKey.Address()) - // divide previous priorities by 2 == CEIL(31/22) as diff > threshold: - expectedVal1PrioBeforeAvg := prevVal1.ProposerPriority/2 + prevVal1.VotingPower - wantVal2ProposerPrio = wantVal2ProposerPrio/2 + updatedVotingPowVal2 - // val1 will be proposer: - total := val1VotingPower + updatedVotingPowVal2 - expectedVal1PrioBeforeAvg = expectedVal1PrioBeforeAvg - total - avgI64 := (wantVal2ProposerPrio + expectedVal1PrioBeforeAvg) / 2 - wantVal2ProposerPrio = wantVal2ProposerPrio - avgI64 - wantVal1Prio = expectedVal1PrioBeforeAvg - avgI64 - assert.Equal(t, wantVal2ProposerPrio, updatedVal2.ProposerPriority) - _, updatedVal1 = updatedState3.NextValidators.GetByAddress(val1PubKey.Address()) + // 2. Scale + // old prios: v1(10):-38, v2(1):39 + wantVal1Prio = prevVal1.ProposerPriority + wantVal2Prio = prevVal2.ProposerPriority + // scale to diffMax = 22 = 2 * tvp, diff=39-(-38)=77 + // new totalPower + totalPower := updatedVal1.VotingPower + updatedVal2.VotingPower + dist := wantVal2Prio - wantVal1Prio + // ratio := (dist + 2*totalPower - 1) / 2*totalPower = 98/22 = 4 + ratio := (dist + 2*totalPower - 1) / (2 * totalPower) + // v1(10):-38/4, v2(1):39/4 + wantVal1Prio /= ratio // -9 + wantVal2Prio /= ratio // 9 + + // 3. Center - noop + // 4. IncrementProposerPriority() -> + // v1(10):-9+10, v2(1):9+1 -> v2 proposer so subsract tvp(11) + // v1(10):1, v2(1):-1 + wantVal2Prio += updatedVal2.VotingPower // 10 -> prop + wantVal1Prio += updatedVal1.VotingPower // 1 + wantVal2Prio -= totalPower // -1 + + assert.Equal(t, wantVal2Prio, updatedVal2.ProposerPriority) assert.Equal(t, wantVal1Prio, updatedVal1.ProposerPriority) } @@ -527,22 +550,22 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { _, oldVal1 := updatedState2.Validators.GetByAddress(val1PubKey.Address()) _, updatedVal2 := updatedState2.NextValidators.GetByAddress(val2PubKey.Address()) - totalPower = val1VotingPower // no update - v2PrioWhenAddedVal2 := -(totalPower + (totalPower >> 3)) - v2PrioWhenAddedVal2 = v2PrioWhenAddedVal2 + val1VotingPower // -11 + 10 == -1 - v1PrioWhenAddedVal2 := oldVal1.ProposerPriority + val1VotingPower // -10 + 10 == 0 - totalPower = 2 * val1VotingPower // now we have to validators with that power - v1PrioWhenAddedVal2 = v1PrioWhenAddedVal2 - totalPower // mostest - // have to express the AVG in big.Ints as -1/2 == -1 in big.Int while -1/2 == 0 in int64 - avgSum := big.NewInt(0).Add(big.NewInt(v2PrioWhenAddedVal2), big.NewInt(v1PrioWhenAddedVal2)) - avg := avgSum.Div(avgSum, big.NewInt(2)) - expectedVal2Prio := v2PrioWhenAddedVal2 - avg.Int64() - totalPower = 2 * val1VotingPower // 10 + 10 - expectedVal1Prio := oldVal1.ProposerPriority + val1VotingPower - avg.Int64() - totalPower - // val1's ProposerPriority story: -10 (see above) + 10 (voting pow) - (-1) (avg) - 20 (total) == -19 + // 1. Add + val2VotingPower := val1VotingPower + totalPower = val1VotingPower + val2VotingPower // 20 + v2PrioWhenAddedVal2 := -(totalPower + (totalPower >> 3)) // -22 + // 2. Scale - noop + // 3. Center + avgSum := big.NewInt(0).Add(big.NewInt(v2PrioWhenAddedVal2), big.NewInt(oldVal1.ProposerPriority)) + avg := avgSum.Div(avgSum, big.NewInt(2)) // -11 + expectedVal2Prio := v2PrioWhenAddedVal2 - avg.Int64() // -11 + expectedVal1Prio := oldVal1.ProposerPriority - avg.Int64() // 11 + // 4. Increment + expectedVal2Prio = expectedVal2Prio + val2VotingPower // -11 + 10 = -1 + expectedVal1Prio = expectedVal1Prio + val1VotingPower // 11 + 10 == 21 + expectedVal1Prio = expectedVal1Prio - totalPower // 1, val1 proposer + assert.EqualValues(t, expectedVal1Prio, updatedVal1.ProposerPriority) - // val2 prio when added: -(totalVotingPower + (totalVotingPower >> 3)) == -11 - // -> -11 + 10 (voting power) - (-1) (avg) == 0 assert.EqualValues(t, expectedVal2Prio, updatedVal2.ProposerPriority, "unexpected proposer priority for validator: %v", updatedVal2) validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -551,34 +574,40 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { updatedState3, err := updateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) - // proposer changes from now on (every iteration) as long as there are no changes in the validator set: - assert.NotEqual(t, updatedState3.Validators.Proposer.Address, updatedState3.NextValidators.Proposer.Address) + assert.Equal(t, updatedState3.Validators.Proposer.Address, updatedState3.NextValidators.Proposer.Address) assert.Equal(t, updatedState3.Validators, updatedState2.NextValidators) _, updatedVal1 = updatedState3.NextValidators.GetByAddress(val1PubKey.Address()) - _, oldVal1 = updatedState3.Validators.GetByAddress(val1PubKey.Address()) _, updatedVal2 = updatedState3.NextValidators.GetByAddress(val2PubKey.Address()) - _, oldVal2 := updatedState3.Validators.GetByAddress(val2PubKey.Address()) - // val2 will be proposer: - assert.Equal(t, val2PubKey.Address(), updatedState3.NextValidators.Proposer.Address) + // val1 will still be proposer: + assert.Equal(t, val1PubKey.Address(), updatedState3.NextValidators.Proposer.Address) + // check if expected proposer prio is matched: + // Increment + expectedVal2Prio2 := expectedVal2Prio + val2VotingPower // -1 + 10 = 9 + expectedVal1Prio2 := expectedVal1Prio + val1VotingPower // 1 + 10 == 11 + expectedVal1Prio2 = expectedVal1Prio2 - totalPower // -9, val1 proposer - expectedVal1Prio2 := oldVal1.ProposerPriority + val1VotingPower - expectedVal2Prio2 := oldVal2.ProposerPriority + val1VotingPower - totalPower - avgSum = big.NewInt(expectedVal1Prio + expectedVal2Prio) - avg = avgSum.Div(avgSum, big.NewInt(2)) - expectedVal1Prio -= avg.Int64() - expectedVal2Prio -= avg.Int64() - - // -19 + 10 - 0 (avg) == -9 assert.EqualValues(t, expectedVal1Prio2, updatedVal1.ProposerPriority, "unexpected proposer priority for validator: %v", updatedVal2) - // 0 + 10 - 0 (avg) - 20 (total) == -10 assert.EqualValues(t, expectedVal2Prio2, updatedVal2.ProposerPriority, "unexpected proposer priority for validator: %v", updatedVal2) // no changes in voting power and both validators have same voting power // -> proposers should alternate: oldState := updatedState3 + abciResponses = &ABCIResponses{ + EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, + } + validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) + require.NoError(t, err) + + oldState, err = updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + assert.NoError(t, err) + expectedVal1Prio2 = 1 + expectedVal2Prio2 = -1 + expectedVal1Prio = -9 + expectedVal2Prio = 9 + for i := 0; i < 1000; i++ { // no validator updates: abciResponses := &ABCIResponses{ diff --git a/types/validator.go b/types/validator.go index 0b8967b24..325d20f5c 100644 --- a/types/validator.go +++ b/types/validator.go @@ -3,6 +3,7 @@ package types import ( "bytes" "fmt" + "strings" "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" @@ -68,6 +69,16 @@ func (v *Validator) String() string { v.ProposerPriority) } +// ValidatorListString returns a prettified validator list for logging purposes. +func ValidatorListString(vals []*Validator) string { + chunks := make([]string, len(vals)) + for i, val := range vals { + chunks[i] = fmt.Sprintf("%s:%d", val.Address, val.VotingPower) + } + + return strings.Join(chunks, ",") +} + // Bytes computes the unique encoding of a validator with a given voting power. // These are the bytes that gets hashed in consensus. It excludes address // as its redundant with the pubkey. This also excludes ProposerPriority diff --git a/types/validator_set.go b/types/validator_set.go index 2edec595d..c70f33962 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -12,14 +12,20 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" ) -// The maximum allowed total voting power. -// It needs to be sufficiently small to, in all cases:: +// MaxTotalVotingPower - the maximum allowed total voting power. +// It needs to be sufficiently small to, in all cases: // 1. prevent clipping in incrementProposerPriority() -// 2. let (diff+diffMax-1) not overflow in IncrementPropposerPriotity() +// 2. let (diff+diffMax-1) not overflow in IncrementProposerPriority() // (Proof of 1 is tricky, left to the reader). // It could be higher, but this is sufficiently large for our purposes, // and leaves room for defensive purposes. -const MaxTotalVotingPower = int64(math.MaxInt64) / 8 +// PriorityWindowSizeFactor - is a constant that when multiplied with the total voting power gives +// the maximum allowed distance between validator priorities. + +const ( + MaxTotalVotingPower = int64(math.MaxInt64) / 8 + PriorityWindowSizeFactor = 2 +) // ValidatorSet represent a set of *Validator at a given height. // The validators can be fetched by address or index. @@ -42,19 +48,17 @@ type ValidatorSet struct { // NewValidatorSet initializes a ValidatorSet by copying over the // values from `valz`, a list of Validators. If valz is nil or empty, // the new ValidatorSet will have an empty list of Validators. +// The addresses of validators in `valz` must be unique otherwise the +// function panics. func NewValidatorSet(valz []*Validator) *ValidatorSet { - validators := make([]*Validator, len(valz)) - for i, val := range valz { - validators[i] = val.Copy() - } - sort.Sort(ValidatorsByAddress(validators)) - vals := &ValidatorSet{ - Validators: validators, + vals := &ValidatorSet{} + err := vals.updateWithChangeSet(valz, false) + if err != nil { + panic(fmt.Sprintf("cannot create validator set: %s", err)) } if len(valz) > 0 { vals.IncrementProposerPriority(1) } - return vals } @@ -74,6 +78,9 @@ func (vals *ValidatorSet) CopyIncrementProposerPriority(times int) *ValidatorSet // proposer. Panics if validator set is empty. // `times` must be positive. func (vals *ValidatorSet) IncrementProposerPriority(times int) { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } if times <= 0 { panic("Cannot call IncrementProposerPriority with non-positive times") } @@ -81,20 +88,23 @@ func (vals *ValidatorSet) IncrementProposerPriority(times int) { // Cap the difference between priorities to be proportional to 2*totalPower by // re-normalizing priorities, i.e., rescale all priorities by multiplying with: // 2*totalVotingPower/(maxPriority - minPriority) - diffMax := 2 * vals.TotalVotingPower() + diffMax := PriorityWindowSizeFactor * vals.TotalVotingPower() vals.RescalePriorities(diffMax) + vals.shiftByAvgProposerPriority() var proposer *Validator // call IncrementProposerPriority(1) times times: for i := 0; i < times; i++ { proposer = vals.incrementProposerPriority() } - vals.shiftByAvgProposerPriority() vals.Proposer = proposer } func (vals *ValidatorSet) RescalePriorities(diffMax int64) { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } // NOTE: This check is merely a sanity check which could be // removed if all tests would init. voting power appropriately; // i.e. diffMax should always be > 0 @@ -102,7 +112,7 @@ func (vals *ValidatorSet) RescalePriorities(diffMax int64) { return } - // Caculating ceil(diff/diffMax): + // Calculating ceil(diff/diffMax): // Re-normalization is performed by dividing by an integer for simplicity. // NOTE: This may make debugging priority issues easier as well. diff := computeMaxMinPriorityDiff(vals) @@ -146,6 +156,9 @@ func (vals *ValidatorSet) computeAvgProposerPriority() int64 { // compute the difference between the max and min ProposerPriority of that set func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } max := int64(math.MinInt64) min := int64(math.MaxInt64) for _, v := range vals.Validators { @@ -173,21 +186,31 @@ func (vals *ValidatorSet) getValWithMostPriority() *Validator { } func (vals *ValidatorSet) shiftByAvgProposerPriority() { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } avgProposerPriority := vals.computeAvgProposerPriority() for _, val := range vals.Validators { val.ProposerPriority = safeSubClip(val.ProposerPriority, avgProposerPriority) } } +// Makes a copy of the validator list +func validatorListCopy(valsList []*Validator) []*Validator { + if valsList == nil { + return nil + } + valsCopy := make([]*Validator, len(valsList)) + for i, val := range valsList { + valsCopy[i] = val.Copy() + } + return valsCopy +} + // Copy each validator into a new ValidatorSet func (vals *ValidatorSet) Copy() *ValidatorSet { - validators := make([]*Validator, len(vals.Validators)) - for i, val := range vals.Validators { - // NOTE: must copy, since IncrementProposerPriority updates in place. - validators[i] = val.Copy() - } return &ValidatorSet{ - Validators: validators, + Validators: validatorListCopy(vals.Validators), Proposer: vals.Proposer, totalVotingPower: vals.totalVotingPower, } @@ -284,57 +307,6 @@ func (vals *ValidatorSet) Hash() []byte { return merkle.SimpleHashFromByteSlices(bzs) } -// Add adds val to the validator set and returns true. It returns false if val -// is already in the set. -func (vals *ValidatorSet) Add(val *Validator) (added bool) { - val = val.Copy() - idx := sort.Search(len(vals.Validators), func(i int) bool { - return bytes.Compare(val.Address, vals.Validators[i].Address) <= 0 - }) - if idx >= len(vals.Validators) { - vals.Validators = append(vals.Validators, val) - // Invalidate cache - vals.Proposer = nil - vals.totalVotingPower = 0 - return true - } else if bytes.Equal(vals.Validators[idx].Address, val.Address) { - return false - } else { - newValidators := make([]*Validator, len(vals.Validators)+1) - copy(newValidators[:idx], vals.Validators[:idx]) - newValidators[idx] = val - copy(newValidators[idx+1:], vals.Validators[idx:]) - vals.Validators = newValidators - // Invalidate cache - vals.Proposer = nil - vals.totalVotingPower = 0 - return true - } -} - -// Update updates the ValidatorSet by copying in the val. -// If the val is not found, it returns false; otherwise, -// it returns true. The val.ProposerPriority field is ignored -// and unchanged by this method. -func (vals *ValidatorSet) Update(val *Validator) (updated bool) { - index, sameVal := vals.GetByAddress(val.Address) - if sameVal == nil { - return false - } - // Overwrite the ProposerPriority so it doesn't change. - // During block execution, the val passed in here comes - // from ABCI via PB2TM.ValidatorUpdates. Since ABCI - // doesn't know about ProposerPriority, PB2TM.ValidatorUpdates - // uses the default value of 0, which would cause issues for - // proposer selection every time a validator's voting power changes. - val.ProposerPriority = sameVal.ProposerPriority - vals.Validators[index] = val.Copy() - // Invalidate cache - vals.Proposer = nil - vals.totalVotingPower = 0 - return true -} - // Remove deletes the validator with address. It returns the validator removed // and true. If returns nil and false if validator is not present in the set. func (vals *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) { @@ -366,6 +338,253 @@ func (vals *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) { } } +// Checks changes against duplicates, splits the changes in updates and removals, sorts them by address +// +// Returns: +// updates, removals - the sorted lists of updates and removals +// err - non-nil if duplicate entries or entries with negative voting power are seen +// +// No changes are made to 'origChanges' +func processChanges(origChanges []*Validator) (updates, removals []*Validator, err error) { + // Make a deep copy of the changes and sort by address + changes := validatorListCopy(origChanges) + sort.Sort(ValidatorsByAddress(changes)) + + removals = make([]*Validator, 0, len(changes)) + updates = make([]*Validator, 0, len(changes)) + var prevAddr Address + + // Scan changes by address and append valid validators to updates or removals lists + for _, valUpdate := range changes { + if bytes.Equal(valUpdate.Address, prevAddr) { + err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes) + return nil, nil, err + } + if valUpdate.VotingPower < 0 { + err = fmt.Errorf("voting power can't be negative %v", valUpdate) + return nil, nil, err + } + if valUpdate.VotingPower == 0 { + removals = append(removals, valUpdate) + } else { + updates = append(updates, valUpdate) + } + prevAddr = valUpdate.Address + } + return updates, removals, err +} + +// Verifies a list of updates against a validator set, making sure the allowed +// total voting power would not be exceeded if these updates would be applied to the set. +// It also computes the total voting power of the set that would result after the updates but +// before the removals. +// +// Returns: +// updatedTotalVotingPower - the new total voting power if these updates would be applied +// err - non-nil if the maximum allowed total voting power would be exceeded +// +// 'updates' should be a list of proper validator changes, i.e. they have been scanned +// by processChanges for duplicates and invalid values. +// No changes are made to the validator set 'vals'. +func verifyUpdates(updates []*Validator, vals *ValidatorSet) (updatedTotalVotingPower int64, err error) { + + // Scan the updates, compute new total voting power, check for overflow + updatedTotalVotingPower = vals.TotalVotingPower() + + for _, valUpdate := range updates { + address := valUpdate.Address + _, val := vals.GetByAddress(address) + if val == nil { + // new validator, add its voting power the the total + updatedTotalVotingPower += valUpdate.VotingPower + } else { + // updated validator, add the difference in power to the total + updatedTotalVotingPower += valUpdate.VotingPower - val.VotingPower + } + + if updatedTotalVotingPower < 0 { + err = fmt.Errorf( + "failed to add/update validator with negative voting power %v", + valUpdate) + return 0, err + } + overflow := updatedTotalVotingPower > MaxTotalVotingPower + if overflow { + err = fmt.Errorf( + "failed to add/update validator %v, total voting power would exceed the max allowed %v", + valUpdate, MaxTotalVotingPower) + return 0, err + } + } + + return updatedTotalVotingPower, nil +} + +// Computes the proposer priority for the validators not present in the set based on 'updatedTotalVotingPower' +// Leaves unchanged the priorities of validators that are changed. +// +// 'updates' parameter must be a list of unique validators to be added or updated. +// No changes are made to the validator set 'vals'. +func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotalVotingPower int64) int { + + numNew := 0 + // Scan and update the proposerPriority for newly added and updated validators + for _, valUpdate := range updates { + address := valUpdate.Address + _, val := vals.GetByAddress(address) + if val == nil { + // add val + // Set ProposerPriority to -C*totalVotingPower (with C ~= 1.125) to make sure validators can't + // un-bond and then re-bond to reset their (potentially previously negative) ProposerPriority to zero. + // + // Contract: updatedVotingPower < MaxTotalVotingPower to ensure ProposerPriority does + // not exceed the bounds of int64. + // + // Compute ProposerPriority = -1.125*totalVotingPower == -(updatedVotingPower + (updatedVotingPower >> 3)). + valUpdate.ProposerPriority = -(updatedTotalVotingPower + (updatedTotalVotingPower >> 3)) + numNew++ + } else { + valUpdate.ProposerPriority = val.ProposerPriority + } + } + + return numNew +} + +// Merges the vals' validator list with the updates list. +// When two elements with same address are seen, the one from updates is selected. +// Expects updates to be a list of updates sorted by address with no duplicates or errors, +// must have been validated with verifyUpdates() and priorities computed with computeNewPriorities(). +func (vals *ValidatorSet) applyUpdates(updates []*Validator) { + + existing := make([]*Validator, len(vals.Validators)) + copy(existing, vals.Validators) + + merged := make([]*Validator, len(existing)+len(updates)) + i := 0 + + for len(existing) > 0 && len(updates) > 0 { + if bytes.Compare(existing[0].Address, updates[0].Address) < 0 { + merged[i] = existing[0] + existing = existing[1:] + } else { + merged[i] = updates[0] + if bytes.Equal(existing[0].Address, updates[0].Address) { + // validator present in both, advance existing + existing = existing[1:] + } + updates = updates[1:] + } + i++ + } + + for j := 0; j < len(existing); j++ { + merged[i] = existing[j] + i++ + } + + for j := 0; j < len(updates); j++ { + merged[i] = updates[j] + i++ + } + + vals.Validators = merged[:i] + vals.totalVotingPower = 0 +} + +// Checks that the validators to be removed are part of the validator set. +// No changes are made to the validator set 'vals'. +func verifyRemovals(deletes []*Validator, vals *ValidatorSet) error { + + for _, valUpdate := range deletes { + address := valUpdate.Address + _, val := vals.GetByAddress(address) + if val == nil { + return fmt.Errorf("failed to find validator %X to remove", address) + } + } + return nil +} + +// Removes the validators specified in 'deletes' from validator set 'vals'. +// Should not fail as verification has been done before. +func (vals *ValidatorSet) applyRemovals(deletes []*Validator) { + + for _, valUpdate := range deletes { + address := valUpdate.Address + _, removed := vals.Remove(address) + if !removed { + // Should never happen + panic(fmt.Sprintf("failed to remove validator %X", address)) + } + } +} + +// UpdateWithChangeSet attempts to update the validator set with 'changes' +// It performs the following steps: +// - validates the changes making sure there are no duplicates and splits them in updates and deletes +// - verifies that applying the changes will not result in errors +// - computes the total voting power BEFORE removals to ensure that in the next steps the relative priorities +// across old and newly added validators is fair +// - computes the priorities of new validators against the final set +// - applies the updates against the validator set +// - applies the removals against the validator set +// - performs scaling and centering of priority values +// If error is detected during verification steps it is returned and the validator set +// is not changed. +func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { + return vals.updateWithChangeSet(changes, true) +} + +// main function used by UpdateWithChangeSet() and NewValidatorSet() +// If 'allowDeletes' is false then delete operations are not allowed and must be reported if +// present in 'changes' +func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes bool) error { + + if len(changes) <= 0 { + return nil + } + + // Check for duplicates within changes, split in 'updates' and 'deletes' lists (sorted) + updates, deletes, err := processChanges(changes) + if err != nil { + return err + } + + if !allowDeletes && len(deletes) != 0 { + err = fmt.Errorf("cannot process validators with voting power 0: %v", deletes) + return err + } + + // Verify that applying the 'deletes' against 'vals' will not result in error. + if err := verifyRemovals(deletes, vals); err != nil { + return err + } + + // Verify that applying the 'updates' against 'vals' will not result in error. + updatedTotalVotingPower, err := verifyUpdates(updates, vals) + if err != nil { + return err + } + + // Compute the priorities for updates + numNewValidators := computeNewPriorities(updates, vals, updatedTotalVotingPower) + if len(vals.Validators)+numNewValidators <= len(deletes) { + err = fmt.Errorf("applying the validator changes would result in empty set") + return err + } + + // Apply updates and removals + vals.applyUpdates(updates) + vals.applyRemovals(deletes) + + // Scale and center + vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower()) + vals.shiftByAvgProposerPriority() + + return nil +} + // Verify that +2/3 of the set had signed the given signBytes. func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height int64, commit *Commit) error { diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 72b2f6613..04874c198 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "math" + "math/rand" "strings" "testing" "testing/quick" @@ -45,31 +46,29 @@ func TestValidatorSetBasic(t *testing.T) { assert.Nil(t, vset.Hash()) // add - val = randValidator_(vset.TotalVotingPower()) - assert.True(t, vset.Add(val)) + assert.NoError(t, vset.UpdateWithChangeSet([]*Validator{val})) + assert.True(t, vset.HasAddress(val.Address)) - idx, val2 := vset.GetByAddress(val.Address) + idx, _ = vset.GetByAddress(val.Address) assert.Equal(t, 0, idx) - assert.Equal(t, val, val2) - addr, val2 = vset.GetByIndex(0) + addr, _ = vset.GetByIndex(0) assert.Equal(t, []byte(val.Address), addr) - assert.Equal(t, val, val2) assert.Equal(t, 1, vset.Size()) assert.Equal(t, val.VotingPower, vset.TotalVotingPower()) - assert.Equal(t, val, vset.GetProposer()) assert.NotNil(t, vset.Hash()) assert.NotPanics(t, func() { vset.IncrementProposerPriority(1) }) + assert.Equal(t, val.Address, vset.GetProposer().Address) // update - assert.False(t, vset.Update(randValidator_(vset.TotalVotingPower()))) + val = randValidator_(vset.TotalVotingPower()) + assert.NoError(t, vset.UpdateWithChangeSet([]*Validator{val})) _, val = vset.GetByAddress(val.Address) val.VotingPower += 100 proposerPriority := val.ProposerPriority - // Mimic update from types.PB2TM.ValidatorUpdates which does not know about ProposerPriority - // and hence defaults to 0. + val.ProposerPriority = 0 - assert.True(t, vset.Update(val)) + assert.NoError(t, vset.UpdateWithChangeSet([]*Validator{val})) _, val = vset.GetByAddress(val.Address) assert.Equal(t, proposerPriority, val.ProposerPriority) @@ -116,8 +115,9 @@ func BenchmarkValidatorSetCopy(b *testing.B) { for i := 0; i < 1000; i++ { privKey := ed25519.GenPrivKey() pubKey := privKey.PubKey() - val := NewValidator(pubKey, 0) - if !vset.Add(val) { + val := NewValidator(pubKey, 10) + err := vset.UpdateWithChangeSet([]*Validator{val}) + if err != nil { panic("Failed to add validator") } } @@ -284,7 +284,7 @@ func randPubKey() crypto.PubKey { func randValidator_(totalVotingPower int64) *Validator { // this modulo limits the ProposerPriority/VotingPower to stay in the // bounds of MaxTotalVotingPower minus the already existing voting power: - val := NewValidator(randPubKey(), cmn.RandInt64()%(MaxTotalVotingPower-totalVotingPower)) + val := NewValidator(randPubKey(), int64(cmn.RandUint64()%uint64((MaxTotalVotingPower-totalVotingPower)))) val.ProposerPriority = cmn.RandInt64() % (MaxTotalVotingPower - totalVotingPower) return val } @@ -599,3 +599,357 @@ func TestValidatorSetVerifyCommit(t *testing.T) { err = vset.VerifyCommit(chainID, blockID, height, commit) assert.Nil(t, err) } + +func TestEmptySet(t *testing.T) { + + var valList []*Validator + valSet := NewValidatorSet(valList) + assert.Panics(t, func() { valSet.IncrementProposerPriority(1) }) + assert.Panics(t, func() { valSet.RescalePriorities(100) }) + assert.Panics(t, func() { valSet.shiftByAvgProposerPriority() }) + assert.Panics(t, func() { assert.Zero(t, computeMaxMinPriorityDiff(valSet)) }) + valSet.GetProposer() + + // Add to empty set + v1 := newValidator([]byte("v1"), 100) + v2 := newValidator([]byte("v2"), 100) + valList = []*Validator{v1, v2} + assert.NoError(t, valSet.UpdateWithChangeSet(valList)) + verifyValidatorSet(t, valSet) + + // Delete all validators from set + v1 = newValidator([]byte("v1"), 0) + v2 = newValidator([]byte("v2"), 0) + delList := []*Validator{v1, v2} + assert.Error(t, valSet.UpdateWithChangeSet(delList)) + + // Attempt delete from empty set + assert.Error(t, valSet.UpdateWithChangeSet(delList)) + +} + +func TestUpdatesForNewValidatorSet(t *testing.T) { + + v1 := newValidator([]byte("v1"), 100) + v2 := newValidator([]byte("v2"), 100) + valList := []*Validator{v1, v2} + valSet := NewValidatorSet(valList) + verifyValidatorSet(t, valSet) + + // Verify duplicates are caught in NewValidatorSet() and it panics + v111 := newValidator([]byte("v1"), 100) + v112 := newValidator([]byte("v1"), 123) + v113 := newValidator([]byte("v1"), 234) + valList = []*Validator{v111, v112, v113} + assert.Panics(t, func() { NewValidatorSet(valList) }) + + // Verify set including validator with voting power 0 cannot be created + v1 = newValidator([]byte("v1"), 0) + v2 = newValidator([]byte("v2"), 22) + v3 := newValidator([]byte("v3"), 33) + valList = []*Validator{v1, v2, v3} + assert.Panics(t, func() { NewValidatorSet(valList) }) + + // Verify set including validator with negative voting power cannot be created + v1 = newValidator([]byte("v1"), 10) + v2 = newValidator([]byte("v2"), -20) + v3 = newValidator([]byte("v3"), 30) + valList = []*Validator{v1, v2, v3} + assert.Panics(t, func() { NewValidatorSet(valList) }) + +} + +type testVal struct { + name string + power int64 +} + +func TestValSetUpdatesBasicTestsExecute(t *testing.T) { + valSetUpdatesBasicTests := []struct { + startVals []testVal + updateVals []testVal + expectedVals []testVal + expError bool + }{ + // Operations that should result in error + 0: { // updates leading to overflows + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v1", math.MaxInt64}}, + []testVal{{"v1", 10}, {"v2", 10}}, + true}, + 1: { // duplicate entries in changes + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v1", 11}, {"v1", 22}}, + []testVal{{"v1", 10}, {"v2", 10}}, + true}, + 2: { // duplicate entries in removes + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v1", 0}, {"v1", 0}}, + []testVal{{"v1", 10}, {"v2", 10}}, + true}, + 3: { // duplicate entries in removes + changes + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v1", 0}, {"v2", 20}, {"v2", 30}, {"v1", 0}}, + []testVal{{"v1", 10}, {"v2", 10}}, + true}, + 4: { // update with negative voting power + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v1", -123}}, + []testVal{{"v1", 10}, {"v2", 10}}, + true}, + 5: { // delete non existing validator + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v3", 0}}, + []testVal{{"v1", 10}, {"v2", 10}}, + true}, + + // Operations that should be successful + 6: { // no changes + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{}, + []testVal{{"v1", 10}, {"v2", 10}}, + false}, + 7: { // voting power changes + []testVal{{"v1", 10}, {"v2", 10}}, + []testVal{{"v1", 11}, {"v2", 22}}, + []testVal{{"v1", 11}, {"v2", 22}}, + false}, + 8: { // add new validators + []testVal{{"v1", 10}, {"v2", 20}}, + []testVal{{"v3", 30}, {"v4", 40}}, + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}, {"v4", 40}}, + false}, + 9: { // delete validators + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, + []testVal{{"v2", 0}}, + []testVal{{"v1", 10}, {"v3", 30}}, + false}, + 10: { // delete all validators + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, + []testVal{{"v1", 0}, {"v2", 0}, {"v3", 0}}, + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, + true}, + } + + for i, tt := range valSetUpdatesBasicTests { + // create a new set and apply updates, keeping copies for the checks + valSet := createNewValidatorSet(tt.startVals) + valSetCopy := valSet.Copy() + valList := createNewValidatorList(tt.updateVals) + valListCopy := validatorListCopy(valList) + err := valSet.UpdateWithChangeSet(valList) + + if tt.expError { + // for errors check the validator set has not been changed + assert.Error(t, err, "test %d", i) + assert.Equal(t, valSet, valSetCopy, "test %v", i) + } else { + assert.NoError(t, err, "test %d", i) + } + // check the parameter list has not changed + assert.Equal(t, valList, valListCopy, "test %v", i) + + // check the final validator list is as expected and the set is properly scaled and centered. + assert.Equal(t, getValidatorResults(valSet.Validators), tt.expectedVals, "test %v", i) + verifyValidatorSet(t, valSet) + } +} + +func getValidatorResults(valList []*Validator) []testVal { + testList := make([]testVal, len(valList)) + for i, val := range valList { + testList[i].name = string(val.Address) + testList[i].power = val.VotingPower + } + return testList +} + +// Test that different permutations of an update give the same result. +func TestValSetUpdatesOrderTestsExecute(t *testing.T) { + // startVals - initial validators to create the set with + // updateVals - a sequence of updates to be applied to the set. + // updateVals is shuffled a number of times during testing to check for same resulting validator set. + valSetUpdatesOrderTests := []struct { + startVals []testVal + updateVals []testVal + }{ + 0: { // order of changes should not matter, the final validator sets should be the same + []testVal{{"v1", 10}, {"v2", 10}, {"v3", 30}, {"v4", 40}}, + []testVal{{"v1", 11}, {"v2", 22}, {"v3", 33}, {"v4", 44}}}, + + 1: { // order of additions should not matter + []testVal{{"v1", 10}, {"v2", 20}}, + []testVal{{"v3", 30}, {"v4", 40}, {"v5", 50}, {"v6", 60}}}, + + 2: { // order of removals should not matter + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}, {"v4", 40}}, + []testVal{{"v1", 0}, {"v3", 0}, {"v4", 0}}}, + + 3: { // order of mixed operations should not matter + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}, {"v4", 40}}, + []testVal{{"v1", 0}, {"v3", 0}, {"v2", 22}, {"v5", 50}, {"v4", 44}}}, + } + + for i, tt := range valSetUpdatesOrderTests { + // create a new set and apply updates + valSet := createNewValidatorSet(tt.startVals) + valSetCopy := valSet.Copy() + valList := createNewValidatorList(tt.updateVals) + assert.NoError(t, valSetCopy.UpdateWithChangeSet(valList)) + + // save the result as expected for next updates + valSetExp := valSetCopy.Copy() + + // perform at most 20 permutations on the updates and call UpdateWithChangeSet() + n := len(tt.updateVals) + maxNumPerms := cmn.MinInt(20, n*n) + for j := 0; j < maxNumPerms; j++ { + // create a copy of original set and apply a random permutation of updates + valSetCopy := valSet.Copy() + valList := createNewValidatorList(permutation(tt.updateVals)) + + // check there was no error and the set is properly scaled and centered. + assert.NoError(t, valSetCopy.UpdateWithChangeSet(valList), + "test %v failed for permutation %v", i, valList) + verifyValidatorSet(t, valSetCopy) + + // verify the resulting test is same as the expected + assert.Equal(t, valSetCopy, valSetExp, + "test %v failed for permutation %v", i, valList) + } + } +} + +// This tests the private function validator_set.go:applyUpdates() function, used only for additions and changes. +// Should perform a proper merge of updatedVals and startVals +func TestValSetApplyUpdatesTestsExecute(t *testing.T) { + valSetUpdatesBasicTests := []struct { + startVals []testVal + updateVals []testVal + expectedVals []testVal + }{ + // additions + 0: { // prepend + []testVal{{"v4", 44}, {"v5", 55}}, + []testVal{{"v1", 11}}, + []testVal{{"v1", 11}, {"v4", 44}, {"v5", 55}}}, + 1: { // append + []testVal{{"v4", 44}, {"v5", 55}}, + []testVal{{"v6", 66}}, + []testVal{{"v4", 44}, {"v5", 55}, {"v6", 66}}}, + 2: { // insert + []testVal{{"v4", 44}, {"v6", 66}}, + []testVal{{"v5", 55}}, + []testVal{{"v4", 44}, {"v5", 55}, {"v6", 66}}}, + 3: { // insert multi + []testVal{{"v4", 44}, {"v6", 66}, {"v9", 99}}, + []testVal{{"v5", 55}, {"v7", 77}, {"v8", 88}}, + []testVal{{"v4", 44}, {"v5", 55}, {"v6", 66}, {"v7", 77}, {"v8", 88}, {"v9", 99}}}, + // changes + 4: { // head + []testVal{{"v1", 111}, {"v2", 22}}, + []testVal{{"v1", 11}}, + []testVal{{"v1", 11}, {"v2", 22}}}, + 5: { // tail + []testVal{{"v1", 11}, {"v2", 222}}, + []testVal{{"v2", 22}}, + []testVal{{"v1", 11}, {"v2", 22}}}, + 6: { // middle + []testVal{{"v1", 11}, {"v2", 222}, {"v3", 33}}, + []testVal{{"v2", 22}}, + []testVal{{"v1", 11}, {"v2", 22}, {"v3", 33}}}, + 7: { // multi + []testVal{{"v1", 111}, {"v2", 222}, {"v3", 333}}, + []testVal{{"v1", 11}, {"v2", 22}, {"v3", 33}}, + []testVal{{"v1", 11}, {"v2", 22}, {"v3", 33}}}, + // additions and changes + 8: { + []testVal{{"v1", 111}, {"v2", 22}}, + []testVal{{"v1", 11}, {"v3", 33}, {"v4", 44}}, + []testVal{{"v1", 11}, {"v2", 22}, {"v3", 33}, {"v4", 44}}}, + } + + for i, tt := range valSetUpdatesBasicTests { + // create a new validator set with the start values + valSet := createNewValidatorSet(tt.startVals) + + // applyUpdates() with the update values + valList := createNewValidatorList(tt.updateVals) + valSet.applyUpdates(valList) + + // check the new list of validators for proper merge + assert.Equal(t, getValidatorResults(valSet.Validators), tt.expectedVals, "test %v", i) + verifyValidatorSet(t, valSet) + } +} + +func permutation(valList []testVal) []testVal { + if len(valList) == 0 { + return nil + } + permList := make([]testVal, len(valList)) + perm := rand.Perm(len(valList)) + for i, v := range perm { + permList[v] = valList[i] + } + return permList +} + +func createNewValidatorList(testValList []testVal) []*Validator { + valList := make([]*Validator, 0, len(testValList)) + for _, val := range testValList { + valList = append(valList, newValidator([]byte(val.name), val.power)) + } + return valList +} + +func createNewValidatorSet(testValList []testVal) *ValidatorSet { + valList := createNewValidatorList(testValList) + valSet := NewValidatorSet(valList) + return valSet +} + +func verifyValidatorSet(t *testing.T, valSet *ValidatorSet) { + // verify that the vals' tvp is set to the sum of the all vals voting powers + tvp := valSet.TotalVotingPower() + assert.Equal(t, valSet.totalVotingPower, tvp, + "expected TVP %d. Got %d, valSet=%s", tvp, valSet.totalVotingPower, valSet) + + // verify that validator priorities are centered + l := int64(len(valSet.Validators)) + tpp := valSet.TotalVotingPower() + assert.True(t, tpp <= l || tpp >= -l, + "expected total priority in (-%d, %d). Got %d", l, l, tpp) + + // verify that priorities are scaled + dist := computeMaxMinPriorityDiff(valSet) + assert.True(t, dist <= PriorityWindowSizeFactor*tvp, + "expected priority distance < %d. Got %d", PriorityWindowSizeFactor*tvp, dist) +} + +func BenchmarkUpdates(b *testing.B) { + const ( + n = 100 + m = 2000 + ) + // Init with n validators + vs := make([]*Validator, n) + for j := 0; j < n; j++ { + vs[j] = newValidator([]byte(fmt.Sprintf("v%d", j)), 100) + } + valSet := NewValidatorSet(vs) + l := len(valSet.Validators) + + // Make m new validators + newValList := make([]*Validator, m) + for j := 0; j < m; j++ { + newValList[j] = newValidator([]byte(fmt.Sprintf("v%d", j+l)), 1000) + } + b.ResetTimer() + + for i := 0; i < b.N; i++ { + // Add m validators to valSetCopy + valSetCopy := valSet.Copy() + assert.NoError(b, valSetCopy.UpdateWithChangeSet(newValList)) + } +} From 90ba63948ad48856fbe23c725b9ffd85c956c174 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 8 Feb 2019 18:25:48 -0500 Subject: [PATCH 041/259] Sec/bucky/35 commit duplicate evidence (#36) Don't add committed evidence to evpool --- evidence/pool.go | 3 ++- evidence/pool_test.go | 4 ++-- evidence/reactor_test.go | 4 ++-- evidence/store.go | 38 +++++++++++++++++++++----------------- evidence/store_test.go | 22 +++++++++++++++++++--- node/node.go | 3 +-- node/node_test.go | 6 ++---- state/execution.go | 1 - state/services.go | 1 + state/validation.go | 2 ++ 10 files changed, 52 insertions(+), 32 deletions(-) diff --git a/evidence/pool.go b/evidence/pool.go index b5fdbdf1d..3df0ec70a 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -28,7 +28,8 @@ type EvidencePool struct { state sm.State } -func NewEvidencePool(stateDB dbm.DB, evidenceStore *EvidenceStore) *EvidencePool { +func NewEvidencePool(stateDB, evidenceDB dbm.DB) *EvidencePool { + evidenceStore := NewEvidenceStore(evidenceDB) evpool := &EvidencePool{ stateDB: stateDB, state: sm.LoadState(stateDB), diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 1f4f1a06f..677ce78bb 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -56,8 +56,8 @@ func TestEvidencePool(t *testing.T) { valAddr := []byte("val1") height := int64(5) stateDB := initializeValidatorState(valAddr, height) - store := NewEvidenceStore(dbm.NewMemDB()) - pool := NewEvidencePool(stateDB, store) + evidenceDB := dbm.NewMemDB() + pool := NewEvidencePool(stateDB, evidenceDB) goodEvidence := types.NewMockGoodEvidence(height, 0, valAddr) badEvidence := types.MockBadEvidence{goodEvidence} diff --git a/evidence/reactor_test.go b/evidence/reactor_test.go index 1c4e731ab..635e9553f 100644 --- a/evidence/reactor_test.go +++ b/evidence/reactor_test.go @@ -37,8 +37,8 @@ func makeAndConnectEvidenceReactors(config *cfg.Config, stateDBs []dbm.DB) []*Ev logger := evidenceLogger() for i := 0; i < N; i++ { - store := NewEvidenceStore(dbm.NewMemDB()) - pool := NewEvidencePool(stateDBs[i], store) + evidenceDB := dbm.NewMemDB() + pool := NewEvidencePool(stateDBs[i], evidenceDB) reactors[i] = NewEvidenceReactor(pool) reactors[i].SetLogger(logger.With("validator", i)) } diff --git a/evidence/store.go b/evidence/store.go index 17b37aaba..998a763d4 100644 --- a/evidence/store.go +++ b/evidence/store.go @@ -117,32 +117,33 @@ func (store *EvidenceStore) listEvidence(prefixKey string, maxNum int64) (eviden return evidence } -// GetEvidence fetches the evidence with the given height and hash. -func (store *EvidenceStore) GetEvidence(height int64, hash []byte) *EvidenceInfo { +// GetEvidenceInfo fetches the EvidenceInfo with the given height and hash. +// If not found, ei.Evidence is nil. +func (store *EvidenceStore) GetEvidenceInfo(height int64, hash []byte) EvidenceInfo { key := keyLookupFromHeightAndHash(height, hash) val := store.db.Get(key) if len(val) == 0 { - return nil + return EvidenceInfo{} } var ei EvidenceInfo err := cdc.UnmarshalBinaryBare(val, &ei) if err != nil { panic(err) } - return &ei + return ei } // AddNewEvidence adds the given evidence to the database. // It returns false if the evidence is already stored. func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int64) bool { // check if we already have seen it - ei_ := store.GetEvidence(evidence.Height(), evidence.Hash()) - if ei_ != nil && ei_.Evidence != nil { + ei := store.getEvidenceInfo(evidence) + if ei.Evidence != nil { return false } - ei := EvidenceInfo{ + ei = EvidenceInfo{ Committed: false, Priority: priority, Evidence: evidence, @@ -165,6 +166,11 @@ func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int // MarkEvidenceAsBroadcasted removes evidence from Outqueue. func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) { ei := store.getEvidenceInfo(evidence) + if ei.Evidence == nil { + // nothin to do + return + } + // remove from the outqueue key := keyOutqueue(evidence, ei.Priority) store.db.Delete(key) } @@ -177,8 +183,12 @@ func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) { pendingKey := keyPending(evidence) store.db.Delete(pendingKey) - ei := store.getEvidenceInfo(evidence) - ei.Committed = true + // committed EvidenceInfo doens't need priority + ei := EvidenceInfo{ + Committed: true, + Evidence: evidence, + Priority: 0, + } lookupKey := keyLookup(evidence) store.db.SetSync(lookupKey, cdc.MustMarshalBinaryBare(ei)) @@ -187,13 +197,7 @@ func (store *EvidenceStore) MarkEvidenceAsCommitted(evidence types.Evidence) { //--------------------------------------------------- // utils +// getEvidenceInfo is convenience for calling GetEvidenceInfo if we have the full evidence. func (store *EvidenceStore) getEvidenceInfo(evidence types.Evidence) EvidenceInfo { - key := keyLookup(evidence) - var ei EvidenceInfo - b := store.db.Get(key) - err := cdc.UnmarshalBinaryBare(b, &ei) - if err != nil { - panic(err) - } - return ei + return store.GetEvidenceInfo(evidence.Height(), evidence.Hash()) } diff --git a/evidence/store_test.go b/evidence/store_test.go index 35eb28d01..5a7a8bd36 100644 --- a/evidence/store_test.go +++ b/evidence/store_test.go @@ -27,6 +27,21 @@ func TestStoreAddDuplicate(t *testing.T) { assert.False(added) } +func TestStoreCommitDuplicate(t *testing.T) { + assert := assert.New(t) + + db := dbm.NewMemDB() + store := NewEvidenceStore(db) + + priority := int64(10) + ev := types.NewMockGoodEvidence(2, 1, []byte("val1")) + + store.MarkEvidenceAsCommitted(ev) + + added := store.AddNewEvidence(ev, priority) + assert.False(added) +} + func TestStoreMark(t *testing.T) { assert := assert.New(t) @@ -46,7 +61,7 @@ func TestStoreMark(t *testing.T) { assert.True(added) // get the evidence. verify. should be uncommitted - ei := store.GetEvidence(ev.Height(), ev.Hash()) + ei := store.GetEvidenceInfo(ev.Height(), ev.Hash()) assert.Equal(ev, ei.Evidence) assert.Equal(priority, ei.Priority) assert.False(ei.Committed) @@ -72,9 +87,10 @@ func TestStoreMark(t *testing.T) { assert.Equal(0, len(pendingEv)) // evidence should show committed - ei = store.GetEvidence(ev.Height(), ev.Hash()) + newPriority := int64(0) + ei = store.GetEvidenceInfo(ev.Height(), ev.Hash()) assert.Equal(ev, ei.Evidence) - assert.Equal(priority, ei.Priority) + assert.Equal(newPriority, ei.Priority) assert.True(ei.Committed) } diff --git a/node/node.go b/node/node.go index 1b7319811..969452c40 100644 --- a/node/node.go +++ b/node/node.go @@ -345,8 +345,7 @@ func NewNode(config *cfg.Config, return nil, err } evidenceLogger := logger.With("module", "evidence") - evidenceStore := evidence.NewEvidenceStore(evidenceDB) - evidencePool := evidence.NewEvidencePool(stateDB, evidenceStore) + evidencePool := evidence.NewEvidencePool(stateDB, evidenceDB) evidencePool.SetLogger(evidenceLogger) evidenceReactor := evidence.NewEvidenceReactor(evidencePool) evidenceReactor.SetLogger(evidenceLogger) diff --git a/node/node_test.go b/node/node_test.go index 3218c8327..4b4610e10 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -227,11 +227,10 @@ func TestCreateProposalBlock(t *testing.T) { mempool.SetLogger(logger) // Make EvidencePool - types.RegisterMockEvidencesGlobal() + types.RegisterMockEvidencesGlobal() // XXX! evidence.RegisterMockEvidences() evidenceDB := dbm.NewMemDB() - evidenceStore := evidence.NewEvidenceStore(evidenceDB) - evidencePool := evidence.NewEvidencePool(stateDB, evidenceStore) + evidencePool := evidence.NewEvidencePool(stateDB, evidenceDB) evidencePool.SetLogger(logger) // fill the evidence pool with more evidence @@ -270,7 +269,6 @@ func TestCreateProposalBlock(t *testing.T) { err = blockExec.ValidateBlock(state, block) assert.NoError(t, err) - } func state(nVals int, height int64) (sm.State, dbm.DB) { diff --git a/state/execution.go b/state/execution.go index 470e22bc0..e3668c77a 100644 --- a/state/execution.go +++ b/state/execution.go @@ -94,7 +94,6 @@ func (blockExec *BlockExecutor) CreateProposalBlock( txs := blockExec.mempool.ReapMaxBytesMaxGas(maxDataBytes, maxGas) return state.MakeBlock(height, txs, commit, evidence, proposerAddr) - } // ValidateBlock validates the given block against the given state. diff --git a/state/services.go b/state/services.go index b8f1febe1..90f0cd019 100644 --- a/state/services.go +++ b/state/services.go @@ -80,6 +80,7 @@ type BlockStore interface { // evidence pool // EvidencePool defines the EvidencePool interface used by the ConsensusState. +// Get/Set/Commit type EvidencePool interface { PendingEvidence(int64) []types.Evidence AddEvidence(types.Evidence) error diff --git a/state/validation.go b/state/validation.go index cd571e34f..82c479ac6 100644 --- a/state/validation.go +++ b/state/validation.go @@ -185,6 +185,8 @@ func VerifyEvidence(stateDB dbm.DB, state State, evidence types.Evidence) error // The address must have been an active validator at the height. // NOTE: we will ignore evidence from H if the key was not a validator // at H, even if it is a validator at some nearby H' + // XXX: this makes lite-client bisection as is unsafe + // See https://github.com/tendermint/tendermint/issues/3244 ev := evidence height, addr := ev.Height(), ev.Address() _, val := valset.GetByAddress(addr) From 87bdc42bf827c659aaa4b3c9c796bc3f8e698a87 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Sat, 9 Feb 2019 00:30:45 +0100 Subject: [PATCH 042/259] Reject blocks with committed evidence (#37) * evidence: NewEvidencePool takes evidenceDB * evidence: failing TestStoreCommitDuplicate tendermint/security#35 * GetEvidence -> GetEvidenceInfo * fix TestStoreCommitDuplicate * comment in VerifyEvidence * add check if evidence was already seen - modify EventPool interface (EventStore is not known in ApplyBlock): - add IsCommitted method to iface - add test * update changelog * fix TestStoreMark: - priority in evidence info gets reset to zero after evidence gets committed * review comments: simplify EvidencePool.IsCommitted - delete obsolete EvidenceStore.IsCommitted * add simple test for IsCommitted * update changelog: this is actually breaking (PR number still missing) * fix TestStoreMark: - priority in evidence info gets reset to zero after evidence gets committed * review suggestion: simplify return --- CHANGELOG_PENDING.md | 15 +++++++++++++++ consensus/reactor_test.go | 1 + evidence/pool.go | 6 ++++++ evidence/pool_test.go | 21 +++++++++++++++++++++ evidence/store.go | 2 +- state/execution.go | 2 +- state/services.go | 3 +++ state/validation.go | 5 ++++- state/validation_test.go | 25 +++++++++++++++++++++++++ 9 files changed, 77 insertions(+), 3 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 4548eb1eb..91eed1882 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -6,8 +6,23 @@ Special thanks to external contributors on this release: ### BREAKING CHANGES: +* CLI/RPC/Config + +* Apps + +* Go API + +* Blockchain Protocol + + - [types] Reject blocks which contain already committed evidence + +* P2P Protocol + ### FEATURES: ### IMPROVEMENTS: ### BUG FIXES: + + - [evidence] Do not store evidence which was already marked as committed + diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 28e245aec..d35eaf3c0 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -211,6 +211,7 @@ func (m *mockEvidencePool) Update(block *types.Block, state sm.State) { } m.height++ } +func (m *mockEvidencePool) IsCommitted(types.Evidence) bool { return false } //------------------------------------ diff --git a/evidence/pool.go b/evidence/pool.go index 3df0ec70a..18ccb3344 100644 --- a/evidence/pool.go +++ b/evidence/pool.go @@ -133,6 +133,12 @@ func (evpool *EvidencePool) MarkEvidenceAsCommitted(height int64, evidence []typ } +// IsCommitted returns true if we have already seen this exact evidence and it is already marked as committed. +func (evpool *EvidencePool) IsCommitted(evidence types.Evidence) bool { + ei := evpool.evidenceStore.getEvidenceInfo(evidence) + return ei.Evidence != nil && ei.Committed +} + func (evpool *EvidencePool) removeEvidence(height, maxAge int64, blockEvidenceMap map[string]struct{}) { for e := evpool.evidenceList.Front(); e != nil; e = e.Next() { ev := e.Value.(types.Evidence) diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 677ce78bb..30b20011e 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -84,3 +84,24 @@ func TestEvidencePool(t *testing.T) { assert.Nil(t, err) assert.Equal(t, 1, pool.evidenceList.Len()) } + +func TestEvidencePoolIsCommitted(t *testing.T) { + // Initialization: + valAddr := []byte("validator_address") + height := int64(42) + stateDB := initializeValidatorState(valAddr, height) + evidenceDB := dbm.NewMemDB() + pool := NewEvidencePool(stateDB, evidenceDB) + + // evidence not seen yet: + evidence := types.NewMockGoodEvidence(height, 0, valAddr) + assert.False(t, pool.IsCommitted(evidence)) + + // evidence seen but not yet committed: + assert.NoError(t, pool.AddEvidence(evidence)) + assert.False(t, pool.IsCommitted(evidence)) + + // evidence seen and committed: + pool.MarkEvidenceAsCommitted(height, []types.Evidence{evidence}) + assert.True(t, pool.IsCommitted(evidence)) +} diff --git a/evidence/store.go b/evidence/store.go index 998a763d4..464d6138e 100644 --- a/evidence/store.go +++ b/evidence/store.go @@ -167,7 +167,7 @@ func (store *EvidenceStore) AddNewEvidence(evidence types.Evidence, priority int func (store *EvidenceStore) MarkEvidenceAsBroadcasted(evidence types.Evidence) { ei := store.getEvidenceInfo(evidence) if ei.Evidence == nil { - // nothin to do + // nothing to do; we did not store the evidence yet (AddNewEvidence): return } // remove from the outqueue diff --git a/state/execution.go b/state/execution.go index e3668c77a..8ab95839e 100644 --- a/state/execution.go +++ b/state/execution.go @@ -101,7 +101,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( // Validation does not mutate state, but does require historical information from the stateDB, // ie. to verify evidence from a validator at an old height. func (blockExec *BlockExecutor) ValidateBlock(state State, block *types.Block) error { - return validateBlock(blockExec.db, state, block) + return validateBlock(blockExec.evpool, blockExec.db, state, block) } // ApplyBlock validates the block against the state, executes it against the app, diff --git a/state/services.go b/state/services.go index 90f0cd019..02c3aa7d1 100644 --- a/state/services.go +++ b/state/services.go @@ -85,6 +85,8 @@ type EvidencePool interface { PendingEvidence(int64) []types.Evidence AddEvidence(types.Evidence) error Update(*types.Block, State) + // IsCommitted indicates if this evidence was already marked committed in another block. + IsCommitted(types.Evidence) bool } // MockMempool is an empty implementation of a Mempool, useful for testing. @@ -93,3 +95,4 @@ type MockEvidencePool struct{} func (m MockEvidencePool) PendingEvidence(int64) []types.Evidence { return nil } func (m MockEvidencePool) AddEvidence(types.Evidence) error { return nil } func (m MockEvidencePool) Update(*types.Block, State) {} +func (m MockEvidencePool) IsCommitted(types.Evidence) bool { return false } diff --git a/state/validation.go b/state/validation.go index 82c479ac6..3cb0ee8fb 100644 --- a/state/validation.go +++ b/state/validation.go @@ -13,7 +13,7 @@ import ( //----------------------------------------------------- // Validate block -func validateBlock(stateDB dbm.DB, state State, block *types.Block) error { +func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block *types.Block) error { // Validate internal consistency. if err := block.ValidateBasic(); err != nil { return err @@ -145,6 +145,9 @@ func validateBlock(stateDB dbm.DB, state State, block *types.Block) error { if err := VerifyEvidence(stateDB, state, ev); err != nil { return types.NewErrEvidenceInvalid(ev, err) } + if evidencePool != nil && evidencePool.IsCommitted(ev) { + return types.NewErrEvidenceInvalid(ev, errors.New("evidence was already committed")) + } } // NOTE: We can't actually verify it's the right proposer because we dont diff --git a/state/validation_test.go b/state/validation_test.go index 12aaf6361..a873855a9 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -121,6 +121,31 @@ func TestValidateBlockEvidence(t *testing.T) { require.True(t, ok) } +// always returns true if asked if any evidence was already committed. +type mockEvPoolAlwaysCommitted struct{} + +func (m mockEvPoolAlwaysCommitted) PendingEvidence(int64) []types.Evidence { return nil } +func (m mockEvPoolAlwaysCommitted) AddEvidence(types.Evidence) error { return nil } +func (m mockEvPoolAlwaysCommitted) Update(*types.Block, State) {} +func (m mockEvPoolAlwaysCommitted) IsCommitted(types.Evidence) bool { return true } + +func TestValidateFailBlockOnCommittedEvidence(t *testing.T) { + var height int64 = 1 + state, stateDB := state(1, int(height)) + + blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, mockEvPoolAlwaysCommitted{}) + // A block with a couple pieces of evidence passes. + block := makeBlock(state, height) + addr, _ := state.Validators.GetByIndex(0) + alreadyCommittedEvidence := types.NewMockGoodEvidence(height, 0, addr) + block.Evidence.Evidence = []types.Evidence{alreadyCommittedEvidence} + block.EvidenceHash = block.Evidence.Hash() + err := blockExec.ValidateBlock(state, block) + + require.Error(t, err) + require.IsType(t, err, &types.ErrEvidenceInvalid{}) +} + /* TODO(#2589): - test unmarshalling BlockParts that are too big into a Block that From 4f2ef3670143e8bc46fc76e734be80416053cc16 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 8 Feb 2019 18:40:41 -0500 Subject: [PATCH 043/259] types.NewCommit (#3275) * types.NewCommit * use types.NewCommit everywhere * fix log in unsafe_reset * memoize height and round in constructor * notes about deprecating toVote * bring back memoizeHeightRound --- blockchain/reactor_test.go | 4 +-- blockchain/store_test.go | 7 ++-- .../commands/reset_priv_validator.go | 2 +- consensus/replay_test.go | 6 ++-- consensus/state.go | 2 +- consensus/types/round_state_test.go | 7 ++-- lite/helpers.go | 8 ++--- lite/proxy/validate_test.go | 8 ++--- node/node_test.go | 2 +- state/execution_test.go | 4 +-- types/block.go | 34 +++++++++++-------- types/validator_set_test.go | 10 ++---- types/vote_set.go | 5 +-- 13 files changed, 42 insertions(+), 57 deletions(-) diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 138e16222..1e8666f12 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -95,13 +95,13 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals // let's add some blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { - lastCommit := &types.Commit{} + lastCommit := types.NewCommit(types.BlockID{}, nil) if blockHeight > 1 { lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1) lastBlock := blockStore.LoadBlock(blockHeight - 1) vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0]).CommitSig() - lastCommit = &types.Commit{Precommits: []*types.CommitSig{vote}, BlockID: lastBlockMeta.BlockID} + lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{vote}) } thisBlock := makeBlock(blockHeight, state, lastCommit) diff --git a/blockchain/store_test.go b/blockchain/store_test.go index 9abc210b1..931faf6a7 100644 --- a/blockchain/store_test.go +++ b/blockchain/store_test.go @@ -23,11 +23,8 @@ import ( // make a Commit with a single vote containing just the height and a timestamp func makeTestCommit(height int64, timestamp time.Time) *types.Commit { - return &types.Commit{ - Precommits: []*types.CommitSig{ - {Height: height, Timestamp: timestamp}, - }, - } + commitSigs := []*types.CommitSig{{Height: height, Timestamp: timestamp}} + return types.NewCommit(types.BlockID{}, commitSigs) } func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 122c2a725..055a76c51 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -61,7 +61,7 @@ func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) { } else { pv := privval.GenFilePV(privValKeyFile, privValStateFile) pv.Save() - logger.Info("Generated private validator file", "file", "keyFile", privValKeyFile, + logger.Info("Generated private validator file", "keyFile", privValKeyFile, "stateFile", privValStateFile) } } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index e7269254c..297c13c3b 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -537,10 +537,8 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { } case *types.Vote: if p.Type == types.PrecommitType { - thisBlockCommit = &types.Commit{ - BlockID: p.BlockID, - Precommits: []*types.CommitSig{p.CommitSig()}, - } + commitSigs := []*types.CommitSig{p.CommitSig()} + thisBlockCommit = types.NewCommit(p.BlockID, commitSigs) } } } diff --git a/consensus/state.go b/consensus/state.go index 74165801b..c6c49d875 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -954,7 +954,7 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts if cs.Height == 1 { // We're creating a proposal for the first block. // The commit is empty, but not nil. - commit = &types.Commit{} + commit = types.NewCommit(types.BlockID{}, nil) } else if cs.LastCommit.HasTwoThirdsMajority() { // Make the commit from LastCommit commit = cs.LastCommit.MakeCommit() diff --git a/consensus/types/round_state_test.go b/consensus/types/round_state_test.go index cb16f939a..a9bc8e14b 100644 --- a/consensus/types/round_state_test.go +++ b/consensus/types/round_state_test.go @@ -53,11 +53,8 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) { Data: types.Data{ Txs: txs, }, - Evidence: types.EvidenceData{}, - LastCommit: &types.Commit{ - BlockID: blockID, - Precommits: precommits, - }, + Evidence: types.EvidenceData{}, + LastCommit: types.NewCommit(blockID, precommits), } parts := block.MakePartSet(4096) // Random Proposal diff --git a/lite/helpers.go b/lite/helpers.go index 6b18b3514..119797f36 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -80,12 +80,8 @@ func (pkz privKeys) signHeader(header *types.Header, first, last int) *types.Com vote := makeVote(header, vset, pkz[i]) commitSigs[vote.ValidatorIndex] = vote.CommitSig() } - - res := &types.Commit{ - BlockID: types.BlockID{Hash: header.Hash()}, - Precommits: commitSigs, - } - return res + blockID := types.BlockID{Hash: header.Hash()} + return types.NewCommit(blockID, commitSigs) } func makeVote(header *types.Header, valset *types.ValidatorSet, key crypto.PrivKey) *types.Vote { diff --git a/lite/proxy/validate_test.go b/lite/proxy/validate_test.go index 1ce4d667e..dce177d7b 100644 --- a/lite/proxy/validate_test.go +++ b/lite/proxy/validate_test.go @@ -70,7 +70,7 @@ func TestValidateBlock(t *testing.T) { }, signedHeader: types.SignedHeader{ Header: &types.Header{Height: 11}, - Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("0xDEADBEEF")}}, + Commit: types.NewCommit(types.BlockID{Hash: []byte("0xDEADBEEF")}, nil), }, wantErr: "Data hash doesn't match header", }, @@ -81,7 +81,7 @@ func TestValidateBlock(t *testing.T) { }, signedHeader: types.SignedHeader{ Header: &types.Header{Height: 11}, - Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}}, + Commit: types.NewCommit(types.BlockID{Hash: []byte("DEADBEEF")}, nil), }, }, // End Header.Data hash mismatch test @@ -169,7 +169,7 @@ func TestValidateBlockMeta(t *testing.T) { ValidatorsHash: []byte("Tendermint"), Time: testTime2, }, - Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}}, + Commit: types.NewCommit(types.BlockID{Hash: []byte("DEADBEEF")}, nil), }, wantErr: "Headers don't match", }, @@ -188,7 +188,7 @@ func TestValidateBlockMeta(t *testing.T) { ValidatorsHash: []byte("Tendermint-x"), Time: testTime2, }, - Commit: &types.Commit{BlockID: types.BlockID{Hash: []byte("DEADBEEF")}}, + Commit: types.NewCommit(types.BlockID{Hash: []byte("DEADBEEF")}, nil), }, wantErr: "Headers don't match", }, diff --git a/node/node_test.go b/node/node_test.go index 4b4610e10..d7907e88a 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -260,7 +260,7 @@ func TestCreateProposalBlock(t *testing.T) { evidencePool, ) - commit := &types.Commit{} + commit := types.NewCommit(types.BlockID{}, nil) block, _ := blockExec.CreateProposalBlock( height, state, commit, diff --git a/state/execution_test.go b/state/execution_test.go index e0c9b4b9d..8cd90f963 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -79,7 +79,7 @@ func TestBeginBlockValidators(t *testing.T) { } for _, tc := range testCases { - lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: tc.lastCommitPrecommits} + lastCommit := types.NewCommit(prevBlockID, tc.lastCommitPrecommits) // block for height 2 block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) @@ -139,7 +139,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig() commitSig1 := (&types.Vote{ValidatorIndex: 1, Timestamp: now}).CommitSig() commitSigs := []*types.CommitSig{commitSig0, commitSig1} - lastCommit := &types.Commit{BlockID: prevBlockID, Precommits: commitSigs} + lastCommit := types.NewCommit(prevBlockID, commitSigs) for _, tc := range testCases { block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) diff --git a/types/block.go b/types/block.go index ec09fd449..bcaf0725c 100644 --- a/types/block.go +++ b/types/block.go @@ -490,8 +490,8 @@ func (cs *CommitSig) String() string { } // toVote converts the CommitSig to a vote. -// Once CommitSig has fewer fields than vote, -// converting to a Vote will require more information. +// TODO: deprecate for #1648. Converting to Vote will require +// access to ValidatorSet. func (cs *CommitSig) toVote() *Vote { if cs == nil { return nil @@ -509,18 +509,30 @@ type Commit struct { BlockID BlockID `json:"block_id"` Precommits []*CommitSig `json:"precommits"` - // Volatile + // memoized in first call to corresponding method + // NOTE: can't memoize in constructor because constructor + // isn't used for unmarshaling height int64 round int hash cmn.HexBytes bitArray *cmn.BitArray } +// NewCommit returns a new Commit with the given blockID and precommits. +// TODO: memoize ValidatorSet in constructor so votes can be easily reconstructed +// from CommitSig after #1648. +func NewCommit(blockID BlockID, precommits []*CommitSig) *Commit { + return &Commit{ + BlockID: blockID, + Precommits: precommits, + } +} + // VoteSignBytes constructs the SignBytes for the given CommitSig. // The only unique part of the SignBytes is the Timestamp - all other fields // signed over are otherwise the same for all validators. func (commit *Commit) VoteSignBytes(chainID string, cs *CommitSig) []byte { - return cs.toVote().SignBytes(chainID) + return commit.ToVote(cs).SignBytes(chainID) } // memoizeHeightRound memoizes the height and round of the commit using @@ -543,27 +555,20 @@ func (commit *Commit) memoizeHeightRound() { // ToVote converts a CommitSig to a Vote. // If the CommitSig is nil, the Vote will be nil. -// When CommitSig is reduced to contain fewer fields, -// this will need access to the ValidatorSet to properly -// reconstruct the vote. func (commit *Commit) ToVote(cs *CommitSig) *Vote { + // TODO: use commit.validatorSet to reconstruct vote + // and deprecate .toVote return cs.toVote() } // Height returns the height of the commit func (commit *Commit) Height() int64 { - if len(commit.Precommits) == 0 { - return 0 - } commit.memoizeHeightRound() return commit.height } // Round returns the round of the commit func (commit *Commit) Round() int { - if len(commit.Precommits) == 0 { - return 0 - } commit.memoizeHeightRound() return commit.round } @@ -595,9 +600,10 @@ func (commit *Commit) BitArray() *cmn.BitArray { } // GetByIndex returns the vote corresponding to a given validator index. +// Panics if `index >= commit.Size()`. // Implements VoteSetReader. func (commit *Commit) GetByIndex(index int) *Vote { - return commit.Precommits[index].toVote() + return commit.ToVote(commit.Precommits[index]) } // IsCommit returns true if there is at least one vote. diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 04874c198..cdc04d459 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -563,18 +563,12 @@ func TestValidatorSetVerifyCommit(t *testing.T) { sig, err := privKey.Sign(vote.SignBytes(chainID)) assert.NoError(t, err) vote.Signature = sig - commit := &Commit{ - BlockID: blockID, - Precommits: []*CommitSig{vote.CommitSig()}, - } + commit := NewCommit(blockID, []*CommitSig{vote.CommitSig()}) badChainID := "notmychainID" badBlockID := BlockID{Hash: []byte("goodbye")} badHeight := height + 1 - badCommit := &Commit{ - BlockID: blockID, - Precommits: []*CommitSig{nil}, - } + badCommit := NewCommit(blockID, []*CommitSig{nil}) // test some error cases // TODO: test more cases! diff --git a/types/vote_set.go b/types/vote_set.go index 14930da4a..1cd0f2281 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -545,10 +545,7 @@ func (voteSet *VoteSet) MakeCommit() *Commit { for i, v := range voteSet.votes { commitSigs[i] = v.CommitSig() } - return &Commit{ - BlockID: *voteSet.maj23, - Precommits: commitSigs, - } + return NewCommit(*voteSet.maj23, commitSigs) } //-------------------------------------------------------------------------------- From 792b12573eee1a58b574decce2bd62399b0b24c3 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Fri, 8 Feb 2019 18:50:02 -0500 Subject: [PATCH 044/259] Prepare v0.30.0 (#3287) * changelog, upgrading, version * update for evidence fixes * linkify * fix an entry --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++- CHANGELOG_PENDING.md | 7 +------ UPGRADING.md | 23 +++++++++++++++++++++ version/version.go | 8 +++++--- 4 files changed, 77 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0e736bcf..592a7c6b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,52 @@ # Changelog +## v0.30.0 + +*February 8th, 2019* + +This release fixes yet another issue with the proposer selection algorithm. +We hope it's the last one, but we won't be surprised if it's not. +We plan to one day expose the selection algorithm more directly to +the application ([\#3285](https://github.com/tendermint/tendermint/issues/3285)), and even to support randomness ([\#763](https://github.com/tendermint/tendermint/issues/763)). +For more, see issues marked +[proposer-selection](https://github.com/tendermint/tendermint/labels/proposer-selection). + +This release also includes a fix to prevent Tendermint from including the same +piece of evidence in more than one block. This issue was reported by @chengwenxi in our +[bug bounty program](https://hackerone.com/tendermint). + +### BREAKING CHANGES: + +* Apps + - [state] [\#3222](https://github.com/tendermint/tendermint/issues/3222) + Duplicate updates for the same validator are forbidden. Apps must ensure + that a given `ResponseEndBlock.ValidatorUpdates` contains only one entry per pubkey. + +* Go API + - [types] [\#3222](https://github.com/tendermint/tendermint/issues/3222) + Remove `Add` and `Update` methods from `ValidatorSet` in favor of new + `UpdateWithChangeSet`. This allows updates to be applied as a set, instead of + one at a time. + +* Block Protocol + - [state] [\#3286](https://github.com/tendermint/tendermint/issues/3286) Blocks that include already committed evidence are invalid. + +* P2P Protocol + - [consensus] [\#3222](https://github.com/tendermint/tendermint/issues/3222) + Validator updates are applied as a set, instead of one at a time, thus + impacting the proposer priority calculation. This ensures that the proposer + selection algorithm does not depend on the order of updates in + `ResponseEndBlock.ValidatorUpdates`. + +### IMPROVEMENTS: +- [crypto] [\#3279](https://github.com/tendermint/tendermint/issues/3279) Use `btcec.S256().N` directly instead of hard coding a copy. + +### BUG FIXES: +- [state] [\#3222](https://github.com/tendermint/tendermint/issues/3222) Fix validator set updates so they are applied as a set, rather + than one at a time. This makes the proposer selection algorithm independent of + the order of updates in `ResponseEndBlock.ValidatorUpdates`. +- [evidence] [\#3286](https://github.com/tendermint/tendermint/issues/3286) Don't add committed evidence to evidence pool. + ## v0.29.2 *February 7th, 2019* @@ -11,7 +58,7 @@ Special thanks to external contributors on this release: `crypto` packages: - p2p: - Partial fix for MITM attacks on the p2p connection. MITM conditions may - still exist. See \#3010. + still exist. See [\#3010](https://github.com/tendermint/tendermint/issues/3010). - crypto: - Eliminate our fork of `btcd` and use the `btcd/btcec` library directly for native secp256k1 signing. Note we still modify the signature encoding to diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 91eed1882..640985836 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.30 +## v0.31.0 ** @@ -14,8 +14,6 @@ Special thanks to external contributors on this release: * Blockchain Protocol - - [types] Reject blocks which contain already committed evidence - * P2P Protocol ### FEATURES: @@ -23,6 +21,3 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: - - - [evidence] Do not store evidence which was already marked as committed - diff --git a/UPGRADING.md b/UPGRADING.md index dd35ff26c..f3fecb5e0 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,29 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. +## v0.30.0 + +This release contains a breaking change to both the block and p2p protocols, +however it may be compatible with blockchains created with v0.29.0 depending on +the chain history. If your blockchain has not included any pieces of evidence, +or no piece of evidence has been included in more than one block, +and if your application has never returned multiple updates +for the same validator in a single block, then v0.30.0 will work fine with +blockchains created with v0.29.0. + +The p2p protocol change is to fix the proposer selection algorithm again. +Note that proposer selection is purely a p2p concern right +now since the algorithm is only relevant during real time consensus. +This change is thus compatible with v0.29.0, but +all nodes must be upgraded to avoid disagreements on the proposer. + +### Applications + +Applications must ensure they do not return duplicates in +`ResponseEndBlock.ValidatorUpdates`. A pubkey must only appear once per set of +updates. Duplicates will cause irrecoverable failure. If you have a very good +reason why we shouldn't do this, please open an issue. + ## v0.29.0 This release contains some breaking changes to the block and p2p protocols, diff --git a/version/version.go b/version/version.go index b20223c24..37a0da78d 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.29.2" + TMCoreSemVer = "0.30.0" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" @@ -38,10 +38,12 @@ func (p Protocol) Uint64() uint64 { var ( // P2PProtocol versions all p2p behaviour and msgs. - P2PProtocol Protocol = 6 + // This includes proposer selection. + P2PProtocol Protocol = 7 // BlockProtocol versions all block data structures and processing. - BlockProtocol Protocol = 9 + // This includes validity of blocks and state updates. + BlockProtocol Protocol = 10 ) //------------------------------------------------------------------------ From 966b5bdf6ecb89f548a9bffa6c328f918ba7e541 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Mon, 11 Feb 2019 13:21:51 +0100 Subject: [PATCH 045/259] fix failure in TestProposerFrequency (#3293) ``` --- FAIL: TestProposerFrequency (2.50s) panic: empty validator set [recovered] panic: empty validator set goroutine 91 [running]: testing.tRunner.func1(0xc000a98c00) /usr/local/go/src/testing/testing.go:792 +0x6a7 panic(0xeae7e0, 0x11fbb30) /usr/local/go/src/runtime/panic.go:513 +0x1b9 github.com/tendermint/tendermint/types.(*ValidatorSet).RescalePriorities(0xc0000e7380, 0x0) /go/src/github.com/tendermint/tendermint/types/validator_set.go:106 +0x1ac github.com/tendermint/tendermint/state.TestProposerFrequency(0xc000a98c00) /go/src/github.com/tendermint/tendermint/state/state_test.go:335 +0xb44 testing.tRunner(0xc000a98c00, 0x111a4d8) /usr/local/go/src/testing/testing.go:827 +0x163 created by testing.(*T).Run /usr/local/go/src/testing/testing.go:878 +0x65a FAIL github.com/tendermint/tendermint/state 3.139s ``` --- state/state_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/state/state_test.go b/state/state_test.go index 9cbe83424..6d5f8f46b 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -313,12 +313,12 @@ func TestProposerFrequency(t *testing.T) { } } - // some random test cases with up to 300 validators + // some random test cases with up to 100 validators maxVals := 100 maxPower := 1000 nTestCases := 5 for i := 0; i < nTestCases; i++ { - N := cmn.RandInt() % maxVals + N := cmn.RandInt()%maxVals + 1 vals := make([]*types.Validator, N) totalVotePower := int64(0) for j := 0; j < N; j++ { From 7fd51e6ade5bd3d6820acdd53ad443c509a4fc7a Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 11 Feb 2019 16:31:34 +0400 Subject: [PATCH 046/259] make govet linter pass (#3292) * make govet linter pass Refs #3262 * close PipeReader and check for err --- .golangci.yml | 1 - abci/client/grpc_client.go | 22 +++++++++++----------- abci/cmd/abci-cli/abci-cli.go | 1 - blockchain/reactor.go | 2 +- cmd/tendermint/commands/run_node.go | 2 -- consensus/common_test.go | 4 +--- consensus/reactor.go | 2 +- consensus/replay.go | 2 +- consensus/replay_file.go | 2 -- consensus/state.go | 10 +++++----- consensus/types/height_vote_set_test.go | 1 - crypto/encoding/amino/encode_test.go | 2 ++ crypto/secp256k1/secp256k1_nocgo.go | 4 ++-- libs/common/bit_array.go | 2 +- p2p/conn/secret_connection_test.go | 9 ++++----- privval/file.go | 1 - privval/server.go | 2 +- rpc/client/mock/abci.go | 12 ++++++------ rpc/client/rpc_test.go | 2 +- rpc/core/abci.go | 4 ++-- rpc/core/blocks.go | 6 ++++-- rpc/core/consensus.go | 14 ++++++++++---- rpc/core/events.go | 2 +- rpc/core/mempool.go | 2 +- rpc/core/net.go | 6 +++--- state/execution.go | 2 +- tools/tm-monitor/monitor/node.go | 4 ++-- 27 files changed, 61 insertions(+), 62 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 4cae2f76a..0bea5e3e1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -18,7 +18,6 @@ linters: - nakedret - lll - gochecknoglobals - - govet - gocritic - gosec - gochecknoinits diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index 94aabc5e0..d04f42b66 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -129,7 +129,7 @@ func (cli *grpcClient) EchoAsync(msg string) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{Echo: res}}) } func (cli *grpcClient) FlushAsync() *ReqRes { @@ -138,7 +138,7 @@ func (cli *grpcClient) FlushAsync() *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{Flush: res}}) } func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes { @@ -147,7 +147,7 @@ func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{Info: res}}) } func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes { @@ -156,7 +156,7 @@ func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{SetOption: res}}) } func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes { @@ -165,7 +165,7 @@ func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}}) } func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes { @@ -174,7 +174,7 @@ func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{CheckTx: res}}) } func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes { @@ -183,7 +183,7 @@ func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{Query: res}}) } func (cli *grpcClient) CommitAsync() *ReqRes { @@ -192,7 +192,7 @@ func (cli *grpcClient) CommitAsync() *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{Commit: res}}) } func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes { @@ -201,7 +201,7 @@ func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{InitChain: res}}) } func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes { @@ -210,7 +210,7 @@ func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{BeginBlock: res}}) } func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes { @@ -219,7 +219,7 @@ func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes { if err != nil { cli.StopForError(err) } - return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{res}}) + return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{EndBlock: res}}) } func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes { diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index cc3f9c452..8823f7be8 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -394,7 +394,6 @@ func cmdConsole(cmd *cobra.Command, args []string) error { return err } } - return nil } func muxOnCommands(cmd *cobra.Command, pArgs []string) error { diff --git a/blockchain/reactor.go b/blockchain/reactor.go index bed082cd3..847398b7b 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -302,7 +302,7 @@ FOR_LOOP: firstParts := first.MakePartSet(types.BlockPartSizeBytes) firstPartsHeader := firstParts.Header() - firstID := types.BlockID{first.Hash(), firstPartsHeader} + firstID := types.BlockID{Hash: first.Hash(), PartsHeader: firstPartsHeader} // Finally, verify the first block using the second's commit // NOTE: we can probably make this more efficient, but note that calling // first.Hash() doesn't verify the tx contents, so MakePartSet() is diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index ef205aa6b..c720de7d8 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -77,8 +77,6 @@ func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command { // Run forever select {} - - return nil }, } diff --git a/consensus/common_test.go b/consensus/common_test.go index e6e64c252..7e24d3ffa 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -621,7 +621,6 @@ func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { } } panic("didnt find peer in switches") - return -1 } //------------------------------------------------------------------------------- @@ -699,8 +698,7 @@ func (m *mockTicker) Chan() <-chan timeoutInfo { return m.c } -func (mockTicker) SetLogger(log.Logger) { -} +func (*mockTicker) SetLogger(log.Logger) {} //------------------------------------ diff --git a/consensus/reactor.go b/consensus/reactor.go index b92ae1f72..604e54b49 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -896,7 +896,7 @@ type PeerState struct { peer p2p.Peer logger log.Logger - mtx sync.Mutex `json:"-"` // NOTE: Modify below using setters, never directly. + mtx sync.Mutex // NOTE: Modify below using setters, never directly. PRS cstypes.PeerRoundState `json:"round_state"` // Exposed. Stats *peerStateStats `json:"stats"` // Exposed. } diff --git a/consensus/replay.go b/consensus/replay.go index 21fef6b29..5453c700e 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -334,7 +334,7 @@ func (h *Handshaker) ReplayBlocks( } else if storeBlockHeight < appBlockHeight { // the app should never be ahead of the store (but this is under app's control) - return appHash, sm.ErrAppBlockHeightTooHigh{storeBlockHeight, appBlockHeight} + return appHash, sm.ErrAppBlockHeightTooHigh{CoreHeight: storeBlockHeight, AppHeight: appBlockHeight} } else if storeBlockHeight < stateBlockHeight { // the state should never be ahead of the store (this is under tendermint's control) diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 3e92bad67..15246d221 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -103,7 +103,6 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error { } pb.count++ } - return nil } //------------------------------------------------ @@ -295,7 +294,6 @@ func (pb *playback) replayConsoleLoop() int { fmt.Println(pb.count) } } - return 0 } //-------------------------------------------------------------------------------- diff --git a/consensus/state.go b/consensus/state.go index c6c49d875..940318c0b 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -909,7 +909,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) { } // Make proposal - propBlockId := types.BlockID{block.Hash(), blockParts.Header()} + propBlockId := types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()} proposal := types.NewProposal(height, round, cs.ValidRound, propBlockId) if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal); err == nil { @@ -1320,7 +1320,7 @@ func (cs *ConsensusState) finalizeCommit(height int64) { // Execute and commit the block, update and save the state, and update the mempool. // NOTE The block.AppHash wont reflect these txs until the next block. var err error - stateCopy, err = cs.blockExec.ApplyBlock(stateCopy, types.BlockID{block.Hash(), blockParts.Header()}, block) + stateCopy, err = cs.blockExec.ApplyBlock(stateCopy, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()}, block) if err != nil { cs.Logger.Error("Error on ApplyBlock. Did the application crash? Please restart tendermint", "err", err) err := cmn.Kill() @@ -1543,7 +1543,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, } cs.Logger.Info(fmt.Sprintf("Added to lastPrecommits: %v", cs.LastCommit.StringShort())) - cs.eventBus.PublishEventVote(types.EventDataVote{vote}) + cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}) cs.evsw.FireEvent(types.EventVote, vote) // if we can skip timeoutCommit and have all the votes now, @@ -1571,7 +1571,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, return } - cs.eventBus.PublishEventVote(types.EventDataVote{vote}) + cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}) cs.evsw.FireEvent(types.EventVote, vote) switch vote.Type { @@ -1683,7 +1683,7 @@ func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, heade Round: cs.Round, Timestamp: cs.voteTime(), Type: type_, - BlockID: types.BlockID{hash, header}, + BlockID: types.BlockID{Hash: hash, PartsHeader: header}, } err := cs.privValidator.SignVote(cs.state.ChainID, vote) return vote, err diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index 4460cd3ec..afb741626 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -64,7 +64,6 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali err := privVal.SignVote(chainID, vote) if err != nil { panic(fmt.Sprintf("Error signing vote: %v", err)) - return nil } return vote } diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index 955103069..d4e349454 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -47,6 +47,8 @@ func checkAminoJSON(t *testing.T, src interface{}, dst interface{}, isNil bool) require.Nil(t, err, "%+v", err) } +// ExamplePrintRegisteredTypes refers to unknown identifier: PrintRegisteredTypes +//nolint:govet func ExamplePrintRegisteredTypes() { cdc.PrintTypes(os.Stdout) // Output: | Type | Name | Prefix | Length | Notes | diff --git a/crypto/secp256k1/secp256k1_nocgo.go b/crypto/secp256k1/secp256k1_nocgo.go index cd1655a5c..18782b375 100644 --- a/crypto/secp256k1/secp256k1_nocgo.go +++ b/crypto/secp256k1/secp256k1_nocgo.go @@ -52,8 +52,8 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sigStr []byte) bool { // that len(sigStr) == 64. func signatureFromBytes(sigStr []byte) *secp256k1.Signature { return &secp256k1.Signature{ - new(big.Int).SetBytes(sigStr[:32]), - new(big.Int).SetBytes(sigStr[32:64]), + R: new(big.Int).SetBytes(sigStr[:32]), + S: new(big.Int).SetBytes(sigStr[32:64]), } } diff --git a/libs/common/bit_array.go b/libs/common/bit_array.go index ebd6cc4a0..2e6c255cc 100644 --- a/libs/common/bit_array.go +++ b/libs/common/bit_array.go @@ -412,6 +412,6 @@ func (bA *BitArray) UnmarshalJSON(bz []byte) error { bA2.SetIndex(i, true) } } - *bA = *bA2 + *bA = *bA2 //nolint:govet return nil } diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 6b2854767..5c389ee3a 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -238,6 +238,10 @@ func TestSecretConnectionReadWrite(t *testing.T) { for { n, err := nodeSecretConn.Read(readBuffer) if err == io.EOF { + if err := nodeConn.PipeReader.Close(); err != nil { + t.Error(err) + return nil, err, true + } return nil, nil, false } else if err != nil { t.Errorf("Failed to read from nodeSecretConn: %v", err) @@ -245,11 +249,6 @@ func TestSecretConnectionReadWrite(t *testing.T) { } *nodeReads = append(*nodeReads, string(readBuffer[:n])) } - if err := nodeConn.PipeReader.Close(); err != nil { - t.Error(err) - return nil, err, true - } - return nil, nil, false }, ) assert.True(t, ok, "Unexpected task abortion") diff --git a/privval/file.go b/privval/file.go index d27d7a788..8eb38e806 100644 --- a/privval/file.go +++ b/privval/file.go @@ -31,7 +31,6 @@ func voteToStep(vote *types.Vote) int8 { return stepPrecommit default: panic("Unknown vote type") - return 0 } } diff --git a/privval/server.go b/privval/server.go index 8b22c69e9..cce659525 100644 --- a/privval/server.go +++ b/privval/server.go @@ -67,7 +67,7 @@ func DialTCPFn(addr string, connTimeout time.Duration, privKey ed25519.PrivKeyEd // DialUnixFn dials the given unix socket. func DialUnixFn(addr string) Dialer { return func() (net.Conn, error) { - unixAddr := &net.UnixAddr{addr, "unix"} + unixAddr := &net.UnixAddr{Name: addr, Net: "unix"} return net.DialUnix("unix", nil, unixAddr) } } diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index e63d22e0c..2ab62a420 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -23,7 +23,7 @@ var ( ) func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) { - return &ctypes.ResultABCIInfo{a.App.Info(proxy.RequestInfo)}, nil + return &ctypes.ResultABCIInfo{Response: a.App.Info(proxy.RequestInfo)}, nil } func (a ABCIApp) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { @@ -37,7 +37,7 @@ func (a ABCIApp) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts clien Height: opts.Height, Prove: opts.Prove, }) - return &ctypes.ResultABCIQuery{q}, nil + return &ctypes.ResultABCIQuery{Response: q}, nil } // NOTE: Caller should call a.App.Commit() separately, @@ -60,7 +60,7 @@ func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error if !c.IsErr() { go func() { a.App.DeliverTx(tx) }() // nolint: errcheck } - return &ctypes.ResultBroadcastTx{c.Code, c.Data, c.Log, tx.Hash()}, nil + return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil } func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { @@ -69,7 +69,7 @@ func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) if !c.IsErr() { go func() { a.App.DeliverTx(tx) }() // nolint: errcheck } - return &ctypes.ResultBroadcastTx{c.Code, c.Data, c.Log, tx.Hash()}, nil + return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil } // ABCIMock will send all abci related request to the named app, @@ -87,7 +87,7 @@ func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) { if err != nil { return nil, err } - return &ctypes.ResultABCIInfo{res.(abci.ResponseInfo)}, nil + return &ctypes.ResultABCIInfo{Response: res.(abci.ResponseInfo)}, nil } func (m ABCIMock) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { @@ -100,7 +100,7 @@ func (m ABCIMock) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts clie return nil, err } resQuery := res.(abci.ResponseQuery) - return &ctypes.ResultABCIQuery{resQuery}, nil + return &ctypes.ResultABCIQuery{Response: resQuery}, nil } func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 8ae88f43b..d3dc90b85 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -42,9 +42,9 @@ func TestCorsEnabled(t *testing.T) { req.Header.Set("Origin", origin) c := &http.Client{} resp, err := c.Do(req) + require.Nil(t, err, "%+v", err) defer resp.Body.Close() - require.Nil(t, err, "%+v", err) assert.Equal(t, resp.Header.Get("Access-Control-Allow-Origin"), origin) } diff --git a/rpc/core/abci.go b/rpc/core/abci.go index c9d516f97..aa6089b60 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -63,7 +63,7 @@ func ABCIQuery(path string, data cmn.HexBytes, height int64, prove bool) (*ctype return nil, err } logger.Info("ABCIQuery", "path", path, "data", data, "result", resQuery) - return &ctypes.ResultABCIQuery{*resQuery}, nil + return &ctypes.ResultABCIQuery{Response: *resQuery}, nil } // Get some info about the application. @@ -101,5 +101,5 @@ func ABCIInfo() (*ctypes.ResultABCIInfo, error) { if err != nil { return nil, err } - return &ctypes.ResultABCIInfo{*resInfo}, nil + return &ctypes.ResultABCIInfo{Response: *resInfo}, nil } diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index ee4009e5c..906aea7be 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -85,7 +85,9 @@ func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, e blockMetas = append(blockMetas, blockMeta) } - return &ctypes.ResultBlockchainInfo{blockStore.Height(), blockMetas}, nil + return &ctypes.ResultBlockchainInfo{ + LastHeight: blockStore.Height(), + BlockMetas: blockMetas}, nil } // error if either min or max are negative or min < max @@ -233,7 +235,7 @@ func Block(heightPtr *int64) (*ctypes.ResultBlock, error) { blockMeta := blockStore.LoadBlockMeta(height) block := blockStore.LoadBlock(height) - return &ctypes.ResultBlock{blockMeta, block}, nil + return &ctypes.ResultBlock{BlockMeta: blockMeta, Block: block}, nil } // Get block commit at a given height. diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 9968a1b2a..81694b7ee 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -60,7 +60,9 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) { if err != nil { return nil, err } - return &ctypes.ResultValidators{height, validators.Validators}, nil + return &ctypes.ResultValidators{ + BlockHeight: height, + Validators: validators.Validators}, nil } // DumpConsensusState dumps consensus state. @@ -223,7 +225,9 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { if err != nil { return nil, err } - return &ctypes.ResultDumpConsensusState{roundState, peerStates}, nil + return &ctypes.ResultDumpConsensusState{ + RoundState: roundState, + Peers: peerStates}, nil } // ConsensusState returns a concise summary of the consensus state. @@ -276,7 +280,7 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { func ConsensusState() (*ctypes.ResultConsensusState, error) { // Get self round state. bz, err := consensusState.GetRoundStateSimpleJSON() - return &ctypes.ResultConsensusState{bz}, err + return &ctypes.ResultConsensusState{RoundState: bz}, err } // Get the consensus parameters at the given block height. @@ -327,5 +331,7 @@ func ConsensusParams(heightPtr *int64) (*ctypes.ResultConsensusParams, error) { if err != nil { return nil, err } - return &ctypes.ResultConsensusParams{BlockHeight: height, ConsensusParams: consensusparams}, nil + return &ctypes.ResultConsensusParams{ + BlockHeight: height, + ConsensusParams: consensusparams}, nil } diff --git a/rpc/core/events.go b/rpc/core/events.go index e4fd20417..21979f018 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -109,7 +109,7 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscri go func() { for event := range ch { - tmResult := &ctypes.ResultEvent{query, event.(tmtypes.TMEventData)} + tmResult := &ctypes.ResultEvent{Query: query, Data: event.(tmtypes.TMEventData)} wsCtx.TryWriteRPCResponse(rpctypes.NewRPCSuccessResponse(wsCtx.Codec(), rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", wsCtx.Request.ID)), tmResult)) } }() diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index ff6b029cf..d6dcc93e4 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -275,7 +275,7 @@ func UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { limit = validatePerPage(limit) txs := mempool.ReapMaxTxs(limit) - return &ctypes.ResultUnconfirmedTxs{len(txs), txs}, nil + return &ctypes.ResultUnconfirmedTxs{N: len(txs), Txs: txs}, nil } // Get number of unconfirmed transactions. diff --git a/rpc/core/net.go b/rpc/core/net.go index 4d95c2ef0..56e9624d7 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -77,7 +77,7 @@ func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { if err != nil { return &ctypes.ResultDialSeeds{}, err } - return &ctypes.ResultDialSeeds{"Dialing seeds in progress. See /net_info for details"}, nil + return &ctypes.ResultDialSeeds{Log: "Dialing seeds in progress. See /net_info for details"}, nil } func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { @@ -90,7 +90,7 @@ func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, if err != nil { return &ctypes.ResultDialPeers{}, err } - return &ctypes.ResultDialPeers{"Dialing peers in progress. See /net_info for details"}, nil + return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil } // Get genesis file. @@ -136,5 +136,5 @@ func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, // } // ``` func Genesis() (*ctypes.ResultGenesis, error) { - return &ctypes.ResultGenesis{genDoc}, nil + return &ctypes.ResultGenesis{Genesis: genDoc}, nil } diff --git a/state/execution.go b/state/execution.go index 8ab95839e..c4b94fb9f 100644 --- a/state/execution.go +++ b/state/execution.go @@ -446,7 +446,7 @@ func fireEvents(logger log.Logger, eventBus types.BlockEventPublisher, block *ty }) for i, tx := range block.Data.Txs { - eventBus.PublishEventTx(types.EventDataTx{types.TxResult{ + eventBus.PublishEventTx(types.EventDataTx{TxResult: types.TxResult{ Height: block.Height, Index: uint32(i), Tx: tx, diff --git a/tools/tm-monitor/monitor/node.go b/tools/tm-monitor/monitor/node.go index 6f7051456..c16f06098 100644 --- a/tools/tm-monitor/monitor/node.go +++ b/tools/tm-monitor/monitor/node.go @@ -21,8 +21,8 @@ const maxRestarts = 25 type Node struct { rpcAddr string - IsValidator bool `json:"is_validator"` // validator or non-validator? - pubKey crypto.PubKey `json:"pub_key"` + IsValidator bool `json:"is_validator"` // validator or non-validator? + pubKey crypto.PubKey Name string `json:"name"` Online bool `json:"online"` From b089587b42e7d4a6f7e78971fb4bfadcadb798d0 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 12 Feb 2019 05:54:12 +0100 Subject: [PATCH 047/259] make gosec linter pass (#3294) * not related to linter: remove obsolete constants: - `Insecure` and `Secure` and type `Security` are not used anywhere * not related to linter: update example - NewInsecure was deleted; change example to NewRemoteDB * address: Binds to all network interfaces (gosec): - bind to localhost instead of 0.0.0.0 - regenerate test key and cert for this purpose (was valid for ::) and otherwise we would see: transport: authentication handshake failed: x509: certificate is valid for ::, not 127.0.0.1\" (used https://github.com/google/keytransparency/blob/master/scripts/gen_server_keys.sh to regenerate certs) * use sha256 in tests instead of md5; time difference is negligible * nolint usage of math/rand in test and add comment on its import - crypto/rand is slower and we do not need sth more secure in tests * enable linter in circle-ci * another nolint math/rand in test * replace another occurrence of md5 * consistent comment about importing math/rand --- .golangci.yml | 1 - crypto/merkle/proof_key_path_test.go | 4 ++- libs/db/remotedb/doc.go | 2 +- libs/db/remotedb/grpcdb/client.go | 8 ----- libs/db/remotedb/remotedb_test.go | 2 +- libs/db/remotedb/test.crt | 40 ++++++++++------------ libs/db/remotedb/test.key | 50 ++++++++++++++-------------- mempool/mempool_test.go | 4 +-- tools/tm-bench/transacter.go | 13 +++++--- tools/tm-bench/transacter_test.go | 6 ++-- types/block_test.go | 6 ++-- 11 files changed, 64 insertions(+), 72 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 0bea5e3e1..45cabe201 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -19,7 +19,6 @@ linters: - lll - gochecknoglobals - gocritic - - gosec - gochecknoinits - scopelint - stylecheck diff --git a/crypto/merkle/proof_key_path_test.go b/crypto/merkle/proof_key_path_test.go index 48fda3032..34c918f4a 100644 --- a/crypto/merkle/proof_key_path_test.go +++ b/crypto/merkle/proof_key_path_test.go @@ -1,6 +1,8 @@ package merkle import ( + // it is ok to use math/rand here: we do not need a cryptographically secure random + // number generator here and we can run the tests a bit faster "math/rand" "testing" @@ -24,7 +26,7 @@ func TestKeyPath(t *testing.T) { keys[i][j] = alphanum[rand.Intn(len(alphanum))] } case KeyEncodingHex: - rand.Read(keys[i]) + rand.Read(keys[i]) //nolint: gosec default: panic("Unexpected encoding") } diff --git a/libs/db/remotedb/doc.go b/libs/db/remotedb/doc.go index 07c95a56a..93d9c8a29 100644 --- a/libs/db/remotedb/doc.go +++ b/libs/db/remotedb/doc.go @@ -11,7 +11,7 @@ remotedb's RemoteDB implements db.DB so can be used normally like other databases. One just has to explicitly connect to the remote database with a client setup such as: - client, err := remotedb.NewInsecure(addr) + client, err := remotedb.NewRemoteDB(addr, cert) // Make sure to invoke InitRemote! if err := client.InitRemote(&remotedb.Init{Name: "test-remote-db", Type: "leveldb"}); err != nil { log.Fatalf("Failed to initialize the remote db") diff --git a/libs/db/remotedb/grpcdb/client.go b/libs/db/remotedb/grpcdb/client.go index e11b7839b..b3c69ff23 100644 --- a/libs/db/remotedb/grpcdb/client.go +++ b/libs/db/remotedb/grpcdb/client.go @@ -7,14 +7,6 @@ import ( protodb "github.com/tendermint/tendermint/libs/db/remotedb/proto" ) -// Security defines how the client will talk to the gRPC server. -type Security uint - -const ( - Insecure Security = iota - Secure -) - // NewClient creates a gRPC client connected to the bound gRPC server at serverAddr. // Use kind to set the level of security to either Secure or Insecure. func NewClient(serverAddr, serverCert string) (protodb.DBClient, error) { diff --git a/libs/db/remotedb/remotedb_test.go b/libs/db/remotedb/remotedb_test.go index 0e7319971..f5c8e2cb5 100644 --- a/libs/db/remotedb/remotedb_test.go +++ b/libs/db/remotedb/remotedb_test.go @@ -14,7 +14,7 @@ import ( func TestRemoteDB(t *testing.T) { cert := "test.crt" key := "test.key" - ln, err := net.Listen("tcp", "0.0.0.0:0") + ln, err := net.Listen("tcp", "localhost:0") require.Nil(t, err, "expecting a port to have been assigned on which we can listen") srv, err := grpcdb.NewServer(cert, key) require.Nil(t, err) diff --git a/libs/db/remotedb/test.crt b/libs/db/remotedb/test.crt index bdc8a0f29..06ffec1d2 100644 --- a/libs/db/remotedb/test.crt +++ b/libs/db/remotedb/test.crt @@ -1,25 +1,19 @@ -----BEGIN CERTIFICATE----- -MIIEQTCCAimgAwIBAgIRANqF1HD19i/uvQ3n62TAKTwwDQYJKoZIhvcNAQELBQAw -GTEXMBUGA1UEAxMOdGVuZGVybWludC5jb20wHhcNMTgwNzAyMDMwNzMyWhcNMjAw -MTAyMDMwNzMwWjANMQswCQYDVQQDEwI6OjCCASIwDQYJKoZIhvcNAQEBBQADggEP -ADCCAQoCggEBAOuWUMCSzYJmvKU1vsouDTe7OxnPWO3oV0FjSH8vKYoi2zpZQX35 -dQDPtLDF2/v/ANZJ5pzMJR8yMMtEQ4tWxKuGzJw1ZgTgHtASPbj/M5fDnDO7Hqg4 -D09eLTkZAUfiBf6BzDyQIHn22CUexhaS70TbIT9AOAoOsGXMZz9d+iImKIm+gbzf -pR52LNbBGesHWGjwIuGF4InstIMsKSwGv2DctzhWI+i/m5Goi3rd1V8z/lzUbsf1 -0uXqQcSfTyv3ee6YiCWj2W8vcdc5H+B6KzSlGjAR4sRcHTHOQJYO9BgA9evQ3qsJ -Pp00iez13RdheJWPtbfUqQy4gdpu8HFeZx8CAwEAAaOBjzCBjDAOBgNVHQ8BAf8E -BAMCA7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRc -XBo+bJILrLcJiGkTWeMPpXb1TDAfBgNVHSMEGDAWgBQqk1Xu65Ww7EBCROw4KLGw -KuToaDAbBgNVHREEFDAShxAAAAAAAAAAAAAAAAAAAAAAMA0GCSqGSIb3DQEBCwUA -A4ICAQAbGsIMhL8clczNmhGl9xZhmyNz6FbLq6g163x9LTgfvwHPt+7urthtd++O -uy4Ut8zFurh/yk7eooPlzf8jO7QUJBAFVy4vj8IcsvpWbFa7cuEOIulbjIzyAm/v -lgy7vUQ6xrWn8x8O9K1ww9z7wugwCyl22BD0wSHZKclJz++AwpL6vUVOD76IIuJO -+S6bE6z26/0ndpundh2AkA++2eIleD6ygnTeTl0PWu6aGoCggBmos50f8KgYHZF/ -OZVef203kDls9xCaOiMzaU91VsgLqq/gNcT+2cBd5r3IZTY3C8Rve6EEHS+/4zxf -PKlmiLN7lU9GFZogKecYzY+zPT7OArY7OVFnGTo4qdhdmxnXzHsI+anMCjxLOgEJ -381hyplQGPQOouEupCBxFcwa7oMYoGu20+1nLWYEqFcIXCeyH+s77MyteJSsseqL -xivG5PT+jKJn9hrnFb39bBmht9Vsa+Th6vk953zi5wCSe1j2wXsxFaENDq6BQZOK -f86Kp86M2elYnv3lJ3j2DE2ZTMpw+PA5ThYUnB+HVqYeeB2Y3ErRS8P1FOp1LBE8 -+eTz7yXQO5OM2wdYhNNL1zDri/41fHXi9b6337PZVqc39GM+N74x/O4Q7xEBiWgQ -T0dT8SNwf55kv63MeZh63ImxFV0FNRkCteYLcJMle3ohIY4zyQ== +MIIDAjCCAeqgAwIBAgIJAOGCVedOwRbOMA0GCSqGSIb3DQEBBQUAMCExCzAJBgNV +BAYTAlVTMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkwMjExMTU0NjQ5WhcNMjAw +MjExMTU0NjQ5WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJbG9jYWxob3N0MIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA60S/fNUWoHm1PYI/yrlnZNtr +dRqDORHe0hPwl/lttLz7+a7HzQZFnpiXnuxbDJtpIq/h1vhAl0sFy86Ip26LhbWc +GjxJL24tVwiOwqYRzTPZ/rK3JYuNcIvcztXjMqdzPrHSZy5YZgrQB6yhTiqpBc4D +h/XgWjEt4DhpHwf/zuIK9XkJw0IaTWjFmoyKRoWW3q4bHzoKNxS9bXP117Tz7tn0 +AdsQCjt1GKcIROkcOGUHqByINJ2XlBkb7SQPjQVBLDVJKdRDUt+yHkkdbn97UDhq +HRTCt5UELWs/53Gj1ffNuhjECOVjG1HkZweLgZjJRQYe8X2OOLNOyfVY1KsDnQID +AQABoz0wOzAMBgNVHRMEBTADAQH/MCsGA1UdEQQkMCKCCWxvY2FsaG9zdIIJbG9j +YWxob3N0hwQAAAAAhwR/AAABMA0GCSqGSIb3DQEBBQUAA4IBAQCe2A5gDc3jiZwT +a5TJrc2J2KouqxB/PCddw5VY8jPsZJfsr9gxHi+Xa5g8p3oqmEOIlqM5BVhrZRUG +RWHDmL+bCsuzMoA/vGHtHmUIwLeZQLWgT3kv12Dc8M9flNNjmXWxdMR9lOMwcL83 +F0CdElxSmaEbNvCIJBDetJJ7vMCqS2lnTLWurbH4ZGeGwvjzNgpgGCKwbyK/gU+j +UXiTQbVvPQ3WWACDnfH6rg0TpxU9jOBkd+4/9tUrBG7UclQBfGULk3sObLO9kx4N +8RxJmtp8jljIXVPX3udExI05pz039pAgvaeZWtP17QSbYcKF1jFtKo6ckrv2GKXX +M5OXGXdw -----END CERTIFICATE----- diff --git a/libs/db/remotedb/test.key b/libs/db/remotedb/test.key index 14d285584..e1adb3e1e 100644 --- a/libs/db/remotedb/test.key +++ b/libs/db/remotedb/test.key @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEpgIBAAKCAQEA65ZQwJLNgma8pTW+yi4NN7s7Gc9Y7ehXQWNIfy8piiLbOllB -ffl1AM+0sMXb+/8A1knmnMwlHzIwy0RDi1bEq4bMnDVmBOAe0BI9uP8zl8OcM7se -qDgPT14tORkBR+IF/oHMPJAgefbYJR7GFpLvRNshP0A4Cg6wZcxnP136IiYoib6B -vN+lHnYs1sEZ6wdYaPAi4YXgiey0gywpLAa/YNy3OFYj6L+bkaiLet3VXzP+XNRu -x/XS5epBxJ9PK/d57piIJaPZby9x1zkf4HorNKUaMBHixFwdMc5Alg70GAD169De -qwk+nTSJ7PXdF2F4lY+1t9SpDLiB2m7wcV5nHwIDAQABAoIBAQCB2/ilPgaUE8d2 -ldqWHa5hgw4/2uCdO04ll/GVUczm/PG1BxAnvYL2MIfcTSRGkrjGZjP9SDZKLONi -mD1XKDv+hK5yiKi0lUnGzddCC0JILKYEieeLOGOQD0yERblEA13kfW20EIomUJ+y -TnVIajQD03pPIDoDqTco1fQvpMDFYw5Q//UhH7VBC261GO1akvhT2Gqdb4aKLaYQ -iDW9IEButL5cRKIJuRxToB/JbmPVEF7xIZtm0sf9dtYVOlBQLeID0uHXgaci0enc -de6GMajmj7NFqc36ypb+Ct18fqEwQBYD+TSQdKs7/lMsAXwRjd5HW4RbYiMZyYnf -Dxgh7QVBAoGBAP9aLLIUcIG7+gk1x7xd+8wRhfo+dhsungeCluSigI9AsfDr6dpR -G9/0lEJH56noZZKQueACTmj7shmRB40xFFLc8w0IDRZCnofsl+Z15k9K84uFPA3W -hdZH9nMieU/mRKdcUYK7pHGqbicHTaJQ5ydZ+xb2E+zYQHOzYpQacHv/AoGBAOwv -TjDZSiassnAPYmmfcHtkUF4gf7PTpiZfH0hXHGAb0mJX4cXAoktAeDeHSi2tz3LW -dAc0ReP8Pdf3uSNv7wkJ1KpNRxAhU5bhnDFmjRc7gMZknVOU+az2M+4yGOn/SOiJ -I6uMHgQDS/VsI+N583n6gbGxVHbQfr9TOc4bLpThAoGBAKin0JmWMnEdzRnEMbZS -hPrWIB2Wn794XNws/qjoQ+1aF60+xGhz5etXyYy1nWd1nZDekkZIf62LgKiuR8ST -xA6u7MGQrcQkID06oWGQQZvhr1ZZm76wEBnl0ftdq66AMpwvt46XjReeL78LbdVl -hidRoSwbQDHQ61EADH4xsFXVAoGBAISXqhXSZsZ/fU1b1avmTod3MYcmR4r07vnr -vOwnu05ZUCrVm3IhSvtkHhlOYl5yjVuy+UByICp1mWJ9N/qlBFTWqAVTjOmJTBwQ -XFd/cwXv6cN3CLu7js+DCHRYu5PiNVQWaWgNKWynTSViqGM0O3PnJphTLU/mjMFs -P69toyEBAoGBALh9YsqxHdYdS5WK9chzDfGlaTQ79jwN+gEzQuP1ooLF0JkMgh5W -//2C6kCrgBsGTm1gfHAjEfC04ZDZLFbKLm56YVKUGL6JJNapm6e5kfiZGjbRKWAg -ViCeRS2qQnVbH74GfHyimeTPDI9cJMiJfDDTPbfosqWSsPEcg2jfsySJ +MIIEogIBAAKCAQEA60S/fNUWoHm1PYI/yrlnZNtrdRqDORHe0hPwl/lttLz7+a7H +zQZFnpiXnuxbDJtpIq/h1vhAl0sFy86Ip26LhbWcGjxJL24tVwiOwqYRzTPZ/rK3 +JYuNcIvcztXjMqdzPrHSZy5YZgrQB6yhTiqpBc4Dh/XgWjEt4DhpHwf/zuIK9XkJ +w0IaTWjFmoyKRoWW3q4bHzoKNxS9bXP117Tz7tn0AdsQCjt1GKcIROkcOGUHqByI +NJ2XlBkb7SQPjQVBLDVJKdRDUt+yHkkdbn97UDhqHRTCt5UELWs/53Gj1ffNuhjE +COVjG1HkZweLgZjJRQYe8X2OOLNOyfVY1KsDnQIDAQABAoIBAAb5n8+8pZIWaags +L2X8PzN/Sd1L7u4HOJrz2mM3EuiT3ciWRPgwImpETeJ5UW27Qc+0dTahX5DcuYxE +UErefSZ2ru0cMnNEifWVnF3q/IYf7mudss5bJ9NZYi+Dqdu7mTAXp4xFlHtaALbp +iFK/8wjoBbTHNmKWKK0IHx27Z/sjK+7QnoKij+rRzvhmNyN2r3dT7EO4VePriesr +zyVaGexNPFhtd1HLJLQ5GqRAidtLM4x1ubvp3NLTCvvoQKKYFOg7WqKycZ2VllOg +ApcpZb/kB/sNTacLvum5HgMNWuWwgREISuQJR+esz/5WaSTQ04L2+vMVomGM18X+ +9n4KYwECgYEA/Usajzl3tWv1IIairSk9Md7Z2sbaPVBNKv4IDJy3mLwt+2VN2mqo +fpeV5rBaFNWzJR0M0JwLbdlsvSfXgVFkUePg1UiJyFqOKmMO8Bd/nxV9NAewVg1D +KXQLsfrojBfka7HtFmfk/GA2swEMCGzUcY23bwah1JUTLhvbl19GNMECgYEA7chW +Ip/IvYBiaaD/qgklwJE8QoAVzi9zqlI1MOJJNf1r/BTeZ2R8oXlRk8PVxFglliuA +vMgwCkfuqxA8irIdHReLzqcLddPtaHo6R8zKP2cpYBo61C3CPzEAucasaOXQFpjs +DPnp4QFeboNPgiEGLVGHFvD5TwZpideBpWTwud0CgYEAy04MDGfJEQKNJ0VJr4mJ +R80iubqgk1QwDFEILu9fYiWxFrbSTX0Mr0eGlzp3o39/okt17L9DYTGCWTVwgajN +x/kLjsYBaaJdt+H4rHeABTWfYDLHs9pDTTOK65mELGZE/rg6n6BWqMelP/qYKO8J +efeRA3mkTVg2o+zSTea4GEECgYEA3DB4EvgD2/fXKhl8puhxnTDgrHQPvS8T3NTj +jLD/Oo/CP1zT1sqm3qCJelwOyBMYO0dtn2OBmQOjb6VJauYlL5tuS59EbYgigG0v +Ku3pG21cUzH26CS3i+zEz0O6xCiL2WEitaF3gnTSDWRrbAVIww6MGiJru1IkyRBX +beFbScECf1n00W9qrXnqsWefk73ucggfV0gQQmDnauMA9J7B96+MvGprE54Tx9vl +SBodgvJsCod9Y9Q7QsMcXb4CuEgTgWKDBp5cA/KUOQmK5buOrysosLnnm12LaHiF +O7IIh8Cmb9TbdldgW+8ndZ4EQ3lfIS0zN3/7rWD34bs19JDYkRY= -----END RSA PRIVATE KEY----- diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 9d21e734f..dd624262d 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -1,8 +1,8 @@ package mempool import ( - "crypto/md5" "crypto/rand" + "crypto/sha256" "encoding/binary" "fmt" "io/ioutil" @@ -451,7 +451,7 @@ func TestMempoolMaxMsgSize(t *testing.T) { } func checksumIt(data []byte) string { - h := md5.New() + h := sha256.New() h.Write(data) return fmt.Sprintf("%x", h.Sum(nil)) } diff --git a/tools/tm-bench/transacter.go b/tools/tm-bench/transacter.go index c20aa5b5b..3347fdfb0 100644 --- a/tools/tm-bench/transacter.go +++ b/tools/tm-bench/transacter.go @@ -1,11 +1,14 @@ package main import ( - "crypto/md5" + "crypto/sha256" "encoding/binary" "encoding/hex" "encoding/json" "fmt" + + // it is ok to use math/rand here: we do not need a cryptographically secure random + // number generator here and we can run the tests a bit faster "math/rand" "net" "net/http" @@ -154,12 +157,12 @@ func (t *transacter) sendLoop(connIndex int) { }() // hash of the host name is a part of each tx - var hostnameHash [md5.Size]byte + var hostnameHash [sha256.Size]byte hostname, err := os.Hostname() if err != nil { hostname = "127.0.0.1" } - hostnameHash = md5.Sum([]byte(hostname)) + hostnameHash = sha256.Sum256([]byte(hostname)) // each transaction embeds connection index, tx number and hash of the hostname // we update the tx number between successive txs tx := generateTx(connIndex, txNumber, t.Size, hostnameHash) @@ -257,7 +260,7 @@ func connect(host string) (*websocket.Conn, *http.Response, error) { return websocket.DefaultDialer.Dial(u.String(), nil) } -func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [md5.Size]byte) []byte { +func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [sha256.Size]byte) []byte { tx := make([]byte, txSize) binary.PutUvarint(tx[:8], uint64(connIndex)) @@ -266,7 +269,7 @@ func generateTx(connIndex int, txNumber int, txSize int, hostnameHash [md5.Size] binary.PutUvarint(tx[32:40], uint64(time.Now().Unix())) // 40-* random data - if _, err := rand.Read(tx[40:]); err != nil { + if _, err := rand.Read(tx[40:]); err != nil { //nolint: gosec panic(errors.Wrap(err, "failed to read random bytes")) } diff --git a/tools/tm-bench/transacter_test.go b/tools/tm-bench/transacter_test.go index 03f304605..7379da072 100644 --- a/tools/tm-bench/transacter_test.go +++ b/tools/tm-bench/transacter_test.go @@ -1,7 +1,7 @@ package main import ( - "crypto/md5" + "crypto/sha256" "encoding/hex" "encoding/json" "fmt" @@ -28,7 +28,7 @@ func TestGenerateTxUpdateTxConsistentency(t *testing.T) { } for tcIndex, tc := range cases { - hostnameHash := md5.Sum([]byte(tc.hostname)) + hostnameHash := sha256.Sum256([]byte(tc.hostname)) // Tx generated from update tx. This is defined outside of the loop, since we have // to a have something initially to update updatedTx := generateTx(tc.connIndex, tc.startingTxNumber, tc.txSize, hostnameHash) @@ -69,7 +69,7 @@ func BenchmarkIterationOfSendLoop(b *testing.B) { // something too far away to matter endTime := now.Add(time.Hour) txNumber := 0 - hostnameHash := md5.Sum([]byte{0}) + hostnameHash := sha256.Sum256([]byte{0}) tx := generateTx(connIndex, txNumber, txSize, hostnameHash) txHex := make([]byte, len(tx)*2) hex.Encode(txHex, tx) diff --git a/types/block_test.go b/types/block_test.go index 31e7983f2..75b5c19dd 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -1,6 +1,8 @@ package types import ( + // it is ok to use math/rand here: we do not need a cryptographically secure random + // number generator here and we can run the tests a bit faster "crypto/rand" "math" "os" @@ -162,8 +164,8 @@ func TestBlockString(t *testing.T) { func makeBlockIDRandom() BlockID { blockHash := make([]byte, tmhash.Size) partSetHash := make([]byte, tmhash.Size) - rand.Read(blockHash) - rand.Read(partSetHash) + rand.Read(blockHash) //nolint: gosec + rand.Read(partSetHash) //nolint: gosec blockPartsHeader := PartSetHeader{123, partSetHash} return BlockID{blockHash, blockPartsHeader} } From 8a9eecce7fd00b8510186aa03290a8c4e252d4c4 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Tue, 12 Feb 2019 06:02:44 +0100 Subject: [PATCH 048/259] test blockExec does not panic if all vals removed (#3241) Fix for #3224 Also address #2084 --- blockchain/reactor.go | 4 +--- state/execution_test.go | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 847398b7b..4a3f90493 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -8,7 +8,6 @@ import ( amino "github.com/tendermint/go-amino" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" sm "github.com/tendermint/tendermint/state" @@ -338,8 +337,7 @@ FOR_LOOP: state, err = bcR.blockExec.ApplyBlock(state, firstID, first) if err != nil { // TODO This is bad, are we zombie? - cmn.PanicQ(fmt.Sprintf("Failed to process committed block (%d:%X): %v", - first.Height, first.Hash(), err)) + panic(fmt.Sprintf("Failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err)) } blocksSynced++ diff --git a/state/execution_test.go b/state/execution_test.go index 8cd90f963..143faa1ab 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -354,6 +354,33 @@ func TestEndBlockValidatorUpdates(t *testing.T) { } } +// TestEndBlockValidatorUpdatesResultingInEmptySet checks that processing validator updates that +// would result in empty set causes no panic, an error is raised and NextValidators is not updated +func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { + app := &testApp{} + cc := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(cc) + err := proxyApp.Start() + require.Nil(t, err) + defer proxyApp.Stop() + + state, stateDB := state(1, 1) + blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), MockMempool{}, MockEvidencePool{}) + + block := makeBlock(state, 1) + blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + + // Remove the only validator + app.ValidatorUpdates = []abci.ValidatorUpdate{ + {PubKey: types.TM2PB.PubKey(state.Validators.Validators[0].PubKey), Power: 0}, + } + + assert.NotPanics(t, func() { state, err = blockExec.ApplyBlock(state, blockID, block) }) + assert.NotNil(t, err) + assert.NotEmpty(t, state.NextValidators.Validators) + +} + //---------------------------------------------------------------------------- // make some bogus txs From 08dabab024b88d6ed9a2416a6e8b9a2604e6d59b Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 12 Feb 2019 06:04:23 +0100 Subject: [PATCH 049/259] types: validator set update tests (#3284) * types: validator set update tests * docs: abci val updates must not include duplicates --- docs/spec/abci/apps.md | 4 + types/validator_set_test.go | 217 ++++++++++++++++++++++++++---------- 2 files changed, 163 insertions(+), 58 deletions(-) diff --git a/docs/spec/abci/apps.md b/docs/spec/abci/apps.md index a3b342400..caffaaea3 100644 --- a/docs/spec/abci/apps.md +++ b/docs/spec/abci/apps.md @@ -171,6 +171,10 @@ Note that the maximum total power of the validator set is bounded by they do not make changes to the validator set that cause it to exceed this limit. +Additionally, applications must ensure that a single set of updates does not contain any duplicates - +a given public key can only appear in an update once. If an update includes +duplicates, the block execution will fail irrecoverably. + ### InitChain ResponseInitChain can return a list of validators. diff --git a/types/validator_set_test.go b/types/validator_set_test.go index cdc04d459..d9558d131 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -658,88 +658,189 @@ type testVal struct { power int64 } +func testValSet(nVals int, power int64) []testVal { + vals := make([]testVal, nVals) + for i := 0; i < nVals; i++ { + vals[i] = testVal{fmt.Sprintf("v%d", i+1), power} + } + return vals +} + +type valSetErrTestCase struct { + startVals []testVal + updateVals []testVal +} + +func executeValSetErrTestCase(t *testing.T, idx int, tt valSetErrTestCase) { + // create a new set and apply updates, keeping copies for the checks + valSet := createNewValidatorSet(tt.startVals) + valSetCopy := valSet.Copy() + valList := createNewValidatorList(tt.updateVals) + valListCopy := validatorListCopy(valList) + err := valSet.UpdateWithChangeSet(valList) + + // for errors check the validator set has not been changed + assert.Error(t, err, "test %d", idx) + assert.Equal(t, valSet, valSetCopy, "test %v", idx) + + // check the parameter list has not changed + assert.Equal(t, valList, valListCopy, "test %v", idx) +} + +func TestValSetUpdatesDuplicateEntries(t *testing.T) { + testCases := []valSetErrTestCase{ + // Duplicate entries in changes + { // first entry is duplicated change + testValSet(2, 10), + []testVal{{"v1", 11}, {"v1", 22}}, + }, + { // second entry is duplicated change + testValSet(2, 10), + []testVal{{"v2", 11}, {"v2", 22}}, + }, + { // change duplicates are separated by a valid change + testValSet(2, 10), + []testVal{{"v1", 11}, {"v2", 22}, {"v1", 12}}, + }, + { // change duplicates are separated by a valid change + testValSet(3, 10), + []testVal{{"v1", 11}, {"v3", 22}, {"v1", 12}}, + }, + + // Duplicate entries in remove + { // first entry is duplicated remove + testValSet(2, 10), + []testVal{{"v1", 0}, {"v1", 0}}, + }, + { // second entry is duplicated remove + testValSet(2, 10), + []testVal{{"v2", 0}, {"v2", 0}}, + }, + { // remove duplicates are separated by a valid remove + testValSet(2, 10), + []testVal{{"v1", 0}, {"v2", 0}, {"v1", 0}}, + }, + { // remove duplicates are separated by a valid remove + testValSet(3, 10), + []testVal{{"v1", 0}, {"v3", 0}, {"v1", 0}}, + }, + + { // remove and update same val + testValSet(2, 10), + []testVal{{"v1", 0}, {"v2", 20}, {"v1", 30}}, + }, + { // duplicate entries in removes + changes + testValSet(2, 10), + []testVal{{"v1", 0}, {"v2", 20}, {"v2", 30}, {"v1", 0}}, + }, + { // duplicate entries in removes + changes + testValSet(3, 10), + []testVal{{"v1", 0}, {"v3", 5}, {"v2", 20}, {"v2", 30}, {"v1", 0}}, + }, + } + + for i, tt := range testCases { + executeValSetErrTestCase(t, i, tt) + } +} + +func TestValSetUpdatesOverflows(t *testing.T) { + maxVP := MaxTotalVotingPower + testCases := []valSetErrTestCase{ + { // single update leading to overflow + testValSet(2, 10), + []testVal{{"v1", math.MaxInt64}}, + }, + { // single update leading to overflow + testValSet(2, 10), + []testVal{{"v2", math.MaxInt64}}, + }, + { // add validator leading to exceed Max + testValSet(1, maxVP-1), + []testVal{{"v2", 5}}, + }, + { // add validator leading to exceed Max + testValSet(2, maxVP/3), + []testVal{{"v3", maxVP / 2}}, + }, + } + + for i, tt := range testCases { + executeValSetErrTestCase(t, i, tt) + } +} + +func TestValSetUpdatesOtherErrors(t *testing.T) { + testCases := []valSetErrTestCase{ + { // update with negative voting power + testValSet(2, 10), + []testVal{{"v1", -123}}, + }, + { // update with negative voting power + testValSet(2, 10), + []testVal{{"v2", -123}}, + }, + { // remove non-existing validator + testValSet(2, 10), + []testVal{{"v3", 0}}, + }, + { // delete all validators + []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, + []testVal{{"v1", 0}, {"v2", 0}, {"v3", 0}}, + }, + } + + for i, tt := range testCases { + executeValSetErrTestCase(t, i, tt) + } +} + func TestValSetUpdatesBasicTestsExecute(t *testing.T) { valSetUpdatesBasicTests := []struct { startVals []testVal updateVals []testVal expectedVals []testVal - expError bool }{ - // Operations that should result in error - 0: { // updates leading to overflows - []testVal{{"v1", 10}, {"v2", 10}}, - []testVal{{"v1", math.MaxInt64}}, - []testVal{{"v1", 10}, {"v2", 10}}, - true}, - 1: { // duplicate entries in changes - []testVal{{"v1", 10}, {"v2", 10}}, - []testVal{{"v1", 11}, {"v1", 22}}, - []testVal{{"v1", 10}, {"v2", 10}}, - true}, - 2: { // duplicate entries in removes - []testVal{{"v1", 10}, {"v2", 10}}, - []testVal{{"v1", 0}, {"v1", 0}}, - []testVal{{"v1", 10}, {"v2", 10}}, - true}, - 3: { // duplicate entries in removes + changes - []testVal{{"v1", 10}, {"v2", 10}}, - []testVal{{"v1", 0}, {"v2", 20}, {"v2", 30}, {"v1", 0}}, - []testVal{{"v1", 10}, {"v2", 10}}, - true}, - 4: { // update with negative voting power - []testVal{{"v1", 10}, {"v2", 10}}, - []testVal{{"v1", -123}}, - []testVal{{"v1", 10}, {"v2", 10}}, - true}, - 5: { // delete non existing validator - []testVal{{"v1", 10}, {"v2", 10}}, - []testVal{{"v3", 0}}, - []testVal{{"v1", 10}, {"v2", 10}}, - true}, - - // Operations that should be successful - 6: { // no changes - []testVal{{"v1", 10}, {"v2", 10}}, + { // no changes + testValSet(2, 10), []testVal{}, - []testVal{{"v1", 10}, {"v2", 10}}, - false}, - 7: { // voting power changes - []testVal{{"v1", 10}, {"v2", 10}}, + testValSet(2, 10), + }, + { // voting power changes + testValSet(2, 10), []testVal{{"v1", 11}, {"v2", 22}}, []testVal{{"v1", 11}, {"v2", 22}}, - false}, - 8: { // add new validators + }, + { // add new validators []testVal{{"v1", 10}, {"v2", 20}}, []testVal{{"v3", 30}, {"v4", 40}}, []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}, {"v4", 40}}, - false}, - 9: { // delete validators + }, + { // add new validator to middle + []testVal{{"v1", 10}, {"v3", 20}}, + []testVal{{"v2", 30}}, + []testVal{{"v1", 10}, {"v2", 30}, {"v3", 20}}, + }, + { // add new validator to beginning + []testVal{{"v2", 10}, {"v3", 20}}, + []testVal{{"v1", 30}}, + []testVal{{"v1", 30}, {"v2", 10}, {"v3", 20}}, + }, + { // delete validators []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, []testVal{{"v2", 0}}, []testVal{{"v1", 10}, {"v3", 30}}, - false}, - 10: { // delete all validators - []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, - []testVal{{"v1", 0}, {"v2", 0}, {"v3", 0}}, - []testVal{{"v1", 10}, {"v2", 20}, {"v3", 30}}, - true}, + }, } for i, tt := range valSetUpdatesBasicTests { // create a new set and apply updates, keeping copies for the checks valSet := createNewValidatorSet(tt.startVals) - valSetCopy := valSet.Copy() valList := createNewValidatorList(tt.updateVals) valListCopy := validatorListCopy(valList) err := valSet.UpdateWithChangeSet(valList) + assert.NoError(t, err, "test %d", i) - if tt.expError { - // for errors check the validator set has not been changed - assert.Error(t, err, "test %d", i) - assert.Equal(t, valSet, valSetCopy, "test %v", i) - } else { - assert.NoError(t, err, "test %d", i) - } // check the parameter list has not changed assert.Equal(t, valList, valListCopy, "test %v", i) From dc6567c677dfdfed7278759927ec47e6e94e7bff Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 12 Feb 2019 06:32:40 +0100 Subject: [PATCH 050/259] consensus: flush wal on stop (#3297) Should fix #3295 Also partial fix of #3043 --- CHANGELOG.md | 2 +- CHANGELOG_PENDING.md | 2 ++ consensus/wal.go | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 592a7c6b5..1842c8e32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -89,7 +89,7 @@ Special thanks to external contributors on this release: - [p2p] [\#3247](https://github.com/tendermint/tendermint/issues/3247) Fix panic in SeedMode when calling FlushStop and OnStop concurrently - [p2p] [\#3040](https://github.com/tendermint/tendermint/issues/3040) Fix MITM on secret connection by checking low-order points -- [privval] [\#3258](https://github.com/tendermint/tendermint/issues/3258) Fix race between sign requests and ping requests in socket +- [privval] [\#3258](https://github.com/tendermint/tendermint/issues/3258) Fix race between sign requests and ping requests in socket that was causing messages to be corrupted ## v0.29.1 diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 640985836..f23ec5bfb 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,3 +21,5 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: +- [consensus] \#3297 Flush WAL on stop to prevent data corruption during + graceful shutdown diff --git a/consensus/wal.go b/consensus/wal.go index d56ede26d..26428a4c6 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -116,6 +116,7 @@ func (wal *baseWAL) OnStart() error { // Use Wait() to ensure it's finished shutting down // before cleaning up files. func (wal *baseWAL) OnStop() { + wal.group.Flush() wal.group.Stop() wal.group.Close() } From d32f7d241608c8079f0755a38dbd1b9bd2262724 Mon Sep 17 00:00:00 2001 From: Zach Date: Tue, 12 Feb 2019 23:26:01 -0500 Subject: [PATCH 051/259] update codeowners (#3305) limit the scope of @zramsay because he's annoyed by notifications --- .github/CODEOWNERS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9d2fc15be..df77531cc 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -4,4 +4,6 @@ * @ebuchman @melekes @xla # Precious documentation -/docs/ @zramsay +/docs/README.md @zramsay +/docs/DOCS_README.md @zramsay +/docs/.vuepress/ @zramsay From cf737ec85c10fb3baa2071bf5b689ab95cf797be Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Sat, 16 Feb 2019 00:07:18 -0500 Subject: [PATCH 052/259] return an error on `show_validator` (#3316) * return a command error prior to init * add a pending log entry * Update CHANGELOG_PENDING.md Co-Authored-By: alexanderbez closes: #3314 --- CHANGELOG_PENDING.md | 6 +++++- cmd/tendermint/commands/show_node_id.go | 3 +-- cmd/tendermint/commands/show_validator.go | 21 ++++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f23ec5bfb..cfaa1b219 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -21,5 +21,9 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: -- [consensus] \#3297 Flush WAL on stop to prevent data corruption during + +* [consensus] \#3297 Flush WAL on stop to prevent data corruption during graceful shutdown +* [cmd] \#3314 Return an + error on `show_validator` when the private validator file does not exist. + diff --git a/cmd/tendermint/commands/show_node_id.go b/cmd/tendermint/commands/show_node_id.go index 02ab1a9bb..6026e3e18 100644 --- a/cmd/tendermint/commands/show_node_id.go +++ b/cmd/tendermint/commands/show_node_id.go @@ -16,12 +16,11 @@ var ShowNodeIDCmd = &cobra.Command{ } func showNodeID(cmd *cobra.Command, args []string) error { - nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile()) if err != nil { return err } - fmt.Println(nodeKey.ID()) + fmt.Println(nodeKey.ID()) return nil } diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index 78bc06034..fbf14f2f6 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -5,6 +5,7 @@ import ( "github.com/spf13/cobra" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/privval" ) @@ -12,11 +13,21 @@ import ( var ShowValidatorCmd = &cobra.Command{ Use: "show_validator", Short: "Show this node's validator info", - Run: showValidator, + RunE: showValidator, } -func showValidator(cmd *cobra.Command, args []string) { - privValidator := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) - pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey()) - fmt.Println(string(pubKeyJSONBytes)) +func showValidator(cmd *cobra.Command, args []string) error { + keyFilePath := config.PrivValidatorKeyFile() + if !cmn.FileExists(keyFilePath) { + return fmt.Errorf("private validator file %s does not exist", keyFilePath) + } + + pv := privval.LoadFilePV(keyFilePath, config.PrivValidatorStateFile()) + bz, err := cdc.MarshalJSON(pv.GetPubKey()) + if err != nil { + return err + } + + fmt.Println(string(bz)) + return nil } From af3ba5145a1763dc16616a4cd9b697c10a6dd373 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Sat, 16 Feb 2019 06:12:00 +0100 Subject: [PATCH 053/259] fix test-vectors to actually match the sign bytes: (#3312) - sign bytes are length prefixed - change test to use SignBytes methods instead of calling amino methods directly Resolves #3311 ref: #2455 #2508 --- types/vote_test.go | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/types/vote_test.go b/types/vote_test.go index e4bf658bc..af8a9625b 100644 --- a/types/vote_test.go +++ b/types/vote_test.go @@ -67,23 +67,23 @@ func TestVoteSignable(t *testing.T) { require.Equal(t, expected, signBytes, "Got unexpected sign bytes for Vote.") } -func TestVoteSignableTestVectors(t *testing.T) { - vote := CanonicalizeVote("", &Vote{Height: 1, Round: 1}) +func TestVoteSignBytesTestVectors(t *testing.T) { tests := []struct { - canonicalVote CanonicalVote - want []byte + chainID string + vote *Vote + want []byte }{ - { - CanonicalizeVote("", &Vote{}), + 0: { + "", &Vote{}, // NOTE: Height and Round are skipped here. This case needs to be considered while parsing. - // []byte{0x2a, 0x9, 0x9, 0x0, 0x9, 0x6e, 0x88, 0xf1, 0xff, 0xff, 0xff}, - []byte{0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, + []byte{0xd, 0x2a, 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, // with proper (fixed size) height and round (PreCommit): - { - CanonicalizeVote("", &Vote{Height: 1, Round: 1, Type: PrecommitType}), + 1: { + "", &Vote{Height: 1, Round: 1, Type: PrecommitType}, []byte{ + 0x21, // length 0x8, // (field_number << 3) | wire_type 0x2, // PrecommitType 0x11, // (field_number << 3) | wire_type @@ -95,9 +95,10 @@ func TestVoteSignableTestVectors(t *testing.T) { 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, // with proper (fixed size) height and round (PreVote): - { - CanonicalizeVote("", &Vote{Height: 1, Round: 1, Type: PrevoteType}), + 2: { + "", &Vote{Height: 1, Round: 1, Type: PrevoteType}, []byte{ + 0x21, // length 0x8, // (field_number << 3) | wire_type 0x1, // PrevoteType 0x11, // (field_number << 3) | wire_type @@ -108,9 +109,10 @@ func TestVoteSignableTestVectors(t *testing.T) { // remaining fields (timestamp): 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, - { - vote, + 3: { + "", &Vote{Height: 1, Round: 1}, []byte{ + 0x1f, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type @@ -120,9 +122,10 @@ func TestVoteSignableTestVectors(t *testing.T) { 0xb, 0x8, 0x80, 0x92, 0xb8, 0xc3, 0x98, 0xfe, 0xff, 0xff, 0xff, 0x1}, }, // containing non-empty chain_id: - { - CanonicalizeVote("test_chain_id", &Vote{Height: 1, Round: 1}), + 4: { + "test_chain_id", &Vote{Height: 1, Round: 1}, []byte{ + 0x2e, // length 0x11, // (field_number << 3) | wire_type 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, // height 0x19, // (field_number << 3) | wire_type @@ -135,9 +138,7 @@ func TestVoteSignableTestVectors(t *testing.T) { }, } for i, tc := range tests { - got, err := cdc.MarshalBinaryBare(tc.canonicalVote) - require.NoError(t, err) - + got := tc.vote.SignBytes(tc.chainID) require.Equal(t, tc.want, got, "test case #%v: got unexpected sign bytes for Vote.", i) } } From 7ced9e416bc1c08a8b268875762db6a83746777f Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 16 Feb 2019 09:46:02 +0400 Subject: [PATCH 054/259] rpc/net_info: change RemoteIP type from net.IP to String (#3309) * rpc/net_info: change RemoteIP type from net.IP to String Before: "AAAAAAAAAAAAAP//rB8ktw==" which is amino-encoded net.IP byte slice After: "192.0.2.1" Fixes #3251 * rpc/net_info: non-empty response in docs --- CHANGELOG_PENDING.md | 6 +- rpc/core/net.go | 140 ++++++++++++++++++++++++++++++++---- rpc/core/types/responses.go | 3 +- 3 files changed, 131 insertions(+), 18 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index cfaa1b219..540a5d7d9 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -24,6 +24,8 @@ Special thanks to external contributors on this release: * [consensus] \#3297 Flush WAL on stop to prevent data corruption during graceful shutdown +- [rpc] \#3251 Fix /net_info#peers#remote_ip format. New format spec: + * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address + * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address * [cmd] \#3314 Return an - error on `show_validator` when the private validator file does not exist. - + error on `show_validator` when the private validator file does not exist diff --git a/rpc/core/net.go b/rpc/core/net.go index 56e9624d7..e920ea7c3 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -29,21 +29,133 @@ import ( // // ```json // { -// "error": "", -// "result": { -// "n_peers": "0", -// "peers": [], -// "listeners": [ -// "Listener(@10.0.2.15:26656)" -// ], -// "listening": true -// }, -// "id": "", -// "jsonrpc": "2.0" -// } +// "jsonrpc": "2.0", +// "id": "", +// "result": { +// "listening": true, +// "listeners": [ +// "Listener(@)" +// ], +// "n_peers": "3", +// "peers": [ +// { +// "node_info": { +// "protocol_version": { +// "p2p": "7", +// "block": "8", +// "app": "1" +// }, +// "id": "93529da3435c090d02251a050342b6a488d4ab56", +// "listen_addr": "tcp://0.0.0.0:26656", +// "network": "chain-RFo6qC", +// "version": "0.30.0", +// "channels": "4020212223303800", +// "moniker": "fc89e4ed23f2", +// "other": { +// "tx_index": "on", +// "rpc_address": "tcp://0.0.0.0:26657" +// } +// }, +// "is_outbound": true, +// "connection_status": { +// "Duration": "3475230558", +// "SendMonitor": { +// "Active": true, +// "Start": "2019-02-14T12:40:47.52Z", +// "Duration": "3480000000", +// "Idle": "240000000", +// "Bytes": "4512", +// "Samples": "9", +// "InstRate": "1338", +// "CurRate": "2046", +// "AvgRate": "1297", +// "PeakRate": "6570", +// "BytesRem": "0", +// "TimeRem": "0", +// "Progress": 0 +// }, +// "RecvMonitor": { +// "Active": true, +// "Start": "2019-02-14T12:40:47.52Z", +// "Duration": "3480000000", +// "Idle": "280000000", +// "Bytes": "4489", +// "Samples": "10", +// "InstRate": "1821", +// "CurRate": "1663", +// "AvgRate": "1290", +// "PeakRate": "5512", +// "BytesRem": "0", +// "TimeRem": "0", +// "Progress": 0 +// }, +// "Channels": [ +// { +// "ID": 48, +// "SendQueueCapacity": "1", +// "SendQueueSize": "0", +// "Priority": "5", +// "RecentlySent": "0" +// }, +// { +// "ID": 64, +// "SendQueueCapacity": "1000", +// "SendQueueSize": "0", +// "Priority": "10", +// "RecentlySent": "14" +// }, +// { +// "ID": 32, +// "SendQueueCapacity": "100", +// "SendQueueSize": "0", +// "Priority": "5", +// "RecentlySent": "619" +// }, +// { +// "ID": 33, +// "SendQueueCapacity": "100", +// "SendQueueSize": "0", +// "Priority": "10", +// "RecentlySent": "1363" +// }, +// { +// "ID": 34, +// "SendQueueCapacity": "100", +// "SendQueueSize": "0", +// "Priority": "5", +// "RecentlySent": "2145" +// }, +// { +// "ID": 35, +// "SendQueueCapacity": "2", +// "SendQueueSize": "0", +// "Priority": "1", +// "RecentlySent": "0" +// }, +// { +// "ID": 56, +// "SendQueueCapacity": "1", +// "SendQueueSize": "0", +// "Priority": "5", +// "RecentlySent": "0" +// }, +// { +// "ID": 0, +// "SendQueueCapacity": "10", +// "SendQueueSize": "0", +// "Priority": "1", +// "RecentlySent": "10" +// } +// ] +// }, +// "remote_ip": "192.167.10.3" +// }, +// ... +// } // ``` func NetInfo() (*ctypes.ResultNetInfo, error) { - peers := []ctypes.Peer{} + out, in, _ := p2pPeers.NumPeers() + peers := make([]ctypes.Peer, 0, out+in) for _, peer := range p2pPeers.Peers().List() { nodeInfo, ok := peer.NodeInfo().(p2p.DefaultNodeInfo) if !ok { @@ -53,7 +165,7 @@ func NetInfo() (*ctypes.ResultNetInfo, error) { NodeInfo: nodeInfo, IsOutbound: peer.IsOutbound(), ConnectionStatus: peer.Status(), - RemoteIP: peer.RemoteIP(), + RemoteIP: peer.RemoteIP().String(), }) } // TODO: Should we include PersistentPeers and Seeds in here? diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 62be1cafd..f3ce4054b 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -2,7 +2,6 @@ package core_types import ( "encoding/json" - "net" "time" abci "github.com/tendermint/tendermint/abci/types" @@ -111,7 +110,7 @@ type Peer struct { NodeInfo p2p.DefaultNodeInfo `json:"node_info"` IsOutbound bool `json:"is_outbound"` ConnectionStatus p2p.ConnectionStatus `json:"connection_status"` - RemoteIP net.IP `json:"remote_ip"` + RemoteIP string `json:"remote_ip"` } // Validators for a height From 0b0a8b3128e52bf34a34685804922a47cd3c7810 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 18 Feb 2019 11:23:06 +0400 Subject: [PATCH 055/259] cs/wal: refuse to encode msg that is bigger than maxMsgSizeBytes (#3303) Earlier this week somebody posted this in GoS Riot chat: ``` E[2019-02-12|10:38:37.596] Corrupted entry. Skipping... module=consensus wal=/home/gaia/.gaiad/data/cs.wal/wal err="DataCorruptionError[length 878916964 exceeded maximum possible value of 1048576 bytes]" E[2019-02-12|10:38:37.596] Corrupted entry. Skipping... module=consensus wal=/home/gaia/.gaiad/data/cs.wal/wal err="DataCorruptionError[length 825701731 exceeded maximum possible value of 1048576 bytes]" E[2019-02-12|10:38:37.596] Corrupted entry. Skipping... module=consensus wal=/home/gaia/.gaiad/data/cs.wal/wal err="DataCorruptionError[length 1631073634 exceeded maximum possible value of 1048576 bytes]" E[2019-02-12|10:38:37.596] Corrupted entry. Skipping... module=consensus wal=/home/gaia/.gaiad/data/cs.wal/wal err="DataCorruptionError[length 912418148 exceeded maximum possible value of 1048576 bytes]" E[2019-02-12|10:38:37.600] Corrupted entry. Skipping... module=consensus wal=/home/gaia/.gaiad/data/cs.wal/wal err="DataCorruptionError[failed to read data: EOF]" E[2019-02-12|10:38:37.600] Error on catchup replay. Proceeding to start ConsensusState anyway module=consensus err="Cannot replay height 7242. WAL does not contain #ENDHEIGHT for 7241" E[2019-02-12|10:38:37.861] Error dialing peer module=p2p err="dial tcp 35.183.126.181:26656: i/o timeout ``` Note the length error messages. What has happened is the length field got corrupted probably. I've looked at the code and noticed that we don't check the msg size during encoding. This PR fixes that. It also improves a few error messages in WALDecoder. --- consensus/wal.go | 13 +++++++++---- consensus/wal_test.go | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/consensus/wal.go b/consensus/wal.go index 26428a4c6..f8988691c 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -229,12 +229,17 @@ func NewWALEncoder(wr io.Writer) *WALEncoder { return &WALEncoder{wr} } -// Encode writes the custom encoding of v to the stream. +// Encode writes the custom encoding of v to the stream. It returns an error if +// the amino-encoded size of v is greater than 1MB. Any error encountered +// during the write is also returned. func (enc *WALEncoder) Encode(v *TimedWALMessage) error { data := cdc.MustMarshalBinaryBare(v) crc := crc32.Checksum(data, crc32c) length := uint32(len(data)) + if length > maxMsgSizeBytes { + return fmt.Errorf("Msg is too big: %d bytes, max: %d bytes", length, maxMsgSizeBytes) + } totalLength := 8 + int(length) msg := make([]byte, totalLength) @@ -307,15 +312,15 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) { } data := make([]byte, length) - _, err = dec.rd.Read(data) + n, err := dec.rd.Read(data) if err != nil { - return nil, DataCorruptionError{fmt.Errorf("failed to read data: %v", err)} + return nil, DataCorruptionError{fmt.Errorf("failed to read data: %v (read: %d, wanted: %d)", err, n, length)} } // check checksum before decoding data actualCRC := crc32.Checksum(data, crc32c) if actualCRC != crc { - return nil, DataCorruptionError{fmt.Errorf("checksums do not match: (read: %v, actual: %v)", crc, actualCRC)} + return nil, DataCorruptionError{fmt.Errorf("checksums do not match: read: %v, actual: %v", crc, actualCRC)} } var res = new(TimedWALMessage) // nolint: gosimple diff --git a/consensus/wal_test.go b/consensus/wal_test.go index 93beb68bb..7ec338345 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -95,6 +95,26 @@ func TestWALEncoderDecoder(t *testing.T) { } } +func TestWALWritePanicsIfMsgIsTooBig(t *testing.T) { + walDir, err := ioutil.TempDir("", "wal") + require.NoError(t, err) + defer os.RemoveAll(walDir) + walFile := filepath.Join(walDir, "wal") + + wal, err := NewWAL(walFile) + require.NoError(t, err) + err = wal.Start() + require.NoError(t, err) + defer func() { + wal.Stop() + // wait for the wal to finish shutting down so we + // can safely remove the directory + wal.Wait() + }() + + assert.Panics(t, func() { wal.Write(make([]byte, maxMsgSizeBytes+1)) }) +} + func TestWALSearchForEndHeight(t *testing.T) { walBody, err := WALWithNBlocks(6) if err != nil { From af8793c01a168f0f15d3147673c572c0f8bf7863 Mon Sep 17 00:00:00 2001 From: Zarko Milosevic Date: Mon, 18 Feb 2019 08:29:41 +0100 Subject: [PATCH 056/259] cs: reset triggered timeout precommit (#3310) * Reset TriggeredTimeoutPrecommit as part of updateToState * Add failing test and fix * fix DATA RACE in TestResetTimeoutPrecommitUponNewHeight ``` WARNING: DATA RACE Read at 0x00c001691d28 by goroutine 691: github.com/tendermint/tendermint/consensus.decideProposal() /go/src/github.com/tendermint/tendermint/consensus/common_test.go:133 +0x121 github.com/tendermint/tendermint/consensus.TestResetTimeoutPrecommitUponNewHeight() /go/src/github.com/tendermint/tendermint/consensus/state_test.go:1389 +0x958 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 Previous write at 0x00c001691d28 by goroutine 931: github.com/tendermint/tendermint/consensus.(*ConsensusState).updateToState() /go/src/github.com/tendermint/tendermint/consensus/state.go:562 +0x5b2 github.com/tendermint/tendermint/consensus.(*ConsensusState).finalizeCommit() /go/src/github.com/tendermint/tendermint/consensus/state.go:1340 +0x141e github.com/tendermint/tendermint/consensus.(*ConsensusState).tryFinalizeCommit() /go/src/github.com/tendermint/tendermint/consensus/state.go:1255 +0x66e github.com/tendermint/tendermint/consensus.(*ConsensusState).enterCommit.func1() /go/src/github.com/tendermint/tendermint/consensus/state.go:1201 +0x135 github.com/tendermint/tendermint/consensus.(*ConsensusState).enterCommit() /go/src/github.com/tendermint/tendermint/consensus/state.go:1232 +0x94b github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote() /go/src/github.com/tendermint/tendermint/consensus/state.go:1657 +0x132e github.com/tendermint/tendermint/consensus.(*ConsensusState).tryAddVote() /go/src/github.com/tendermint/tendermint/consensus/state.go:1503 +0x8f github.com/tendermint/tendermint/consensus.(*ConsensusState).handleMsg() /go/src/github.com/tendermint/tendermint/consensus/state.go:694 +0xa1e github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:655 +0x7dd github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:655 +0x7dd Goroutine 691 (running) created at: testing.(*T).Run() /usr/local/go/src/testing/testing.go:878 +0x659 testing.runTests.func1() /usr/local/go/src/testing/testing.go:1119 +0xa8 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 testing.runTests() testing.(*M).Run() /usr/local/go/src/testing/testing.go:1034 +0x2ee main.main() _testmain.go:216 +0x332 ``` * fix another DATA RACE by locking consensus ``` WARNING: DATA RACE Read at 0x00c009b835a8 by goroutine 871: github.com/tendermint/tendermint/consensus.(*ConsensusState).createProposalBlock() /go/src/github.com/tendermint/tendermint/consensus/state.go:955 +0x7c github.com/tendermint/tendermint/consensus.decideProposal() /go/src/github.com/tendermint/tendermint/consensus/common_test.go:127 +0x53 github.com/tendermint/tendermint/consensus.TestResetTimeoutPrecommitUponNewHeight() /go/src/github.com/tendermint/tendermint/consensus/state_test.go:1389 +0x958 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 Previous write at 0x00c009b835a8 by goroutine 931: github.com/tendermint/tendermint/consensus.(*ConsensusState).updateHeight() /go/src/github.com/tendermint/tendermint/consensus/state.go:446 +0xb7 github.com/tendermint/tendermint/consensus.(*ConsensusState).updateToState() /go/src/github.com/tendermint/tendermint/consensus/state.go:542 +0x22f github.com/tendermint/tendermint/consensus.(*ConsensusState).finalizeCommit() /go/src/github.com/tendermint/tendermint/consensus/state.go:1340 +0x141e github.com/tendermint/tendermint/consensus.(*ConsensusState).tryFinalizeCommit() /go/src/github.com/tendermint/tendermint/consensus/state.go:1255 +0x66e github.com/tendermint/tendermint/consensus.(*ConsensusState).enterCommit.func1() /go/src/github.com/tendermint/tendermint/consensus/state.go:1201 +0x135 github.com/tendermint/tendermint/consensus.(*ConsensusState).enterCommit() /go/src/github.com/tendermint/tendermint/consensus/state.go:1232 +0x94b github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote() /go/src/github.com/tendermint/tendermint/consensus/state.go:1657 +0x132e github.com/tendermint/tendermint/consensus.(*ConsensusState).tryAddVote() /go/src/github.com/tendermint/tendermint/consensus/state.go:1503 +0x8f github.com/tendermint/tendermint/consensus.(*ConsensusState).handleMsg() /go/src/github.com/tendermint/tendermint/consensus/state.go:694 +0xa1e github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:655 +0x7dd github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:642 +0x948 github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:655 +0x7dd ``` * Fix failing test * Delete profile.out * fix data races --- CHANGELOG_PENDING.md | 1 + consensus/common_test.go | 10 +++++-- consensus/state.go | 1 + consensus/state_test.go | 65 ++++++++++++++++++++++++++++++++++++++-- 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 540a5d7d9..9ce6f496f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -24,6 +24,7 @@ Special thanks to external contributors on this release: * [consensus] \#3297 Flush WAL on stop to prevent data corruption during graceful shutdown +- [consensus] \#3310 Reset TriggeredTimeoutPrecommit before starting next height - [rpc] \#3251 Fix /net_info#peers#remote_ip format. New format spec: * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address diff --git a/consensus/common_test.go b/consensus/common_test.go index 7e24d3ffa..ec24704ac 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -124,15 +124,21 @@ func startTestRound(cs *ConsensusState, height int64, round int) { // Create proposal block from cs1 but sign it with vs func decideProposal(cs1 *ConsensusState, vs *validatorStub, height int64, round int) (proposal *types.Proposal, block *types.Block) { + cs1.mtx.Lock() block, blockParts := cs1.createProposalBlock() + cs1.mtx.Unlock() if block == nil { // on error panic("error creating proposal block") } // Make proposal - polRound, propBlockID := cs1.ValidRound, types.BlockID{block.Hash(), blockParts.Header()} + cs1.mtx.RLock() + validRound := cs1.ValidRound + chainID := cs1.state.ChainID + cs1.mtx.RUnlock() + polRound, propBlockID := validRound, types.BlockID{block.Hash(), blockParts.Header()} proposal = types.NewProposal(height, round, polRound, propBlockID) - if err := vs.SignProposal(cs1.state.ChainID, proposal); err != nil { + if err := vs.SignProposal(chainID, proposal); err != nil { panic(err) } return diff --git a/consensus/state.go b/consensus/state.go index 940318c0b..08e1da267 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -566,6 +566,7 @@ func (cs *ConsensusState) updateToState(state sm.State) { cs.CommitRound = -1 cs.LastCommit = lastPrecommits cs.LastValidators = state.LastValidators + cs.TriggeredTimeoutPrecommit = false cs.state = state diff --git a/consensus/state_test.go b/consensus/state_test.go index 153f51e16..e90035737 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -1288,8 +1288,8 @@ func (n *fakeTxNotifier) Notify() { } func TestStartNextHeightCorrectly(t *testing.T) { + config.Consensus.SkipTimeoutCommit = false cs1, vss := randConsensusState(4) - cs1.config.SkipTimeoutCommit = false cs1.txNotifier = &fakeTxNotifier{ch: make(chan struct{})} vs2, vs3, vs4 := vss[1], vss[2], vss[3] @@ -1326,13 +1326,14 @@ func TestStartNextHeightCorrectly(t *testing.T) { // add precommits signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) + time.Sleep(5 * time.Millisecond) signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4) - ensureNewBlockHeader(newBlockHeader, height, theBlockHash) - rs = cs1.GetRoundState() assert.True(t, rs.TriggeredTimeoutPrecommit) + ensureNewBlockHeader(newBlockHeader, height, theBlockHash) + cs1.txNotifier.(*fakeTxNotifier).Notify() ensureNewTimeout(timeoutProposeCh, height+1, round, cs1.config.TimeoutPropose.Nanoseconds()) @@ -1340,6 +1341,64 @@ func TestStartNextHeightCorrectly(t *testing.T) { assert.False(t, rs.TriggeredTimeoutPrecommit, "triggeredTimeoutPrecommit should be false at the beginning of each round") } +func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) { + config.Consensus.SkipTimeoutCommit = false + cs1, vss := randConsensusState(4) + + vs2, vs3, vs4 := vss[1], vss[2], vss[3] + height, round := cs1.Height, cs1.Round + + partSize := types.BlockPartSizeBytes + + proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) + + newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) + newBlockHeader := subscribe(cs1.eventBus, types.EventQueryNewBlockHeader) + addr := cs1.privValidator.GetPubKey().Address() + voteCh := subscribeToVoter(cs1, addr) + + // start round and wait for propose and prevote + startTestRound(cs1, height, round) + ensureNewRound(newRoundCh, height, round) + + ensureNewProposal(proposalCh, height, round) + rs := cs1.GetRoundState() + theBlockHash := rs.ProposalBlock.Hash() + theBlockParts := rs.ProposalBlockParts.Header() + + ensurePrevote(voteCh, height, round) + validatePrevote(t, cs1, round, vss[0], theBlockHash) + + signAddVotes(cs1, types.PrevoteType, theBlockHash, theBlockParts, vs2, vs3, vs4) + + ensurePrecommit(voteCh, height, round) + validatePrecommit(t, cs1, round, round, vss[0], theBlockHash, theBlockHash) + + rs = cs1.GetRoundState() + + // add precommits + signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) + signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) + time.Sleep(5 * time.Millisecond) + signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4) + + rs = cs1.GetRoundState() + assert.True(t, rs.TriggeredTimeoutPrecommit) + + ensureNewBlockHeader(newBlockHeader, height, theBlockHash) + + prop, propBlock := decideProposal(cs1, vs2, height+1, 0) + propBlockParts := propBlock.MakePartSet(partSize) + + if err := cs1.SetProposalAndBlock(prop, propBlock, propBlockParts, "some peer"); err != nil { + t.Fatal(err) + } + ensureNewProposal(proposalCh, height+1, 0) + + rs = cs1.GetRoundState() + assert.False(t, rs.TriggeredTimeoutPrecommit, "triggeredTimeoutPrecommit should be false at the beginning of each height") +} + //------------------------------------------------------------------------------------------ // SlashingSuite // TODO: Slashing From 59cc6d36c944012cd4ae39fbee698e8cf353005a Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Mon, 18 Feb 2019 08:45:27 +0100 Subject: [PATCH 057/259] improve ResetTestRootWithChainID() concurrency safety (#3291) * improve ResetTestRootWithChainID() concurrency safety Rely on ioutil.TempDir() to create test root directories and ensure multiple same-chain id test cases can run in parallel. * Update config/toml.go Co-Authored-By: alessio * clean up test directories after completion Closes: #1034 * Remove redundant EnsureDir call * s/PanicSafety()/panic()/s * Put create dir functionality back in ResetTestRootWithChainID * Place test directories in OS's tempdir In modern UNIX and UNIX-like systems /tmp is very often mounted as tmpfs. This might speed test execution a bit. * Set 0700 to a const * rootsDirs -> configRootDirs * Don't double remove directories * Avoid global variables * Fix consensus tests * Reduce defer stack * Address review comments * Try to fix tests * Update CHANGELOG_PENDING.md Co-Authored-By: alessio * Update consensus/common_test.go Co-Authored-By: alessio * Update consensus/common_test.go Co-Authored-By: alessio --- CHANGELOG_PENDING.md | 2 + blockchain/reactor_test.go | 3 ++ blockchain/store_test.go | 40 +++++++++++----- config/toml.go | 41 +++++++--------- config/toml_test.go | 1 + consensus/byzantine_test.go | 7 +-- consensus/common_test.go | 35 +++++++++++--- consensus/mempool_test.go | 8 ++-- consensus/reactor_test.go | 24 +++++----- consensus/replay_test.go | 63 ++++++++++++++++--------- consensus/state_test.go | 4 -- consensus/types/height_vote_set_test.go | 6 ++- consensus/wal_generator.go | 28 ++++------- consensus/wal_test.go | 4 +- lite/client/provider_test.go | 4 +- lite/proxy/query_test.go | 4 +- mempool/bench_test.go | 3 +- mempool/mempool_test.go | 24 ++++++---- mempool/reactor_test.go | 3 +- node/node_test.go | 7 +++ rpc/client/main_test.go | 4 +- rpc/grpc/grpc_test.go | 3 +- rpc/test/helpers.go | 10 ++-- state/state_test.go | 7 +-- 24 files changed, 202 insertions(+), 133 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 9ce6f496f..07a8b6267 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,6 +20,8 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: +- [config] \#3291 Make config.ResetTestRootWithChainID() create concurrency-safe test directories. + ### BUG FIXES: * [consensus] \#3297 Flush WAL on stop to prevent data corruption during diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 1e8666f12..1b3aee56f 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -1,6 +1,7 @@ package blockchain import ( + "os" "sort" "testing" "time" @@ -125,6 +126,7 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals func TestNoBlockResponse(t *testing.T) { config = cfg.ResetTestRoot("blockchain_reactor_test") + defer os.RemoveAll(config.RootDir) genDoc, privVals := randGenesisDoc(1, false, 30) maxBlockHeight := int64(65) @@ -184,6 +186,7 @@ func TestNoBlockResponse(t *testing.T) { // that seems extreme. func TestBadBlockStopsPeer(t *testing.T) { config = cfg.ResetTestRoot("blockchain_reactor_test") + defer os.RemoveAll(config.RootDir) genDoc, privVals := randGenesisDoc(1, false, 30) maxBlockHeight := int64(148) diff --git a/blockchain/store_test.go b/blockchain/store_test.go index 931faf6a7..f1108e722 100644 --- a/blockchain/store_test.go +++ b/blockchain/store_test.go @@ -3,6 +3,7 @@ package blockchain import ( "bytes" "fmt" + "os" "runtime/debug" "strings" "testing" @@ -21,13 +22,16 @@ import ( tmtime "github.com/tendermint/tendermint/types/time" ) +// A cleanupFunc cleans up any config / test files created for a particular test. +type cleanupFunc func() + // make a Commit with a single vote containing just the height and a timestamp func makeTestCommit(height int64, timestamp time.Time) *types.Commit { commitSigs := []*types.CommitSig{{Height: height, Timestamp: timestamp}} return types.NewCommit(types.BlockID{}, commitSigs) } -func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { +func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore, cleanupFunc) { config := cfg.ResetTestRoot("blockchain_reactor_test") // blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB()) // stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB()) @@ -37,7 +41,7 @@ func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) { if err != nil { panic(cmn.ErrorWrap(err, "error constructing state from genesis file")) } - return state, NewBlockStore(blockDB) + return state, NewBlockStore(blockDB), func() { os.RemoveAll(config.RootDir) } } func TestLoadBlockStoreStateJSON(t *testing.T) { @@ -87,19 +91,32 @@ func freshBlockStore() (*BlockStore, db.DB) { } var ( - state, _ = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) - - block = makeBlock(1, state, new(types.Commit)) - partSet = block.MakePartSet(2) - part1 = partSet.GetPart(0) - part2 = partSet.GetPart(1) - seenCommit1 = makeTestCommit(10, tmtime.Now()) + state sm.State + block *types.Block + partSet *types.PartSet + part1 *types.Part + part2 *types.Part + seenCommit1 *types.Commit ) +func TestMain(m *testing.M) { + var cleanup cleanupFunc + state, _, cleanup = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + block = makeBlock(1, state, new(types.Commit)) + partSet = block.MakePartSet(2) + part1 = partSet.GetPart(0) + part2 = partSet.GetPart(1) + seenCommit1 = makeTestCommit(10, tmtime.Now()) + code := m.Run() + cleanup() + os.Exit(code) +} + // TODO: This test should be simplified ... func TestBlockStoreSaveLoadBlock(t *testing.T) { - state, bs := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + defer cleanup() require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") // check there are no blocks at various heights @@ -350,7 +367,8 @@ func TestLoadBlockMeta(t *testing.T) { } func TestBlockFetchAtHeight(t *testing.T) { - state, bs := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + state, bs, cleanup := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer))) + defer cleanup() require.Equal(t, bs.Height(), int64(0), "initially the height should be zero") block := makeBlock(bs.Height()+1, state, new(types.Commit)) diff --git a/config/toml.go b/config/toml.go index e842e9e37..b023e4e57 100644 --- a/config/toml.go +++ b/config/toml.go @@ -3,13 +3,16 @@ package config import ( "bytes" "fmt" - "os" + "io/ioutil" "path/filepath" "text/template" cmn "github.com/tendermint/tendermint/libs/common" ) +// DefaultDirPerm is the default permissions used when creating directories. +const DefaultDirPerm = 0700 + var configTemplate *template.Template func init() { @@ -24,13 +27,13 @@ func init() { // EnsureRoot creates the root, config, and data directories if they don't exist, // and panics if it fails. func EnsureRoot(rootDir string) { - if err := cmn.EnsureDir(rootDir, 0700); err != nil { + if err := cmn.EnsureDir(rootDir, DefaultDirPerm); err != nil { cmn.PanicSanity(err.Error()) } - if err := cmn.EnsureDir(filepath.Join(rootDir, defaultConfigDir), 0700); err != nil { + if err := cmn.EnsureDir(filepath.Join(rootDir, defaultConfigDir), DefaultDirPerm); err != nil { cmn.PanicSanity(err.Error()) } - if err := cmn.EnsureDir(filepath.Join(rootDir, defaultDataDir), 0700); err != nil { + if err := cmn.EnsureDir(filepath.Join(rootDir, defaultDataDir), DefaultDirPerm); err != nil { cmn.PanicSanity(err.Error()) } @@ -322,29 +325,17 @@ func ResetTestRoot(testName string) *Config { } func ResetTestRootWithChainID(testName string, chainID string) *Config { - rootDir := os.ExpandEnv("$HOME/.tendermint_test") - rootDir = filepath.Join(rootDir, testName) - // Remove ~/.tendermint_test_bak - if cmn.FileExists(rootDir + "_bak") { - if err := os.RemoveAll(rootDir + "_bak"); err != nil { - cmn.PanicSanity(err.Error()) - } + // create a unique, concurrency-safe test directory under os.TempDir() + rootDir, err := ioutil.TempDir("", fmt.Sprintf("%s-%s_", chainID, testName)) + if err != nil { + panic(err) } - // Move ~/.tendermint_test to ~/.tendermint_test_bak - if cmn.FileExists(rootDir) { - if err := os.Rename(rootDir, rootDir+"_bak"); err != nil { - cmn.PanicSanity(err.Error()) - } + // ensure config and data subdirs are created + if err := cmn.EnsureDir(filepath.Join(rootDir, defaultConfigDir), DefaultDirPerm); err != nil { + panic(err) } - // Create new dir - if err := cmn.EnsureDir(rootDir, 0700); err != nil { - cmn.PanicSanity(err.Error()) - } - if err := cmn.EnsureDir(filepath.Join(rootDir, defaultConfigDir), 0700); err != nil { - cmn.PanicSanity(err.Error()) - } - if err := cmn.EnsureDir(filepath.Join(rootDir, defaultDataDir), 0700); err != nil { - cmn.PanicSanity(err.Error()) + if err := cmn.EnsureDir(filepath.Join(rootDir, defaultDataDir), DefaultDirPerm); err != nil { + panic(err) } baseConfig := DefaultBaseConfig() diff --git a/config/toml_test.go b/config/toml_test.go index 59528db16..5910f10c5 100644 --- a/config/toml_test.go +++ b/config/toml_test.go @@ -48,6 +48,7 @@ func TestEnsureTestRoot(t *testing.T) { // create root dir cfg := ResetTestRoot(testName) + defer os.RemoveAll(cfg.RootDir) rootDir := cfg.RootDir // make sure config is set properly diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index ba69d0cc1..31a7badfd 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -13,10 +13,6 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - config = ResetConfig("consensus_byzantine_test") -} - //---------------------------------------------- // byzantine failures @@ -29,7 +25,8 @@ func init() { func TestByzantine(t *testing.T) { N := 4 logger := consensusLogger().With("test", "byzantine") - css := randConsensusNet(N, "consensus_byzantine_test", newMockTickerFunc(false), newCounter) + css, cleanup := randConsensusNet(N, "consensus_byzantine_test", newMockTickerFunc(false), newCounter) + defer cleanup() // give the byzantine validator a normal ticker ticker := NewTimeoutTicker() diff --git a/consensus/common_test.go b/consensus/common_test.go index ec24704ac..30c1e4b1f 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -37,8 +37,12 @@ const ( testSubscriber = "test-client" ) +// A cleanupFunc cleans up any config / test files created for a particular test. +type cleanupFunc func() + // genesis, chain_id, priv_val var config *cfg.Config // NOTE: must be reset for each _test.go file +var consensusReplayConfig *cfg.Config var ensureTimeout = time.Millisecond * 100 func ensureDir(dir string, mode os.FileMode) { @@ -248,6 +252,7 @@ func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} { // consensus states func newConsensusState(state sm.State, pv types.PrivValidator, app abci.Application) *ConsensusState { + config := cfg.ResetTestRoot("consensus_state_test") return newConsensusStateWithConfig(config, state, pv, app) } @@ -406,7 +411,7 @@ func ensureNewRound(roundCh <-chan interface{}, height int64, round int) { } func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, timeout int64) { - timeoutDuration := time.Duration(timeout*3) * time.Nanosecond + timeoutDuration := time.Duration(timeout*5) * time.Nanosecond ensureNewEvent(timeoutCh, height, round, timeoutDuration, "Timeout expired while waiting for NewTimeout event") } @@ -560,14 +565,17 @@ func consensusLogger() log.Logger { }).With("module", "consensus") } -func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application, configOpts ...func(*cfg.Config)) []*ConsensusState { +func randConsensusNet(nValidators int, testName string, tickerFunc func() TimeoutTicker, + appFunc func() abci.Application, configOpts ...func(*cfg.Config)) ([]*ConsensusState, cleanupFunc) { genDoc, privVals := randGenesisDoc(nValidators, false, 30) css := make([]*ConsensusState, nValidators) logger := consensusLogger() + configRootDirs := make([]string, 0, nValidators) for i := 0; i < nValidators; i++ { stateDB := dbm.NewMemDB() // each state needs its own db state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + configRootDirs = append(configRootDirs, thisConfig.RootDir) for _, opt := range configOpts { opt(thisConfig) } @@ -580,18 +588,26 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou css[i].SetTimeoutTicker(tickerFunc()) css[i].SetLogger(logger.With("validator", i, "module", "consensus")) } - return css + return css, func() { + for _, dir := range configRootDirs { + os.RemoveAll(dir) + } + } } // nPeers = nValidators + nNotValidator -func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerFunc func() TimeoutTicker, appFunc func() abci.Application) []*ConsensusState { +func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerFunc func() TimeoutTicker, + appFunc func() abci.Application) ([]*ConsensusState, cleanupFunc) { + genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower) css := make([]*ConsensusState, nPeers) logger := consensusLogger() + configRootDirs := make([]string, 0, nPeers) for i := 0; i < nPeers; i++ { stateDB := dbm.NewMemDB() // each state needs its own db state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + configRootDirs = append(configRootDirs, thisConfig.RootDir) ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal var privVal types.PrivValidator if i < nValidators { @@ -617,7 +633,11 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF css[i].SetTimeoutTicker(tickerFunc()) css[i].SetLogger(logger.With("validator", i, "module", "consensus")) } - return css + return css, func() { + for _, dir := range configRootDirs { + os.RemoveAll(dir) + } + } } func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int { @@ -713,6 +733,9 @@ func newCounter() abci.Application { } func newPersistentKVStore() abci.Application { - dir, _ := ioutil.TempDir("/tmp", "persistent-kvstore") + dir, err := ioutil.TempDir("", "persistent-kvstore") + if err != nil { + panic(err) + } return kvstore.NewPersistentKVStoreApplication(dir) } diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index bb4bf6eb9..cfbe40efd 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -3,6 +3,7 @@ package consensus import ( "encoding/binary" "fmt" + "os" "testing" "time" @@ -14,10 +15,6 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - config = ResetConfig("consensus_mempool_test") -} - // for testing func assertMempool(txn txNotifier) sm.Mempool { return txn.(sm.Mempool) @@ -25,6 +22,7 @@ func assertMempool(txn txNotifier) sm.Mempool { func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) { config := ResetConfig("consensus_mempool_txs_available_test") + defer os.RemoveAll(config.RootDir) config.Consensus.CreateEmptyBlocks = false state, privVals := randGenesisState(1, false, 10) cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication()) @@ -43,6 +41,7 @@ func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) { func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) { config := ResetConfig("consensus_mempool_txs_available_test") + defer os.RemoveAll(config.RootDir) config.Consensus.CreateEmptyBlocksInterval = ensureTimeout state, privVals := randGenesisState(1, false, 10) cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication()) @@ -58,6 +57,7 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) { func TestMempoolProgressInHigherRound(t *testing.T) { config := ResetConfig("consensus_mempool_txs_available_test") + defer os.RemoveAll(config.RootDir) config.Consensus.CreateEmptyBlocks = false state, privVals := randGenesisState(1, false, 10) cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication()) diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index d35eaf3c0..473c9d792 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -27,10 +27,6 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - config = ResetConfig("consensus_reactor_test") -} - //---------------------------------------------- // in-process testnets @@ -86,7 +82,8 @@ func stopConsensusNet(logger log.Logger, reactors []*ConsensusReactor, eventBuse // Ensure a testnet makes blocks func TestReactorBasic(t *testing.T) { N := 4 - css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + defer cleanup() reactors, eventChans, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // wait till everyone makes the first new block @@ -116,6 +113,7 @@ func TestReactorWithEvidence(t *testing.T) { stateDB := dbm.NewMemDB() // each state needs its own db state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) + defer os.RemoveAll(thisConfig.RootDir) ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal app := appFunc() vals := types.TM2PB.ValidatorUpdates(state.Validators) @@ -218,10 +216,11 @@ func (m *mockEvidencePool) IsCommitted(types.Evidence) bool { return false } // Ensure a testnet makes blocks when there are txs func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { N := 4 - css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter, + css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter, func(c *cfg.Config) { c.Consensus.CreateEmptyBlocks = false }) + defer cleanup() reactors, eventChans, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) @@ -239,7 +238,8 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { // Test we record stats about votes and block parts from other peers. func TestReactorRecordsVotesAndBlockParts(t *testing.T) { N := 4 - css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + defer cleanup() reactors, eventChans, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) @@ -263,7 +263,8 @@ func TestReactorRecordsVotesAndBlockParts(t *testing.T) { func TestReactorVotingPowerChange(t *testing.T) { nVals := 4 logger := log.TestingLogger() - css := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentKVStore) + css, cleanup := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentKVStore) + defer cleanup() reactors, eventChans, eventBuses := startConsensusNet(t, css, nVals) defer stopConsensusNet(logger, reactors, eventBuses) @@ -324,8 +325,8 @@ func TestReactorVotingPowerChange(t *testing.T) { func TestReactorValidatorSetChanges(t *testing.T) { nPeers := 7 nVals := 4 - css := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStore) - + css, cleanup := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStore) + defer cleanup() logger := log.TestingLogger() reactors, eventChans, eventBuses := startConsensusNet(t, css, nPeers) @@ -422,7 +423,8 @@ func TestReactorValidatorSetChanges(t *testing.T) { // Check we can make blocks with skip_timeout_commit=false func TestReactorWithTimeoutCommit(t *testing.T) { N := 4 - css := randConsensusNet(N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newCounter) + css, cleanup := randConsensusNet(N, "consensus_reactor_with_timeout_commit_test", newMockTickerFunc(false), newCounter) + defer cleanup() // override default SkipTimeoutCommit == true for tests for i := 0; i < N; i++ { css[i].config.SkipTimeoutCommit = false diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 297c13c3b..3472fc4bb 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -8,6 +8,7 @@ import ( "io/ioutil" "os" "path" + "path/filepath" "runtime" "testing" "time" @@ -30,10 +31,19 @@ import ( "github.com/tendermint/tendermint/types" ) -var consensusReplayConfig *cfg.Config - -func init() { +func TestMain(m *testing.M) { + config = ResetConfig("consensus_reactor_test") consensusReplayConfig = ResetConfig("consensus_replay_test") + configStateTest := ResetConfig("consensus_state_test") + configMempoolTest := ResetConfig("consensus_mempool_test") + configByzantineTest := ResetConfig("consensus_byzantine_test") + code := m.Run() + os.RemoveAll(config.RootDir) + os.RemoveAll(consensusReplayConfig.RootDir) + os.RemoveAll(configStateTest.RootDir) + os.RemoveAll(configMempoolTest.RootDir) + os.RemoveAll(configByzantineTest.RootDir) + os.Exit(code) } // These tests ensure we can always recover from failure at any part of the consensus process. @@ -51,7 +61,8 @@ func init() { // and which ones we need the wal for - then we'd also be able to only flush the // wal writer when we need to, instead of with every message. -func startNewConsensusStateAndWaitForBlock(t *testing.T, lastBlockHeight int64, blockDB dbm.DB, stateDB dbm.DB) { +func startNewConsensusStateAndWaitForBlock(t *testing.T, consensusReplayConfig *cfg.Config, + lastBlockHeight int64, blockDB dbm.DB, stateDB dbm.DB) { logger := log.TestingLogger() state, _ := sm.LoadStateFromDBOrGenesisFile(stateDB, consensusReplayConfig.GenesisFile()) privValidator := loadPrivValidator(consensusReplayConfig) @@ -59,7 +70,6 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, lastBlockHeight int64, cs.SetLogger(logger) bytes, _ := ioutil.ReadFile(cs.config.WalFile()) - // fmt.Printf("====== WAL: \n\r%s\n", bytes) t.Logf("====== WAL: \n\r%X\n", bytes) err := cs.Start() @@ -110,21 +120,22 @@ func TestWALCrash(t *testing.T) { 3}, } - for _, tc := range testCases { + for i, tc := range testCases { + consensusReplayConfig := ResetConfig(fmt.Sprintf("%s_%d", t.Name(), i)) t.Run(tc.name, func(t *testing.T) { - crashWALandCheckLiveness(t, tc.initFn, tc.heightToStop) + crashWALandCheckLiveness(t, consensusReplayConfig, tc.initFn, tc.heightToStop) }) } } -func crashWALandCheckLiveness(t *testing.T, initFn func(dbm.DB, *ConsensusState, context.Context), heightToStop int64) { +func crashWALandCheckLiveness(t *testing.T, consensusReplayConfig *cfg.Config, + initFn func(dbm.DB, *ConsensusState, context.Context), heightToStop int64) { walPaniced := make(chan error) crashingWal := &crashingWAL{panicCh: walPaniced, heightToStop: heightToStop} i := 1 LOOP: for { - // fmt.Printf("====== LOOP %d\n", i) t.Logf("====== LOOP %d\n", i) // create consensus state from a clean slate @@ -142,6 +153,7 @@ LOOP: // clean up WAL file from the previous iteration walFile := cs.config.WalFile() + ensureDir(filepath.Dir(walFile), 0700) os.Remove(walFile) // set crashing WAL @@ -163,7 +175,7 @@ LOOP: t.Logf("WAL paniced: %v", err) // make sure we can make blocks after a crash - startNewConsensusStateAndWaitForBlock(t, cs.Height, blockDB, stateDB) + startNewConsensusStateAndWaitForBlock(t, consensusReplayConfig, cs.Height, blockDB, stateDB) // stop consensus state and transactions sender (initFn) cs.Stop() @@ -269,29 +281,37 @@ var modes = []uint{0, 1, 2} // Sync from scratch func TestHandshakeReplayAll(t *testing.T) { - for _, m := range modes { - testHandshakeReplay(t, 0, m) + for i, m := range modes { + config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) + defer os.RemoveAll(config.RootDir) + testHandshakeReplay(t, config, 0, m) } } // Sync many, not from scratch func TestHandshakeReplaySome(t *testing.T) { - for _, m := range modes { - testHandshakeReplay(t, 1, m) + for i, m := range modes { + config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) + defer os.RemoveAll(config.RootDir) + testHandshakeReplay(t, config, 1, m) } } // Sync from lagging by one func TestHandshakeReplayOne(t *testing.T) { - for _, m := range modes { - testHandshakeReplay(t, NUM_BLOCKS-1, m) + for i, m := range modes { + config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) + defer os.RemoveAll(config.RootDir) + testHandshakeReplay(t, config, NUM_BLOCKS-1, m) } } // Sync from caught up func TestHandshakeReplayNone(t *testing.T) { - for _, m := range modes { - testHandshakeReplay(t, NUM_BLOCKS, m) + for i, m := range modes { + config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) + defer os.RemoveAll(config.RootDir) + testHandshakeReplay(t, config, NUM_BLOCKS, m) } } @@ -311,10 +331,8 @@ func tempWALWithData(data []byte) string { } // Make some blocks. Start a fresh app and apply nBlocks blocks. Then restart the app and sync it up with the remaining blocks -func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) { - config := ResetConfig("proxy_test_") - - walBody, err := WALWithNBlocks(NUM_BLOCKS) +func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uint) { + walBody, err := WALWithNBlocks(t, NUM_BLOCKS) require.NoError(t, err) walFile := tempWALWithData(walBody) config.Consensus.SetWalFile(walFile) @@ -631,6 +649,7 @@ func TestInitChainUpdateValidators(t *testing.T) { clientCreator := proxy.NewLocalClientCreator(app) config := ResetConfig("proxy_test_") + defer os.RemoveAll(config.RootDir) privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0) diff --git a/consensus/state_test.go b/consensus/state_test.go index e90035737..69de825e7 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -18,10 +18,6 @@ import ( "github.com/tendermint/tendermint/types" ) -func init() { - config = ResetConfig("consensus_state_test") -} - /* ProposeSuite diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index afb741626..42b5333a1 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -2,6 +2,7 @@ package types import ( "fmt" + "os" "testing" cfg "github.com/tendermint/tendermint/config" @@ -11,8 +12,11 @@ import ( var config *cfg.Config // NOTE: must be reset for each _test.go file -func init() { +func TestMain(m *testing.M) { config = cfg.ResetTestRoot("consensus_height_vote_set_test") + code := m.Run() + os.RemoveAll(config.RootDir) + os.Exit(code) } func TestPeerCatchupRounds(t *testing.T) { diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 83861d3ec..8ed041c6b 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -7,7 +7,7 @@ import ( "io" "os" "path/filepath" - "strings" + "testing" "time" "github.com/pkg/errors" @@ -28,8 +28,9 @@ import ( // stripped down version of node (proxy app, event bus, consensus state) with a // persistent kvstore application and special consensus wal instance // (byteBufferWAL) and waits until numBlocks are created. If the node fails to produce given numBlocks, it returns an error. -func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) { - config := getConfig() +func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { + config := getConfig(t) + defer os.RemoveAll(config.RootDir) app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator")) @@ -102,11 +103,11 @@ func WALGenerateNBlocks(wr io.Writer, numBlocks int) (err error) { } //WALWithNBlocks returns a WAL content with numBlocks. -func WALWithNBlocks(numBlocks int) (data []byte, err error) { +func WALWithNBlocks(t *testing.T, numBlocks int) (data []byte, err error) { var b bytes.Buffer wr := bufio.NewWriter(&b) - if err := WALGenerateNBlocks(wr, numBlocks); err != nil { + if err := WALGenerateNBlocks(t, wr, numBlocks); err != nil { return []byte{}, err } @@ -114,18 +115,6 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) { return b.Bytes(), nil } -// f**ing long, but unique for each test -func makePathname() string { - // get path - p, err := os.Getwd() - if err != nil { - panic(err) - } - // fmt.Println(p) - sep := string(filepath.Separator) - return strings.Replace(p, sep, "_", -1) -} - func randPort() int { // returns between base and base + spread base, spread := 20000, 20000 @@ -140,9 +129,8 @@ func makeAddrs() (string, string, string) { } // getConfig returns a config for test cases -func getConfig() *cfg.Config { - pathname := makePathname() - c := cfg.ResetTestRoot(fmt.Sprintf("%s_%d", pathname, cmn.RandInt())) +func getConfig(t *testing.T) *cfg.Config { + c := cfg.ResetTestRoot(t.Name()) // and we use random ports to run in parallel tm, rpc, grpc := makeAddrs() diff --git a/consensus/wal_test.go b/consensus/wal_test.go index 7ec338345..e1fb9ece0 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -48,7 +48,7 @@ func TestWALTruncate(t *testing.T) { //60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), when headBuf is full, truncate content will Flush to the file. //at this time, RotateFile is called, truncate content exist in each file. - err = WALGenerateNBlocks(wal.Group(), 60) + err = WALGenerateNBlocks(t, wal.Group(), 60) require.NoError(t, err) time.Sleep(1 * time.Millisecond) //wait groupCheckDuration, make sure RotateFile run @@ -116,7 +116,7 @@ func TestWALWritePanicsIfMsgIsTooBig(t *testing.T) { } func TestWALSearchForEndHeight(t *testing.T) { - walBody, err := WALWithNBlocks(6) + walBody, err := WALWithNBlocks(t, 6) if err != nil { t.Fatal(err) } diff --git a/lite/client/provider_test.go b/lite/client/provider_test.go index d8704a52e..015b0f3de 100644 --- a/lite/client/provider_test.go +++ b/lite/client/provider_test.go @@ -15,7 +15,8 @@ import ( func TestMain(m *testing.M) { app := kvstore.NewKVStoreApplication() - node := rpctest.StartTendermint(app) + node, cleanup := rpctest.StartTendermint(app) + defer cleanup() code := m.Run() @@ -28,6 +29,7 @@ func TestProvider(t *testing.T) { assert, require := assert.New(t), require.New(t) cfg := rpctest.GetConfig() + defer os.RemoveAll(cfg.RootDir) rpcAddr := cfg.RPC.ListenAddress genDoc, err := types.GenesisDocFromFile(cfg.GenesisFile()) if err != nil { diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index 9547f7713..7a9882595 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -27,13 +27,15 @@ var waitForEventTimeout = 5 * time.Second // TODO fix tests!! func TestMain(m *testing.M) { + var cleanup func() app := kvstore.NewKVStoreApplication() - node = rpctest.StartTendermint(app) + node, cleanup = rpctest.StartTendermint(app) code := m.Run() node.Stop() node.Wait() + cleanup() os.Exit(code) } diff --git a/mempool/bench_test.go b/mempool/bench_test.go index 68b033caa..8936f8dfb 100644 --- a/mempool/bench_test.go +++ b/mempool/bench_test.go @@ -11,7 +11,8 @@ import ( func BenchmarkReap(b *testing.B) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() size := 10000 for i := 0; i < size; i++ { diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index dd624262d..5e3c71eb2 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -25,7 +25,7 @@ import ( "github.com/tendermint/tendermint/types" ) -func newMempoolWithApp(cc proxy.ClientCreator) *Mempool { +func newMempoolWithApp(cc proxy.ClientCreator) (*Mempool, func()) { config := cfg.ResetTestRoot("mempool_test") appConnMem, _ := cc.NewABCIClient() @@ -36,7 +36,7 @@ func newMempoolWithApp(cc proxy.ClientCreator) *Mempool { } mempool := NewMempool(config.Mempool, appConnMem, 0) mempool.SetLogger(log.TestingLogger()) - return mempool + return mempool, func() { os.RemoveAll(config.RootDir) } } func ensureNoFire(t *testing.T, ch <-chan struct{}, timeoutMS int) { @@ -82,7 +82,8 @@ func checkTxs(t *testing.T, mempool *Mempool, count int) types.Txs { func TestReapMaxBytesMaxGas(t *testing.T) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() // Ensure gas calculation behaves as expected checkTxs(t, mempool, 1) @@ -130,7 +131,8 @@ func TestReapMaxBytesMaxGas(t *testing.T) { func TestMempoolFilters(t *testing.T) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() emptyTxArr := []types.Tx{[]byte{}} nopPreFilter := func(tx types.Tx) error { return nil } @@ -168,7 +170,8 @@ func TestMempoolFilters(t *testing.T) { func TestMempoolUpdateAddsTxsToCache(t *testing.T) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() mempool.Update(1, []types.Tx{[]byte{0x01}}, nil, nil) err := mempool.CheckTx([]byte{0x01}, nil) if assert.Error(t, err) { @@ -179,7 +182,8 @@ func TestMempoolUpdateAddsTxsToCache(t *testing.T) { func TestTxsAvailable(t *testing.T) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() mempool.EnableTxsAvailable() timeoutMS := 500 @@ -224,7 +228,9 @@ func TestSerialReap(t *testing.T) { app.SetOption(abci.RequestSetOption{Key: "serial", Value: "on"}) cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() + appConnCon, _ := cc.NewABCIClient() appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus")) err := appConnCon.Start() @@ -364,6 +370,7 @@ func TestMempoolCloseWAL(t *testing.T) { // 3. Create the mempool wcfg := cfg.DefaultMempoolConfig() wcfg.RootDir = rootDir + defer os.RemoveAll(wcfg.RootDir) app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) appConnMem, _ := cc.NewABCIClient() @@ -406,7 +413,8 @@ func txMessageSize(tx types.Tx) int { func TestMempoolMaxMsgSize(t *testing.T) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempl := newMempoolWithApp(cc) + mempl, cleanup := newMempoolWithApp(cc) + defer cleanup() testCases := []struct { len int diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index ad9ad8b40..51d130187 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -49,7 +49,8 @@ func makeAndConnectMempoolReactors(config *cfg.Config, N int) []*MempoolReactor for i := 0; i < N; i++ { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - mempool := newMempoolWithApp(cc) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() reactors[i] = NewMempoolReactor(config.Mempool, mempool) // so we dont start the consensus states reactors[i].SetLogger(logger.With("validator", i)) diff --git a/node/node_test.go b/node/node_test.go index d7907e88a..e370e5f24 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -31,6 +31,7 @@ import ( func TestNodeStartStop(t *testing.T) { config := cfg.ResetTestRoot("node_node_test") + defer os.RemoveAll(config.RootDir) // create & start node n, err := DefaultNewNode(config, log.TestingLogger()) @@ -90,6 +91,7 @@ func TestSplitAndTrimEmpty(t *testing.T) { func TestNodeDelayedStart(t *testing.T) { config := cfg.ResetTestRoot("node_delayed_start_test") + defer os.RemoveAll(config.RootDir) now := tmtime.Now() // create & start node @@ -104,6 +106,7 @@ func TestNodeDelayedStart(t *testing.T) { func TestNodeSetAppVersion(t *testing.T) { config := cfg.ResetTestRoot("node_app_version_test") + defer os.RemoveAll(config.RootDir) // create & start node n, err := DefaultNewNode(config, log.TestingLogger()) @@ -124,6 +127,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { addr := "tcp://" + testFreeAddr(t) config := cfg.ResetTestRoot("node_priv_val_tcp_test") + defer os.RemoveAll(config.RootDir) config.BaseConfig.PrivValidatorListenAddr = addr dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey()) @@ -153,6 +157,7 @@ func TestPrivValidatorListenAddrNoProtocol(t *testing.T) { addrNoPrefix := testFreeAddr(t) config := cfg.ResetTestRoot("node_priv_val_tcp_test") + defer os.RemoveAll(config.RootDir) config.BaseConfig.PrivValidatorListenAddr = addrNoPrefix _, err := DefaultNewNode(config, log.TestingLogger()) @@ -164,6 +169,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { defer os.Remove(tmpfile) // clean up config := cfg.ResetTestRoot("node_priv_val_tcp_test") + defer os.RemoveAll(config.RootDir) config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile dialer := privval.DialUnixFn(tmpfile) @@ -200,6 +206,7 @@ func testFreeAddr(t *testing.T) string { // mempool and evidence pool and validate it. func TestCreateProposalBlock(t *testing.T) { config := cfg.ResetTestRoot("node_create_proposal") + defer os.RemoveAll(config.RootDir) cc := proxy.NewLocalClientCreator(kvstore.NewKVStoreApplication()) proxyApp := proxy.NewAppConns(cc) err := proxyApp.Start() diff --git a/rpc/client/main_test.go b/rpc/client/main_test.go index 1e911bbe6..6961d6507 100644 --- a/rpc/client/main_test.go +++ b/rpc/client/main_test.go @@ -13,12 +13,14 @@ var node *nm.Node func TestMain(m *testing.M) { // start a tendermint node (and kvstore) in the background to test against + var cleanup func() app := kvstore.NewKVStoreApplication() - node = rpctest.StartTendermint(app) + node, cleanup = rpctest.StartTendermint(app) code := m.Run() // and shut down proper at the end node.Stop() node.Wait() + cleanup() os.Exit(code) } diff --git a/rpc/grpc/grpc_test.go b/rpc/grpc/grpc_test.go index ff05c835f..3a9994989 100644 --- a/rpc/grpc/grpc_test.go +++ b/rpc/grpc/grpc_test.go @@ -15,12 +15,13 @@ import ( func TestMain(m *testing.M) { // start a tendermint node in the background to test against app := kvstore.NewKVStoreApplication() - node := rpctest.StartTendermint(app) + node, cleanup := rpctest.StartTendermint(app) code := m.Run() // and shut down proper at the end node.Stop() node.Wait() + cleanup() os.Exit(code) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 67439b1da..50db418a2 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -100,8 +100,8 @@ func GetGRPCClient() core_grpc.BroadcastAPIClient { } // StartTendermint starts a test tendermint server in a go routine and returns when it is initialized -func StartTendermint(app abci.Application) *nm.Node { - node := NewTendermint(app) +func StartTendermint(app abci.Application) (*nm.Node, func()) { + node, cleanup := NewTendermint(app) err := node.Start() if err != nil { panic(err) @@ -113,11 +113,11 @@ func StartTendermint(app abci.Application) *nm.Node { fmt.Println("Tendermint running!") - return node + return node, cleanup } // NewTendermint creates a new tendermint server and sleeps forever -func NewTendermint(app abci.Application) *nm.Node { +func NewTendermint(app abci.Application) (*nm.Node, func()) { // Create & start node config := GetConfig() logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) @@ -138,5 +138,5 @@ func NewTendermint(app abci.Application) *nm.Node { if err != nil { panic(err) } - return node + return node, func() { os.RemoveAll(config.RootDir) } } diff --git a/state/state_test.go b/state/state_test.go index 6d5f8f46b..bdec63a68 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -5,6 +5,7 @@ import ( "fmt" "math" "math/big" + "os" "testing" "github.com/stretchr/testify/assert" @@ -28,7 +29,7 @@ func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, State) { state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile") - tearDown := func(t *testing.T) {} + tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) } return tearDown, stateDB, state } @@ -802,10 +803,10 @@ func TestLargeGenesisValidator(t *testing.T) { func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { const valSetSize = 2 tearDown, stateDB, state := setupTestCase(t) + defer tearDown(t) state.Validators = genValSet(valSetSize) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) SaveState(stateDB, state) - defer tearDown(t) nextHeight := state.LastBlockHeight + 1 @@ -825,11 +826,11 @@ func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { func TestManyValidatorChangesSaveLoad(t *testing.T) { const valSetSize = 7 tearDown, stateDB, state := setupTestCase(t) + defer tearDown(t) require.Equal(t, int64(0), state.LastBlockHeight) state.Validators = genValSet(valSetSize) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) SaveState(stateDB, state) - defer tearDown(t) _, valOld := state.Validators.GetByIndex(0) var pubkeyOld = valOld.PubKey From 8283ca7ddb5b56090aaa15e005b40782fa618ae7 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 18 Feb 2019 13:23:40 +0400 Subject: [PATCH 058/259] 3291 follow-up (#3323) * changelog: use issue number instead of PR number * follow up to #3291 - rpc/test/helpers.go add StopTendermint(node) func - remove ensureDir(filepath.Dir(walFile), 0700) - mempool/mempool_test.go add type cleanupFunc func() * cmd/show_validator: wrap err to make it more clear --- CHANGELOG_PENDING.md | 7 ++++--- blockchain/store_test.go | 3 ++- cmd/tendermint/commands/show_validator.go | 3 ++- consensus/common_test.go | 3 ++- consensus/replay_test.go | 7 ++----- lite/client/provider_test.go | 6 ++---- lite/proxy/query_test.go | 7 ++----- mempool/mempool_test.go | 6 +++++- node/node.go | 5 +++++ rpc/client/main_test.go | 8 +++----- rpc/grpc/grpc_test.go | 7 +++---- rpc/test/helpers.go | 18 +++++++++++++----- 12 files changed, 45 insertions(+), 35 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 07a8b6267..253d4f98d 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -26,9 +26,10 @@ Special thanks to external contributors on this release: * [consensus] \#3297 Flush WAL on stop to prevent data corruption during graceful shutdown -- [consensus] \#3310 Reset TriggeredTimeoutPrecommit before starting next height +- [consensus] \#3302 Reset TriggeredTimeoutPrecommit before starting next + height - [rpc] \#3251 Fix /net_info#peers#remote_ip format. New format spec: * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address -* [cmd] \#3314 Return an - error on `show_validator` when the private validator file does not exist +* [cmd] \#3314 Return an error on `show_validator` when the private validator + file does not exist diff --git a/blockchain/store_test.go b/blockchain/store_test.go index f1108e722..bd30bc6d2 100644 --- a/blockchain/store_test.go +++ b/blockchain/store_test.go @@ -22,7 +22,8 @@ import ( tmtime "github.com/tendermint/tendermint/types/time" ) -// A cleanupFunc cleans up any config / test files created for a particular test. +// A cleanupFunc cleans up any config / test files created for a particular +// test. type cleanupFunc func() // make a Commit with a single vote containing just the height and a timestamp diff --git a/cmd/tendermint/commands/show_validator.go b/cmd/tendermint/commands/show_validator.go index fbf14f2f6..d76d21970 100644 --- a/cmd/tendermint/commands/show_validator.go +++ b/cmd/tendermint/commands/show_validator.go @@ -3,6 +3,7 @@ package commands import ( "fmt" + "github.com/pkg/errors" "github.com/spf13/cobra" cmn "github.com/tendermint/tendermint/libs/common" @@ -25,7 +26,7 @@ func showValidator(cmd *cobra.Command, args []string) error { pv := privval.LoadFilePV(keyFilePath, config.PrivValidatorStateFile()) bz, err := cdc.MarshalJSON(pv.GetPubKey()) if err != nil { - return err + return errors.Wrap(err, "failed to marshal private validator pubkey") } fmt.Println(string(bz)) diff --git a/consensus/common_test.go b/consensus/common_test.go index 30c1e4b1f..a9fb9e080 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -37,7 +37,8 @@ const ( testSubscriber = "test-client" ) -// A cleanupFunc cleans up any config / test files created for a particular test. +// A cleanupFunc cleans up any config / test files created for a particular +// test. type cleanupFunc func() // genesis, chain_id, priv_val diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 3472fc4bb..136e45b61 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -8,7 +8,6 @@ import ( "io/ioutil" "os" "path" - "path/filepath" "runtime" "testing" "time" @@ -18,17 +17,16 @@ import ( "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" auto "github.com/tendermint/tendermint/libs/autofile" dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/version" - - cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + "github.com/tendermint/tendermint/version" ) func TestMain(m *testing.M) { @@ -153,7 +151,6 @@ LOOP: // clean up WAL file from the previous iteration walFile := cs.config.WalFile() - ensureDir(filepath.Dir(walFile), 0700) os.Remove(walFile) // set crashing WAL diff --git a/lite/client/provider_test.go b/lite/client/provider_test.go index 015b0f3de..df49bdd1f 100644 --- a/lite/client/provider_test.go +++ b/lite/client/provider_test.go @@ -15,13 +15,11 @@ import ( func TestMain(m *testing.M) { app := kvstore.NewKVStoreApplication() - node, cleanup := rpctest.StartTendermint(app) - defer cleanup() + node := rpctest.StartTendermint(app) code := m.Run() - node.Stop() - node.Wait() + rpctest.StopTendermint(node) os.Exit(code) } diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index 7a9882595..c1450a5e6 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -27,15 +27,12 @@ var waitForEventTimeout = 5 * time.Second // TODO fix tests!! func TestMain(m *testing.M) { - var cleanup func() app := kvstore.NewKVStoreApplication() - node, cleanup = rpctest.StartTendermint(app) + node = rpctest.StartTendermint(app) code := m.Run() - node.Stop() - node.Wait() - cleanup() + rpctest.StopTendermint(node) os.Exit(code) } diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 5e3c71eb2..5d737e190 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -25,7 +25,11 @@ import ( "github.com/tendermint/tendermint/types" ) -func newMempoolWithApp(cc proxy.ClientCreator) (*Mempool, func()) { +// A cleanupFunc cleans up any config / test files created for a particular +// test. +type cleanupFunc func() + +func newMempoolWithApp(cc proxy.ClientCreator) (*Mempool, cleanupFunc) { config := cfg.ResetTestRoot("mempool_test") appConnMem, _ := cc.NewABCIClient() diff --git a/node/node.go b/node/node.go index 969452c40..e5ddd09cc 100644 --- a/node/node.go +++ b/node/node.go @@ -793,6 +793,11 @@ func (n *Node) ProxyApp() proxy.AppConns { return n.proxyApp } +// Config returns the Node's config. +func (n *Node) Config() *cfg.Config { + return n.config +} + //------------------------------------------------------------------------------ func (n *Node) Listeners() []string { diff --git a/rpc/client/main_test.go b/rpc/client/main_test.go index 6961d6507..6ec7b7b0e 100644 --- a/rpc/client/main_test.go +++ b/rpc/client/main_test.go @@ -13,14 +13,12 @@ var node *nm.Node func TestMain(m *testing.M) { // start a tendermint node (and kvstore) in the background to test against - var cleanup func() app := kvstore.NewKVStoreApplication() - node, cleanup = rpctest.StartTendermint(app) + node = rpctest.StartTendermint(app) + code := m.Run() // and shut down proper at the end - node.Stop() - node.Wait() - cleanup() + rpctest.StopTendermint(node) os.Exit(code) } diff --git a/rpc/grpc/grpc_test.go b/rpc/grpc/grpc_test.go index 3a9994989..b82e52226 100644 --- a/rpc/grpc/grpc_test.go +++ b/rpc/grpc/grpc_test.go @@ -15,13 +15,12 @@ import ( func TestMain(m *testing.M) { // start a tendermint node in the background to test against app := kvstore.NewKVStoreApplication() - node, cleanup := rpctest.StartTendermint(app) + node := rpctest.StartTendermint(app) + code := m.Run() // and shut down proper at the end - node.Stop() - node.Wait() - cleanup() + rpctest.StopTendermint(node) os.Exit(code) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 50db418a2..10d165625 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -100,8 +100,8 @@ func GetGRPCClient() core_grpc.BroadcastAPIClient { } // StartTendermint starts a test tendermint server in a go routine and returns when it is initialized -func StartTendermint(app abci.Application) (*nm.Node, func()) { - node, cleanup := NewTendermint(app) +func StartTendermint(app abci.Application) *nm.Node { + node := NewTendermint(app) err := node.Start() if err != nil { panic(err) @@ -113,11 +113,19 @@ func StartTendermint(app abci.Application) (*nm.Node, func()) { fmt.Println("Tendermint running!") - return node, cleanup + return node +} + +// StopTendermint stops a test tendermint server, waits until it's stopped and +// cleans up test/config files. +func StopTendermint(node *nm.Node) { + node.Stop() + node.Wait() + os.RemoveAll(node.Config().RootDir) } // NewTendermint creates a new tendermint server and sleeps forever -func NewTendermint(app abci.Application) (*nm.Node, func()) { +func NewTendermint(app abci.Application) *nm.Node { // Create & start node config := GetConfig() logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) @@ -138,5 +146,5 @@ func NewTendermint(app abci.Application) (*nm.Node, func()) { if err != nil { panic(err) } - return node, func() { os.RemoveAll(config.RootDir) } + return node } From d2c7f8dbcf238ccc3a2469e350523c7ed28be154 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Mon, 18 Feb 2019 11:08:22 +0100 Subject: [PATCH 059/259] p2p: check secret conn id matches dialed id (#3321) ref: [#3010 (comment)](https://github.com/tendermint/tendermint/issues/3010#issuecomment-464287627) > I tried searching for code where we authenticate a peer against its NetAddress.ID and couldn't find it. I don't see a reason to switch to Noise, but a need to ensure that the node's ID is authenticated e.g. after dialing from the address book. * p2p: check secret conn id matches dialed id * Fix all p2p tests & make code compile * add simple test for dialing with wrong ID * update changelog * address review comments * yet another place where to use IDAddressString and fix testSetupMultiplexTransport --- CHANGELOG_PENDING.md | 1 + p2p/transport.go | 30 +++++++++++++++++----- p2p/transport_test.go | 59 ++++++++++++++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 253d4f98d..fa96c00e1 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -33,3 +33,4 @@ Special thanks to external contributors on this release: * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address * [cmd] \#3314 Return an error on `show_validator` when the private validator file does not exist +* [p2p] \#3321 Authenticate a peer against its NetAddress.ID while dialing diff --git a/p2p/transport.go b/p2p/transport.go index 2d4420a11..d1bccf9b8 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -194,7 +194,7 @@ func (mt *MultiplexTransport) Dial( return nil, err } - secretConn, nodeInfo, err := mt.upgrade(c) + secretConn, nodeInfo, err := mt.upgrade(c, &addr) if err != nil { return nil, err } @@ -262,7 +262,7 @@ func (mt *MultiplexTransport) acceptPeers() { err := mt.filterConn(c) if err == nil { - secretConn, nodeInfo, err = mt.upgrade(c) + secretConn, nodeInfo, err = mt.upgrade(c, nil) } select { @@ -279,9 +279,9 @@ func (mt *MultiplexTransport) acceptPeers() { // Cleanup removes the given address from the connections set and // closes the connection. -func (mt *MultiplexTransport) Cleanup(peer Peer) { - mt.conns.RemoveAddr(peer.RemoteAddr()) - _ = peer.CloseConn() +func (mt *MultiplexTransport) Cleanup(p Peer) { + mt.conns.RemoveAddr(p.RemoteAddr()) + _ = p.CloseConn() } func (mt *MultiplexTransport) cleanup(c net.Conn) error { @@ -335,6 +335,7 @@ func (mt *MultiplexTransport) filterConn(c net.Conn) (err error) { func (mt *MultiplexTransport) upgrade( c net.Conn, + dialedAddr *NetAddress, ) (secretConn *conn.SecretConnection, nodeInfo NodeInfo, err error) { defer func() { if err != nil { @@ -351,6 +352,23 @@ func (mt *MultiplexTransport) upgrade( } } + // For outgoing conns, ensure connection key matches dialed key. + connID := PubKeyToID(secretConn.RemotePubKey()) + if dialedAddr != nil { + if dialedID := dialedAddr.ID; connID != dialedID { + return nil, nil, ErrRejected{ + conn: c, + id: connID, + err: fmt.Errorf( + "conn.ID (%v) dialed ID (%v) missmatch", + connID, + dialedID, + ), + isAuthFailure: true, + } + } + } + nodeInfo, err = handshake(secretConn, mt.handshakeTimeout, mt.nodeInfo) if err != nil { return nil, nil, ErrRejected{ @@ -369,7 +387,7 @@ func (mt *MultiplexTransport) upgrade( } // Ensure connection key matches self reported key. - if connID := PubKeyToID(secretConn.RemotePubKey()); connID != nodeInfo.ID() { + if connID != nodeInfo.ID() { return nil, nil, ErrRejected{ conn: c, id: connID, diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 7d9c17fb4..81f9d1b8e 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -160,8 +160,7 @@ func TestTransportMultiplexAcceptMultiple(t *testing.T) { }, ) ) - - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -230,7 +229,7 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { // Simulate slow Peer. go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -281,8 +280,7 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { }, ) ) - - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -328,7 +326,7 @@ func TestTransportMultiplexValidateNodeInfo(t *testing.T) { ) ) - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -371,8 +369,7 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) - - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -401,6 +398,38 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) { } } +func TestTransportMultiplexDialRejectWrongID(t *testing.T) { + mt := testSetupMultiplexTransport(t) + + var ( + pv = ed25519.GenPrivKey() + dialer = newMultiplexTransport( + testNodeInfo(PubKeyToID(pv.PubKey()), ""), // Should not be empty + NodeKey{ + PrivKey: pv, + }, + ) + ) + + wrongID := PubKeyToID(ed25519.GenPrivKey().PubKey()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(wrongID, mt.listener.Addr().String())) + if err != nil { + t.Fatalf("invalid address with ID: %v", err) + } + + _, err = dialer.Dial(*addr, peerConfig{}) + if err != nil { + t.Logf("connection failed: %v", err) + if err, ok := err.(ErrRejected); ok { + if !err.IsAuthFailure() { + t.Errorf("expected auth failure") + } + } else { + t.Errorf("expected ErrRejected") + } + } +} + func TestTransportMultiplexRejectIncompatible(t *testing.T) { mt := testSetupMultiplexTransport(t) @@ -416,8 +445,7 @@ func TestTransportMultiplexRejectIncompatible(t *testing.T) { }, ) ) - - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -448,7 +476,7 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) if err != nil { errc <- err return @@ -466,7 +494,7 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { if err := <-errc; err != nil { if err, ok := err.(ErrRejected); ok { if !err.IsSelf() { - t.Errorf("expected to reject self") + t.Errorf("expected to reject self, got: %v", err) } } else { t.Errorf("expected ErrRejected") @@ -478,7 +506,7 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { _, err := mt.Accept(peerConfig{}) if err, ok := err.(ErrRejected); ok { if !err.IsSelf() { - t.Errorf("expected to reject self") + t.Errorf("expected to reject self, got: %v", err) } } else { t.Errorf("expected ErrRejected") @@ -566,9 +594,10 @@ func TestTransportHandshake(t *testing.T) { func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport { var ( pv = ed25519.GenPrivKey() + id = PubKeyToID(pv.PubKey()) mt = newMultiplexTransport( testNodeInfo( - PubKeyToID(pv.PubKey()), "transport", + id, "transport", ), NodeKey{ PrivKey: pv, @@ -576,7 +605,7 @@ func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport { ) ) - addr, err := NewNetAddressStringWithOptionalID("127.0.0.1:0") + addr, err := NewNetAddressStringWithOptionalID(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } From 9d4f59b8366ad393410051e09672b76578545d9e Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 18 Feb 2019 15:27:07 +0400 Subject: [PATCH 060/259] update changelog and bump version --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 13 ------------- version/version.go | 2 +- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1842c8e32..3bd9051aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,33 @@ # Changelog +## v0.30.1 + +*February 18th, 2019* + +This release fixes the state machine halt and DataCorruptionError after restart +in `game_of_stakes_6`. It also adds a check that validates the NetAddress.ID of +the peer we're dialing. + +### IMPROVEMENTS: + +- [config] [\#3291](https://github.com/tendermint/tendermint/issues/3291) Make + config.ResetTestRootWithChainID() create concurrency-safe test directories. + +### BUG FIXES: + +* [consensus] [\#3295](https://github.com/tendermint/tendermint/issues/3295) + Flush WAL on stop to prevent data corruption during graceful shutdown +- [consensus] [\#3302](https://github.com/tendermint/tendermint/issues/3302) + Reset TriggeredTimeoutPrecommit before starting next height +- [rpc] [\#3251](https://github.com/tendermint/tendermint/issues/3251) Fix + /net_info#peers#remote_ip format. New format spec: + * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address + * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address +* [cmd] [\#3314](https://github.com/tendermint/tendermint/issues/3314) Return + an error on `show_validator` when the private validator file does not exist +* [p2p] [\#3010](https://github.com/tendermint/tendermint/issues/3010#issuecomment-464287627) + Authenticate a peer against its NetAddress.ID while dialing + ## v0.30.0 *February 8th, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fa96c00e1..640985836 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,17 +20,4 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: -- [config] \#3291 Make config.ResetTestRootWithChainID() create concurrency-safe test directories. - ### BUG FIXES: - -* [consensus] \#3297 Flush WAL on stop to prevent data corruption during - graceful shutdown -- [consensus] \#3302 Reset TriggeredTimeoutPrecommit before starting next - height -- [rpc] \#3251 Fix /net_info#peers#remote_ip format. New format spec: - * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address - * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address -* [cmd] \#3314 Return an error on `show_validator` when the private validator - file does not exist -* [p2p] \#3321 Authenticate a peer against its NetAddress.ID while dialing diff --git a/version/version.go b/version/version.go index 37a0da78d..1f30978c1 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.30.0" + TMCoreSemVer = "0.30.1" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From dff3deb2a95474ee1a10fc6aa7fd0a0a9a6aee94 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Wed, 20 Feb 2019 07:45:18 +0200 Subject: [PATCH 061/259] cs: sync WAL more frequently (#3300) As per #3043, this adds a ticker to sync the WAL every 2s while the WAL is running. * Flush WAL every 2s This adds a ticker that flushes the WAL every 2s while the WAL is running. This is related to #3043. * Fix spelling * Increase timeout to 2mins for slower build environments * Make WAL sync interval configurable * Add TODO to replace testChan with more comprehensive testBus * Remove extraneous debug statement * Remove testChan in favour of using system time As per https://github.com/tendermint/tendermint/pull/3300#discussion_r255886586, this removes the `testChan` WAL member and replaces the approach with a system time-oriented one. In this new approach, we keep track of the system time at which each flush and periodic flush successfully occurred. The naming of the various functions is also updated here to be more consistent with "flushing" as opposed to "sync'ing". * Update naming convention and ensure lock for timestamp update * Add Flush method as part of WAL interface Adds a `Flush` method as part of the WAL interface to enforce the idea that we can manually trigger a WAL flush from outside of the WAL. This is employed in the consensus state management to flush the WAL prior to signing votes/proposals, as per https://github.com/tendermint/tendermint/issues/3043#issuecomment-453853630 * Update CHANGELOG_PENDING * Remove mutex approach and replace with DI The dependency injection approach to dealing with testing concerns could allow similar effects to some kind of "testing bus"-based approach. This commit introduces an example of this, where instead of relying on (potentially fragile) timing of things between the code and the test, we inject code into the function under test that can signal the test through a channel. This allows us to avoid the `time.Sleep()`-based approach previously employed. * Update comment on WAL flushing during vote signing Co-Authored-By: thanethomson * Simplify flush interval definition Co-Authored-By: thanethomson * Expand commentary on WAL disk flushing Co-Authored-By: thanethomson * Add broken test to illustrate WAL sync test problem Removes test-related state (dependency injection code) from the WAL data structure and adds test code to illustrate the problem with using `WALGenerateNBlocks` and `wal.SearchForEndHeight` to test periodic sync'ing. * Fix test error messages * Use WAL group buffer size to check for flush A function is added to `libs/autofile/group.go#Group` in order to return the size of the buffered data (i.e. data that has not yet been flushed to disk). The test now checks that, prior to a `time.Sleep`, the group buffer has data in it. After the `time.Sleep` (during which time the periodic flush should have been called), the buffer should be empty. * Remove config root dir removal from #3291 * Add godoc for NewWAL mentioning periodic sync --- consensus/replay_test.go | 21 ++++++------- consensus/state.go | 6 ++++ consensus/wal.go | 48 +++++++++++++++++++++++++++--- consensus/wal_generator.go | 5 ++-- consensus/wal_test.go | 60 ++++++++++++++++++++++++++++++++------ libs/autofile/group.go | 7 +++++ 6 files changed, 121 insertions(+), 26 deletions(-) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 136e45b61..2146f21d1 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -83,7 +83,7 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, consensusReplayConfig * require.NoError(t, err) select { case <-newBlockCh: - case <-time.After(60 * time.Second): + case <-time.After(120 * time.Second): t.Fatalf("Timed out waiting for new block (see trace above)") } } @@ -128,8 +128,8 @@ func TestWALCrash(t *testing.T) { func crashWALandCheckLiveness(t *testing.T, consensusReplayConfig *cfg.Config, initFn func(dbm.DB, *ConsensusState, context.Context), heightToStop int64) { - walPaniced := make(chan error) - crashingWal := &crashingWAL{panicCh: walPaniced, heightToStop: heightToStop} + walPanicked := make(chan error) + crashingWal := &crashingWAL{panicCh: walPanicked, heightToStop: heightToStop} i := 1 LOOP: @@ -168,8 +168,8 @@ LOOP: i++ select { - case err := <-walPaniced: - t.Logf("WAL paniced: %v", err) + case err := <-walPanicked: + t.Logf("WAL panicked: %v", err) // make sure we can make blocks after a crash startNewConsensusStateAndWaitForBlock(t, consensusReplayConfig, cs.Height, blockDB, stateDB) @@ -190,14 +190,14 @@ LOOP: // crashingWAL is a WAL which crashes or rather simulates a crash during Save // (before and after). It remembers a message for which we last panicked -// (lastPanicedForMsgIndex), so we don't panic for it in subsequent iterations. +// (lastPanickedForMsgIndex), so we don't panic for it in subsequent iterations. type crashingWAL struct { next WAL panicCh chan error heightToStop int64 - msgIndex int // current message index - lastPanicedForMsgIndex int // last message for which we panicked + msgIndex int // current message index + lastPanickedForMsgIndex int // last message for which we panicked } // WALWriteError indicates a WAL crash. @@ -232,8 +232,8 @@ func (w *crashingWAL) Write(m WALMessage) { return } - if w.msgIndex > w.lastPanicedForMsgIndex { - w.lastPanicedForMsgIndex = w.msgIndex + if w.msgIndex > w.lastPanickedForMsgIndex { + w.lastPanickedForMsgIndex = w.msgIndex _, file, line, _ := runtime.Caller(1) w.panicCh <- WALWriteError{fmt.Sprintf("failed to write %T to WAL (fileline: %s:%d)", m, file, line)} runtime.Goexit() @@ -255,6 +255,7 @@ func (w *crashingWAL) SearchForEndHeight(height int64, options *WALSearchOptions func (w *crashingWAL) Start() error { return w.next.Start() } func (w *crashingWAL) Stop() error { return w.next.Stop() } func (w *crashingWAL) Wait() { w.next.Wait() } +func (w *crashingWAL) Flush() error { return w.Group().Flush() } //------------------------------------------------------------------------------------------ // Handshake Tests diff --git a/consensus/state.go b/consensus/state.go index 08e1da267..8ae17ea6c 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -909,6 +909,9 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) { } } + // Flush the WAL. Otherwise, we may not recompute the same proposal to sign, and the privValidator will refuse to sign anything. + cs.wal.Flush() + // Make proposal propBlockId := types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()} proposal := types.NewProposal(height, round, cs.ValidRound, propBlockId) @@ -1674,6 +1677,9 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, } func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) { + // Flush the WAL. Otherwise, we may not recompute the same vote to sign, and the privValidator will refuse to sign anything. + cs.wal.Flush() + addr := cs.privValidator.GetPubKey().Address() valIndex, _ := cs.Validators.GetByAddress(addr) diff --git a/consensus/wal.go b/consensus/wal.go index f8988691c..b69a99411 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -21,6 +21,9 @@ import ( const ( // must be greater than types.BlockPartSizeBytes + a few bytes maxMsgSizeBytes = 1024 * 1024 // 1MB + + // how often the WAL should be sync'd during period sync'ing + walDefaultFlushInterval = 2 * time.Second ) //-------------------------------------------------------- @@ -56,6 +59,7 @@ type WAL interface { WriteSync(WALMessage) Group() *auto.Group SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) + Flush() error Start() error Stop() error @@ -72,8 +76,14 @@ type baseWAL struct { group *auto.Group enc *WALEncoder + + flushTicker *time.Ticker + flushInterval time.Duration } +// NewWAL attempts to create a new write-ahead logger based on `baseWAL`, which +// implements all of the required WAL functionality. This base WAL also flushes +// data to disk every 2s. func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) { err := cmn.EnsureDir(filepath.Dir(walFile), 0700) if err != nil { @@ -85,13 +95,19 @@ func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) return nil, err } wal := &baseWAL{ - group: group, - enc: NewWALEncoder(group), + group: group, + enc: NewWALEncoder(group), + flushInterval: walDefaultFlushInterval, } wal.BaseService = *cmn.NewBaseService(nil, "baseWAL", wal) return wal, nil } +// SetFlushInterval allows us to override the periodic flush interval for the WAL. +func (wal *baseWAL) SetFlushInterval(i time.Duration) { + wal.flushInterval = i +} + func (wal *baseWAL) Group() *auto.Group { return wal.group } @@ -109,14 +125,37 @@ func (wal *baseWAL) OnStart() error { wal.WriteSync(EndHeightMessage{0}) } err = wal.group.Start() + wal.flushTicker = time.NewTicker(wal.flushInterval) + go wal.processFlushTicks() return err } +// processFlushTicks allows us to periodically attempt to sync the WAL to disk. +func (wal *baseWAL) processFlushTicks() { + for { + select { + case <-wal.flushTicker.C: + err := wal.Flush() + if err != nil { + wal.Logger.Error("Periodic WAL flush failed", "err", err) + } + case <-wal.Quit(): + return + } + } +} + +// Flush will attempt to flush the underlying group's data to disk. +func (wal *baseWAL) Flush() error { + return wal.group.Flush() +} + // Stop the underlying autofile group. // Use Wait() to ensure it's finished shutting down // before cleaning up files. func (wal *baseWAL) OnStop() { - wal.group.Flush() + wal.flushTicker.Stop() + wal.Flush() wal.group.Stop() wal.group.Close() } @@ -150,7 +189,7 @@ func (wal *baseWAL) WriteSync(msg WALMessage) { } wal.Write(msg) - if err := wal.group.Flush(); err != nil { + if err := wal.Flush(); err != nil { panic(fmt.Sprintf("Error flushing consensus wal buf to file. Error: %v \n", err)) } } @@ -343,3 +382,4 @@ func (nilWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *a func (nilWAL) Start() error { return nil } func (nilWAL) Stop() error { return nil } func (nilWAL) Wait() {} +func (nilWAL) Flush() error { return nil } diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 8ed041c6b..9375d5dcc 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -5,7 +5,6 @@ import ( "bytes" "fmt" "io" - "os" "path/filepath" "testing" "time" @@ -24,13 +23,12 @@ import ( "github.com/tendermint/tendermint/types" ) -// WALGenerateNBlocks generates a consensus WAL. It does this by spining up a +// WALGenerateNBlocks generates a consensus WAL. It does this by spinning up a // stripped down version of node (proxy app, event bus, consensus state) with a // persistent kvstore application and special consensus wal instance // (byteBufferWAL) and waits until numBlocks are created. If the node fails to produce given numBlocks, it returns an error. func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { config := getConfig(t) - defer os.RemoveAll(config.RootDir) app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator")) @@ -204,3 +202,4 @@ func (w *byteBufferWAL) SearchForEndHeight(height int64, options *WALSearchOptio func (w *byteBufferWAL) Start() error { return nil } func (w *byteBufferWAL) Stop() error { return nil } func (w *byteBufferWAL) Wait() {} +func (w *byteBufferWAL) Flush() error { return nil } diff --git a/consensus/wal_test.go b/consensus/wal_test.go index e1fb9ece0..a2c80be3e 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -3,7 +3,6 @@ package consensus import ( "bytes" "crypto/rand" - "fmt" "io/ioutil" "os" "path/filepath" @@ -22,6 +21,10 @@ import ( "github.com/stretchr/testify/require" ) +const ( + walTestFlushInterval = time.Duration(100) * time.Millisecond +) + func TestWALTruncate(t *testing.T) { walDir, err := ioutil.TempDir("", "wal") require.NoError(t, err) @@ -57,9 +60,9 @@ func TestWALTruncate(t *testing.T) { h := int64(50) gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) - assert.NoError(t, err, fmt.Sprintf("expected not to err on height %d", h)) - assert.True(t, found, fmt.Sprintf("expected to find end height for %d", h)) - assert.NotNil(t, gr, "expected group not to be nil") + assert.NoError(t, err, "expected not to err on height %d", h) + assert.True(t, found, "expected to find end height for %d", h) + assert.NotNil(t, gr) defer gr.Close() dec := NewWALDecoder(gr) @@ -67,7 +70,7 @@ func TestWALTruncate(t *testing.T) { assert.NoError(t, err, "expected to decode a message") rs, ok := msg.Msg.(tmtypes.EventDataRoundState) assert.True(t, ok, "expected message of type EventDataRoundState") - assert.Equal(t, rs.Height, h+1, fmt.Sprintf("wrong height")) + assert.Equal(t, rs.Height, h+1, "wrong height") } func TestWALEncoderDecoder(t *testing.T) { @@ -128,9 +131,9 @@ func TestWALSearchForEndHeight(t *testing.T) { h := int64(3) gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) - assert.NoError(t, err, fmt.Sprintf("expected not to err on height %d", h)) - assert.True(t, found, fmt.Sprintf("expected to find end height for %d", h)) - assert.NotNil(t, gr, "expected group not to be nil") + assert.NoError(t, err, "expected not to err on height %d", h) + assert.True(t, found, "expected to find end height for %d", h) + assert.NotNil(t, gr) defer gr.Close() dec := NewWALDecoder(gr) @@ -138,7 +141,46 @@ func TestWALSearchForEndHeight(t *testing.T) { assert.NoError(t, err, "expected to decode a message") rs, ok := msg.Msg.(tmtypes.EventDataRoundState) assert.True(t, ok, "expected message of type EventDataRoundState") - assert.Equal(t, rs.Height, h+1, fmt.Sprintf("wrong height")) + assert.Equal(t, rs.Height, h+1, "wrong height") +} + +func TestWALPeriodicSync(t *testing.T) { + walDir, err := ioutil.TempDir("", "wal") + require.NoError(t, err) + defer os.RemoveAll(walDir) + + walFile := filepath.Join(walDir, "wal") + wal, err := NewWAL(walFile, autofile.GroupCheckDuration(1*time.Millisecond)) + require.NoError(t, err) + + wal.SetFlushInterval(walTestFlushInterval) + wal.SetLogger(log.TestingLogger()) + + require.NoError(t, wal.Start()) + defer func() { + wal.Stop() + wal.Wait() + }() + + err = WALGenerateNBlocks(t, wal.Group(), 5) + require.NoError(t, err) + + // We should have data in the buffer now + assert.NotZero(t, wal.Group().Buffered()) + + time.Sleep(walTestFlushInterval + (10 * time.Millisecond)) + + // The data should have been flushed by the periodic sync + assert.Zero(t, wal.Group().Buffered()) + + h := int64(4) + gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) + assert.NoError(t, err, "expected not to err on height %d", h) + assert.True(t, found, "expected to find end height for %d", h) + assert.NotNil(t, gr) + if gr != nil { + gr.Close() + } } /* diff --git a/libs/autofile/group.go b/libs/autofile/group.go index 7e9269461..cafbb6d7e 100644 --- a/libs/autofile/group.go +++ b/libs/autofile/group.go @@ -209,6 +209,13 @@ func (g *Group) WriteLine(line string) error { return err } +// Buffered returns the size of the currently buffered data. +func (g *Group) Buffered() int { + g.mtx.Lock() + defer g.mtx.Unlock() + return g.headBuf.Buffered() +} + // Flush writes any buffered data to the underlying file and commits the // current content of the file to stable storage. func (g *Group) Flush() error { From db5d7602fea6a3418d4ae83511c1a35cf9e1c9ca Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Wed, 20 Feb 2019 14:34:52 +0300 Subject: [PATCH 062/259] docs: fix rpc Tx() method docs (#3331) --- rpc/core/tx.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rpc/core/tx.go b/rpc/core/tx.go index f1bfd56a1..aa439218b 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -16,7 +16,7 @@ import ( // place. // // ```shell -// curl "localhost:26657/tx?hash=0x2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF" +// curl "localhost:26657/tx?hash=0xF87370F68C82D9AC7201248ECA48CEC5F16FFEC99C461C1B2961341A2FE9C1C8" // ``` // // ```go @@ -26,7 +26,8 @@ import ( // // handle error // } // defer client.Stop() -// tx, err := client.Tx([]byte("2B8EC32BA2579B3B8606E42C06DE2F7AFA2556EF"), true) +// hashBytes, err := hex.DecodeString("F87370F68C82D9AC7201248ECA48CEC5F16FFEC99C461C1B2961341A2FE9C1C8") +// tx, err := client.Tx(hashBytes, true) // ``` // // > The above command returns JSON structured like this: From f2351dc758e5c8cf6bc6c5c32931daa8ae6e49a9 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 20 Feb 2019 10:05:57 -0500 Subject: [PATCH 063/259] update changelog --- CHANGELOG.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3bd9051aa..42e8761aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,31 +2,31 @@ ## v0.30.1 -*February 18th, 2019* +*February 20th, 2019* -This release fixes the state machine halt and DataCorruptionError after restart -in `game_of_stakes_6`. It also adds a check that validates the NetAddress.ID of -the peer we're dialing. +This release fixes a consensus halt and a DataCorruptionError after restart +discovered in `game_of_stakes_6`. It also fixes a security issue in the p2p +handshake by authenticating the NetAddress.ID of the peer we're dialing. ### IMPROVEMENTS: -- [config] [\#3291](https://github.com/tendermint/tendermint/issues/3291) Make +* [config] [\#3291](https://github.com/tendermint/tendermint/issues/3291) Make config.ResetTestRootWithChainID() create concurrency-safe test directories. ### BUG FIXES: * [consensus] [\#3295](https://github.com/tendermint/tendermint/issues/3295) - Flush WAL on stop to prevent data corruption during graceful shutdown -- [consensus] [\#3302](https://github.com/tendermint/tendermint/issues/3302) - Reset TriggeredTimeoutPrecommit before starting next height -- [rpc] [\#3251](https://github.com/tendermint/tendermint/issues/3251) Fix - /net_info#peers#remote_ip format. New format spec: + Flush WAL on stop to prevent data corruption during graceful shutdown. +* [consensus] [\#3302](https://github.com/tendermint/tendermint/issues/3302) + Fix possible halt by resetting TriggeredTimeoutPrecommit before starting next height. +* [rpc] [\#3251](https://github.com/tendermint/tendermint/issues/3251) Fix + `/net_info#peers#remote_ip` format. New format spec: * dotted decimal ("192.0.2.1"), if ip is an IPv4 or IP4-mapped IPv6 address * IPv6 ("2001:db8::1"), if ip is a valid IPv6 address * [cmd] [\#3314](https://github.com/tendermint/tendermint/issues/3314) Return - an error on `show_validator` when the private validator file does not exist + an error on `show_validator` when the private validator file does not exist. * [p2p] [\#3010](https://github.com/tendermint/tendermint/issues/3010#issuecomment-464287627) - Authenticate a peer against its NetAddress.ID while dialing + Authenticate a peer against its NetAddress.ID when dialing. ## v0.30.0 From ed1de135486608d8b4869760960e3eb35ade2c6d Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 21 Feb 2019 18:28:02 +0400 Subject: [PATCH 064/259] cs: update wal comments (#3334) * cs: update wal comments Follow-up to https://github.com/tendermint/tendermint/pull/3300 * Update consensus/wal.go Co-Authored-By: melekes --- consensus/wal.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/consensus/wal.go b/consensus/wal.go index b69a99411..2d544857d 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -67,9 +67,10 @@ type WAL interface { } // Write ahead logger writes msgs to disk before they are processed. -// Can be used for crash-recovery and deterministic replay -// TODO: currently the wal is overwritten during replay catchup -// give it a mode so it's either reading or appending - must read to end to start appending again +// Can be used for crash-recovery and deterministic replay. +// TODO: currently the wal is overwritten during replay catchup, give it a mode +// so it's either reading or appending - must read to end to start appending +// again. type baseWAL struct { cmn.BaseService @@ -81,9 +82,8 @@ type baseWAL struct { flushInterval time.Duration } -// NewWAL attempts to create a new write-ahead logger based on `baseWAL`, which -// implements all of the required WAL functionality. This base WAL also flushes -// data to disk every 2s. +// NewWAL returns a new write-ahead logger based on `baseWAL`, which implements +// WAL. It's flushed and synced to disk every 2s and once when stopped. func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) { err := cmn.EnsureDir(filepath.Dir(walFile), 0700) if err != nil { @@ -130,13 +130,11 @@ func (wal *baseWAL) OnStart() error { return err } -// processFlushTicks allows us to periodically attempt to sync the WAL to disk. func (wal *baseWAL) processFlushTicks() { for { select { case <-wal.flushTicker.C: - err := wal.Flush() - if err != nil { + if err := wal.Flush(); err != nil { wal.Logger.Error("Periodic WAL flush failed", "err", err) } case <-wal.Quit(): @@ -145,7 +143,7 @@ func (wal *baseWAL) processFlushTicks() { } } -// Flush will attempt to flush the underlying group's data to disk. +// Flush will attempt to flush and fsync the underlying group's data to disk. func (wal *baseWAL) Flush() error { return wal.group.Flush() } From f22ada442a249597dff765a59197bc57a8eae349 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 22 Feb 2019 11:48:40 +0400 Subject: [PATCH 065/259] refactor decideProposal in common_test (#3343) --- consensus/common_test.go | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/consensus/common_test.go b/consensus/common_test.go index a9fb9e080..1818dc110 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -127,20 +127,18 @@ func startTestRound(cs *ConsensusState, height int64, round int) { cs.startRoutines(0) } -// Create proposal block from cs1 but sign it with vs +// Create proposal block from cs1 but sign it with vs. func decideProposal(cs1 *ConsensusState, vs *validatorStub, height int64, round int) (proposal *types.Proposal, block *types.Block) { cs1.mtx.Lock() block, blockParts := cs1.createProposalBlock() + validRound := cs1.ValidRound + chainID := cs1.state.ChainID cs1.mtx.Unlock() - if block == nil { // on error - panic("error creating proposal block") + if block == nil { + panic("Failed to createProposalBlock. Did you forget to add commit for previous block?") } // Make proposal - cs1.mtx.RLock() - validRound := cs1.ValidRound - chainID := cs1.state.ChainID - cs1.mtx.RUnlock() polRound, propBlockID := validRound, types.BlockID{block.Hash(), blockParts.Header()} proposal = types.NewProposal(height, round, polRound, propBlockID) if err := vs.SignProposal(chainID, proposal); err != nil { From 67fd42835448172d08616589f1473e7153cc0902 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 22 Feb 2019 11:49:08 +0400 Subject: [PATCH 066/259] fix TestWALPeriodicSync (#3342) The test was sometimes failing due to processFlushTicks being called too early. The solution is to call wal#Start later in the test. --- consensus/wal_test.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/consensus/wal_test.go b/consensus/wal_test.go index a2c80be3e..0bb750567 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -156,18 +156,19 @@ func TestWALPeriodicSync(t *testing.T) { wal.SetFlushInterval(walTestFlushInterval) wal.SetLogger(log.TestingLogger()) - require.NoError(t, wal.Start()) - defer func() { - wal.Stop() - wal.Wait() - }() - + // Generate some data err = WALGenerateNBlocks(t, wal.Group(), 5) require.NoError(t, err) // We should have data in the buffer now assert.NotZero(t, wal.Group().Buffered()) + require.NoError(t, wal.Start()) + defer func() { + wal.Stop() + wal.Wait() + }() + time.Sleep(walTestFlushInterval + (10 * time.Millisecond)) // The data should have been flushed by the periodic sync From 2137ecc130874118cc23995befed370958c34448 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 23 Feb 2019 08:11:27 +0400 Subject: [PATCH 067/259] pubsub 2.0 (#3227) * green pubsub tests :OK: * get rid of clientToQueryMap * Subscribe and SubscribeUnbuffered * start adapting other pkgs to new pubsub * nope * rename MsgAndTags to Message * remove TagMap it does not bring any additional benefits * bring back EventSubscriber * fix test * fix data race in TestStartNextHeightCorrectly ``` Write at 0x00c0001c7418 by goroutine 796: github.com/tendermint/tendermint/consensus.TestStartNextHeightCorrectly() /go/src/github.com/tendermint/tendermint/consensus/state_test.go:1296 +0xad testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 Previous read at 0x00c0001c7418 by goroutine 858: github.com/tendermint/tendermint/consensus.(*ConsensusState).addVote() /go/src/github.com/tendermint/tendermint/consensus/state.go:1631 +0x1366 github.com/tendermint/tendermint/consensus.(*ConsensusState).tryAddVote() /go/src/github.com/tendermint/tendermint/consensus/state.go:1476 +0x8f github.com/tendermint/tendermint/consensus.(*ConsensusState).handleMsg() /go/src/github.com/tendermint/tendermint/consensus/state.go:667 +0xa1e github.com/tendermint/tendermint/consensus.(*ConsensusState).receiveRoutine() /go/src/github.com/tendermint/tendermint/consensus/state.go:628 +0x794 Goroutine 796 (running) created at: testing.(*T).Run() /usr/local/go/src/testing/testing.go:878 +0x659 testing.runTests.func1() /usr/local/go/src/testing/testing.go:1119 +0xa8 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 testing.runTests() /usr/local/go/src/testing/testing.go:1117 +0x4ee testing.(*M).Run() /usr/local/go/src/testing/testing.go:1034 +0x2ee main.main() _testmain.go:214 +0x332 Goroutine 858 (running) created at: github.com/tendermint/tendermint/consensus.(*ConsensusState).startRoutines() /go/src/github.com/tendermint/tendermint/consensus/state.go:334 +0x221 github.com/tendermint/tendermint/consensus.startTestRound() /go/src/github.com/tendermint/tendermint/consensus/common_test.go:122 +0x63 github.com/tendermint/tendermint/consensus.TestStateFullRound1() /go/src/github.com/tendermint/tendermint/consensus/state_test.go:255 +0x397 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 ``` * fixes after my own review * fix formatting * wait 100ms before kicking a subscriber out + a test for indexer_service * fixes after my second review * no timeout * add changelog entries * fix merge conflicts * fix typos after Thane's review Co-Authored-By: melekes * reformat code * rewrite indexer service in the attempt to fix failing test https://github.com/tendermint/tendermint/pull/3227/#issuecomment-462316527 * Revert "rewrite indexer service in the attempt to fix failing test" This reverts commit 0d9107a098230de7138abb1c201877c246e89ed1. * another attempt to fix indexer * fixes after Ethan's review * use unbuffered channel when indexing transactions Refs https://github.com/tendermint/tendermint/pull/3227#discussion_r258786716 * add a comment for EventBus#SubscribeUnbuffered * format code --- CHANGELOG_PENDING.md | 2 + consensus/byzantine_test.go | 10 +- consensus/common_test.go | 211 +++++++++----------- consensus/mempool_test.go | 6 +- consensus/reactor_test.go | 151 +++++++------- consensus/replay.go | 12 +- consensus/replay_file.go | 42 +--- consensus/replay_test.go | 9 +- consensus/state_test.go | 7 +- libs/pubsub/example_test.go | 7 +- libs/pubsub/pubsub.go | 273 +++++++++++--------------- libs/pubsub/pubsub_test.go | 207 +++++++++++++------ libs/pubsub/query/empty.go | 4 +- libs/pubsub/query/empty_test.go | 9 +- libs/pubsub/query/query.go | 10 +- libs/pubsub/query/query_test.go | 5 +- libs/pubsub/subscription.go | 89 +++++++++ node/node_test.go | 7 +- rpc/client/helpers.go | 24 +-- rpc/client/httpclient.go | 65 ++++-- rpc/client/localclient.go | 4 +- rpc/core/events.go | 24 ++- rpc/core/mempool.go | 31 ++- rpc/core/types/responses.go | 1 + rpc/lib/server/handlers.go | 1 + rpc/lib/types/types.go | 15 +- state/execution_test.go | 11 +- state/txindex/indexer_service.go | 39 ++-- state/txindex/indexer_service_test.go | 64 ++++++ types/event_bus.go | 26 ++- types/event_bus_test.go | 81 ++++---- 31 files changed, 834 insertions(+), 613 deletions(-) create mode 100644 libs/pubsub/subscription.go create mode 100644 state/txindex/indexer_service_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 640985836..c123f1c34 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -7,6 +7,7 @@ Special thanks to external contributors on this release: ### BREAKING CHANGES: * CLI/RPC/Config +- [httpclient] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) * Apps @@ -21,3 +22,4 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: +- [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) \ No newline at end of file diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 31a7badfd..0c1b88565 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -46,7 +46,7 @@ func TestByzantine(t *testing.T) { switches[i].SetLogger(p2pLogger.With("validator", i)) } - eventChans := make([]chan interface{}, N) + blocksSubs := make([]types.Subscription, N) reactors := make([]p2p.Reactor, N) for i := 0; i < N; i++ { // make first val byzantine @@ -65,8 +65,8 @@ func TestByzantine(t *testing.T) { eventBus := css[i].eventBus eventBus.SetLogger(logger.With("module", "events", "validator", i)) - eventChans[i] = make(chan interface{}, 1) - err := eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock, eventChans[i]) + var err error + blocksSubs[i], err = eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock) require.NoError(t, err) conR := NewConsensusReactor(css[i], true) // so we dont start the consensus states @@ -131,7 +131,7 @@ func TestByzantine(t *testing.T) { p2p.Connect2Switches(switches, ind1, ind2) // wait for someone in the big partition (B) to make a block - <-eventChans[ind2] + <-blocksSubs[ind2].Out() t.Log("A block has been committed. Healing partition") p2p.Connect2Switches(switches, ind0, ind1) @@ -143,7 +143,7 @@ func TestByzantine(t *testing.T) { wg.Add(2) for i := 1; i < N-1; i++ { go func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() wg.Done() }(i) } diff --git a/consensus/common_test.go b/consensus/common_test.go index 1818dc110..5706f2317 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "os" "path/filepath" - "reflect" "sort" "sync" "testing" @@ -25,6 +24,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmpubsub "github.com/tendermint/tendermint/libs/pubsub" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/privval" @@ -227,24 +227,22 @@ func validatePrevoteAndPrecommit(t *testing.T, cs *ConsensusState, thisRound, lo cs.mtx.Unlock() } -// genesis -func subscribeToVoter(cs *ConsensusState, addr []byte) chan interface{} { - voteCh0 := make(chan interface{}) - err := cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryVote, voteCh0) +func subscribeToVoter(cs *ConsensusState, addr []byte) <-chan tmpubsub.Message { + votesSub, err := cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryVote) if err != nil { panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, types.EventQueryVote)) } - voteCh := make(chan interface{}) + ch := make(chan tmpubsub.Message) go func() { - for v := range voteCh0 { - vote := v.(types.EventDataVote) + for msg := range votesSub.Out() { + vote := msg.Data().(types.EventDataVote) // we only fire for our own votes if bytes.Equal(addr, vote.Vote.ValidatorAddress) { - voteCh <- v + ch <- msg } } }() - return voteCh + return ch } //------------------------------------------------------------------------------- @@ -321,7 +319,7 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) { //------------------------------------------------------------------------------- -func ensureNoNewEvent(ch <-chan interface{}, timeout time.Duration, +func ensureNoNewEvent(ch <-chan tmpubsub.Message, timeout time.Duration, errorMessage string) { select { case <-time.After(timeout): @@ -331,28 +329,28 @@ func ensureNoNewEvent(ch <-chan interface{}, timeout time.Duration, } } -func ensureNoNewEventOnChannel(ch <-chan interface{}) { +func ensureNoNewEventOnChannel(ch <-chan tmpubsub.Message) { ensureNoNewEvent( ch, ensureTimeout, "We should be stuck waiting, not receiving new event on the channel") } -func ensureNoNewRoundStep(stepCh <-chan interface{}) { +func ensureNoNewRoundStep(stepCh <-chan tmpubsub.Message) { ensureNoNewEvent( stepCh, ensureTimeout, "We should be stuck waiting, not receiving NewRoundStep event") } -func ensureNoNewUnlock(unlockCh <-chan interface{}) { +func ensureNoNewUnlock(unlockCh <-chan tmpubsub.Message) { ensureNoNewEvent( unlockCh, ensureTimeout, "We should be stuck waiting, not receiving Unlock event") } -func ensureNoNewTimeout(stepCh <-chan interface{}, timeout int64) { +func ensureNoNewTimeout(stepCh <-chan tmpubsub.Message, timeout int64) { timeoutDuration := time.Duration(timeout*5) * time.Nanosecond ensureNoNewEvent( stepCh, @@ -360,142 +358,157 @@ func ensureNoNewTimeout(stepCh <-chan interface{}, timeout int64) { "We should be stuck waiting, not receiving NewTimeout event") } -func ensureNewEvent( - ch <-chan interface{}, - height int64, - round int, - timeout time.Duration, - errorMessage string) { - +func ensureNewEvent(ch <-chan tmpubsub.Message, height int64, round int, timeout time.Duration, errorMessage string) { select { case <-time.After(timeout): panic(errorMessage) - case ev := <-ch: - rs, ok := ev.(types.EventDataRoundState) + case msg := <-ch: + roundStateEvent, ok := msg.Data().(types.EventDataRoundState) if !ok { - panic( - fmt.Sprintf( - "expected a EventDataRoundState, got %v.Wrong subscription channel?", - reflect.TypeOf(rs))) + panic(fmt.Sprintf("expected a EventDataRoundState, got %T. Wrong subscription channel?", + msg.Data())) } - if rs.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height)) + if roundStateEvent.Height != height { + panic(fmt.Sprintf("expected height %v, got %v", height, roundStateEvent.Height)) } - if rs.Round != round { - panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round)) + if roundStateEvent.Round != round { + panic(fmt.Sprintf("expected round %v, got %v", round, roundStateEvent.Round)) } // TODO: We could check also for a step at this point! } } -func ensureNewRound(roundCh <-chan interface{}, height int64, round int) { +func ensureNewRound(roundCh <-chan tmpubsub.Message, height int64, round int) { select { case <-time.After(ensureTimeout): panic("Timeout expired while waiting for NewRound event") - case ev := <-roundCh: - rs, ok := ev.(types.EventDataNewRound) + case msg := <-roundCh: + newRoundEvent, ok := msg.Data().(types.EventDataNewRound) if !ok { - panic( - fmt.Sprintf( - "expected a EventDataNewRound, got %v.Wrong subscription channel?", - reflect.TypeOf(rs))) + panic(fmt.Sprintf("expected a EventDataNewRound, got %T. Wrong subscription channel?", + msg.Data())) } - if rs.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height)) + if newRoundEvent.Height != height { + panic(fmt.Sprintf("expected height %v, got %v", height, newRoundEvent.Height)) } - if rs.Round != round { - panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round)) + if newRoundEvent.Round != round { + panic(fmt.Sprintf("expected round %v, got %v", round, newRoundEvent.Round)) } } } -func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, timeout int64) { +func ensureNewTimeout(timeoutCh <-chan tmpubsub.Message, height int64, round int, timeout int64) { timeoutDuration := time.Duration(timeout*5) * time.Nanosecond ensureNewEvent(timeoutCh, height, round, timeoutDuration, "Timeout expired while waiting for NewTimeout event") } -func ensureNewProposal(proposalCh <-chan interface{}, height int64, round int) { +func ensureNewProposal(proposalCh <-chan tmpubsub.Message, height int64, round int) { select { case <-time.After(ensureTimeout): panic("Timeout expired while waiting for NewProposal event") - case ev := <-proposalCh: - rs, ok := ev.(types.EventDataCompleteProposal) + case msg := <-proposalCh: + proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal) if !ok { - panic( - fmt.Sprintf( - "expected a EventDataCompleteProposal, got %v.Wrong subscription channel?", - reflect.TypeOf(rs))) + panic(fmt.Sprintf("expected a EventDataCompleteProposal, got %T. Wrong subscription channel?", + msg.Data())) } - if rs.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height)) + if proposalEvent.Height != height { + panic(fmt.Sprintf("expected height %v, got %v", height, proposalEvent.Height)) } - if rs.Round != round { - panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round)) + if proposalEvent.Round != round { + panic(fmt.Sprintf("expected round %v, got %v", round, proposalEvent.Round)) } } } -func ensureNewValidBlock(validBlockCh <-chan interface{}, height int64, round int) { +func ensureNewValidBlock(validBlockCh <-chan tmpubsub.Message, height int64, round int) { ensureNewEvent(validBlockCh, height, round, ensureTimeout, "Timeout expired while waiting for NewValidBlock event") } -func ensureNewBlock(blockCh <-chan interface{}, height int64) { +func ensureNewBlock(blockCh <-chan tmpubsub.Message, height int64) { select { case <-time.After(ensureTimeout): panic("Timeout expired while waiting for NewBlock event") - case ev := <-blockCh: - block, ok := ev.(types.EventDataNewBlock) + case msg := <-blockCh: + blockEvent, ok := msg.Data().(types.EventDataNewBlock) if !ok { - panic(fmt.Sprintf("expected a *types.EventDataNewBlock, "+ - "got %v. wrong subscription channel?", - reflect.TypeOf(block))) + panic(fmt.Sprintf("expected a EventDataNewBlock, got %T. Wrong subscription channel?", + msg.Data())) } - if block.Block.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, block.Block.Height)) + if blockEvent.Block.Height != height { + panic(fmt.Sprintf("expected height %v, got %v", height, blockEvent.Block.Height)) } } } -func ensureNewBlockHeader(blockCh <-chan interface{}, height int64, blockHash cmn.HexBytes) { +func ensureNewBlockHeader(blockCh <-chan tmpubsub.Message, height int64, blockHash cmn.HexBytes) { select { case <-time.After(ensureTimeout): panic("Timeout expired while waiting for NewBlockHeader event") - case ev := <-blockCh: - blockHeader, ok := ev.(types.EventDataNewBlockHeader) + case msg := <-blockCh: + blockHeaderEvent, ok := msg.Data().(types.EventDataNewBlockHeader) if !ok { - panic(fmt.Sprintf("expected a *types.EventDataNewBlockHeader, "+ - "got %v. wrong subscription channel?", - reflect.TypeOf(blockHeader))) + panic(fmt.Sprintf("expected a EventDataNewBlockHeader, got %T. Wrong subscription channel?", + msg.Data())) } - if blockHeader.Header.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, blockHeader.Header.Height)) + if blockHeaderEvent.Header.Height != height { + panic(fmt.Sprintf("expected height %v, got %v", height, blockHeaderEvent.Header.Height)) } - if !bytes.Equal(blockHeader.Header.Hash(), blockHash) { - panic(fmt.Sprintf("expected header %X, got %X", blockHash, blockHeader.Header.Hash())) + if !bytes.Equal(blockHeaderEvent.Header.Hash(), blockHash) { + panic(fmt.Sprintf("expected header %X, got %X", blockHash, blockHeaderEvent.Header.Hash())) } } } -func ensureNewUnlock(unlockCh <-chan interface{}, height int64, round int) { +func ensureNewUnlock(unlockCh <-chan tmpubsub.Message, height int64, round int) { ensureNewEvent(unlockCh, height, round, ensureTimeout, "Timeout expired while waiting for NewUnlock event") } -func ensureVote(voteCh <-chan interface{}, height int64, round int, +func ensureProposal(proposalCh <-chan tmpubsub.Message, height int64, round int, propID types.BlockID) { + select { + case <-time.After(ensureTimeout): + panic("Timeout expired while waiting for NewProposal event") + case msg := <-proposalCh: + proposalEvent, ok := msg.Data().(types.EventDataCompleteProposal) + if !ok { + panic(fmt.Sprintf("expected a EventDataCompleteProposal, got %T. Wrong subscription channel?", + msg.Data())) + } + if proposalEvent.Height != height { + panic(fmt.Sprintf("expected height %v, got %v", height, proposalEvent.Height)) + } + if proposalEvent.Round != round { + panic(fmt.Sprintf("expected round %v, got %v", round, proposalEvent.Round)) + } + if !proposalEvent.BlockID.Equals(propID) { + panic("Proposed block does not match expected block") + } + } +} + +func ensurePrecommit(voteCh <-chan tmpubsub.Message, height int64, round int) { + ensureVote(voteCh, height, round, types.PrecommitType) +} + +func ensurePrevote(voteCh <-chan tmpubsub.Message, height int64, round int) { + ensureVote(voteCh, height, round, types.PrevoteType) +} + +func ensureVote(voteCh <-chan tmpubsub.Message, height int64, round int, voteType types.SignedMsgType) { select { case <-time.After(ensureTimeout): panic("Timeout expired while waiting for NewVote event") - case v := <-voteCh: - edv, ok := v.(types.EventDataVote) + case msg := <-voteCh: + voteEvent, ok := msg.Data().(types.EventDataVote) if !ok { - panic(fmt.Sprintf("expected a *types.Vote, "+ - "got %v. wrong subscription channel?", - reflect.TypeOf(v))) + panic(fmt.Sprintf("expected a EventDataVote, got %T. Wrong subscription channel?", + msg.Data())) } - vote := edv.Vote + vote := voteEvent.Vote if vote.Height != height { panic(fmt.Sprintf("expected height %v, got %v", height, vote.Height)) } @@ -508,39 +521,7 @@ func ensureVote(voteCh <-chan interface{}, height int64, round int, } } -func ensureProposal(proposalCh <-chan interface{}, height int64, round int, propId types.BlockID) { - select { - case <-time.After(ensureTimeout): - panic("Timeout expired while waiting for NewProposal event") - case ev := <-proposalCh: - rs, ok := ev.(types.EventDataCompleteProposal) - if !ok { - panic( - fmt.Sprintf( - "expected a EventDataCompleteProposal, got %v.Wrong subscription channel?", - reflect.TypeOf(rs))) - } - if rs.Height != height { - panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height)) - } - if rs.Round != round { - panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round)) - } - if !rs.BlockID.Equals(propId) { - panic("Proposed block does not match expected block") - } - } -} - -func ensurePrecommit(voteCh <-chan interface{}, height int64, round int) { - ensureVote(voteCh, height, round, types.PrecommitType) -} - -func ensurePrevote(voteCh <-chan interface{}, height int64, round int) { - ensureVote(voteCh, height, round, types.PrevoteType) -} - -func ensureNewEventOnChannel(ch <-chan interface{}) { +func ensureNewEventOnChannel(ch <-chan tmpubsub.Message) { select { case <-time.After(ensureTimeout): panic("Timeout expired while waiting for new activity on the channel") diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index cfbe40efd..e7669b177 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -117,9 +117,9 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) { for nTxs := 0; nTxs < NTxs; { ticker := time.NewTicker(time.Second * 30) select { - case b := <-newBlockCh: - evt := b.(types.EventDataNewBlock) - nTxs += int(evt.Block.Header.NumTxs) + case msg := <-newBlockCh: + blockEvent := msg.Data().(types.EventDataNewBlock) + nTxs += int(blockEvent.Block.Header.NumTxs) case <-ticker.C: panic("Timed out waiting to commit blocks with transactions") } diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 473c9d792..dfd6f2508 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -30,9 +30,13 @@ import ( //---------------------------------------------- // in-process testnets -func startConsensusNet(t *testing.T, css []*ConsensusState, N int) ([]*ConsensusReactor, []chan interface{}, []*types.EventBus) { +func startConsensusNet(t *testing.T, css []*ConsensusState, N int) ( + []*ConsensusReactor, + []types.Subscription, + []*types.EventBus, +) { reactors := make([]*ConsensusReactor, N) - eventChans := make([]chan interface{}, N) + blocksSubs := make([]types.Subscription, 0) eventBuses := make([]*types.EventBus, N) for i := 0; i < N; i++ { /*logger, err := tmflags.ParseLogLevel("consensus:info,*:error", logger, "info") @@ -44,9 +48,9 @@ func startConsensusNet(t *testing.T, css []*ConsensusState, N int) ([]*Consensus eventBuses[i] = css[i].eventBus reactors[i].SetEventBus(eventBuses[i]) - eventChans[i] = make(chan interface{}, 1) - err := eventBuses[i].Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock, eventChans[i]) + blocksSub, err := eventBuses[i].Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock) require.NoError(t, err) + blocksSubs = append(blocksSubs, blocksSub) } // make connected switches and start all reactors p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch { @@ -63,7 +67,7 @@ func startConsensusNet(t *testing.T, css []*ConsensusState, N int) ([]*Consensus s := reactors[i].conS.GetState() reactors[i].SwitchToConsensus(s, 0) } - return reactors, eventChans, eventBuses + return reactors, blocksSubs, eventBuses } func stopConsensusNet(logger log.Logger, reactors []*ConsensusReactor, eventBuses []*types.EventBus) { @@ -84,11 +88,11 @@ func TestReactorBasic(t *testing.T) { N := 4 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() - reactors, eventChans, eventBuses := startConsensusNet(t, css, N) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // wait till everyone makes the first new block timeoutWaitGroup(t, N, func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() }, css) } @@ -161,20 +165,20 @@ func TestReactorWithEvidence(t *testing.T) { css[i] = cs } - reactors, eventChans, eventBuses := startConsensusNet(t, css, nValidators) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nValidators) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // wait till everyone makes the first new block with no evidence timeoutWaitGroup(t, nValidators, func(j int) { - blockI := <-eventChans[j] - block := blockI.(types.EventDataNewBlock).Block + msg := <-blocksSubs[j].Out() + block := msg.Data().(types.EventDataNewBlock).Block assert.True(t, len(block.Evidence.Evidence) == 0) }, css) // second block should have evidence timeoutWaitGroup(t, nValidators, func(j int) { - blockI := <-eventChans[j] - block := blockI.(types.EventDataNewBlock).Block + msg := <-blocksSubs[j].Out() + block := msg.Data().(types.EventDataNewBlock).Block assert.True(t, len(block.Evidence.Evidence) > 0) }, css) } @@ -221,7 +225,7 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { c.Consensus.CreateEmptyBlocks = false }) defer cleanup() - reactors, eventChans, eventBuses := startConsensusNet(t, css, N) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // send a tx @@ -231,7 +235,7 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { // wait till everyone makes the first new block timeoutWaitGroup(t, N, func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() }, css) } @@ -240,12 +244,12 @@ func TestReactorRecordsVotesAndBlockParts(t *testing.T) { N := 4 css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) defer cleanup() - reactors, eventChans, eventBuses := startConsensusNet(t, css, N) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // wait till everyone makes the first new block timeoutWaitGroup(t, N, func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() }, css) // Get peer @@ -265,7 +269,7 @@ func TestReactorVotingPowerChange(t *testing.T) { logger := log.TestingLogger() css, cleanup := randConsensusNet(nVals, "consensus_voting_power_changes_test", newMockTickerFunc(true), newPersistentKVStore) defer cleanup() - reactors, eventChans, eventBuses := startConsensusNet(t, css, nVals) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nVals) defer stopConsensusNet(logger, reactors, eventBuses) // map of active validators @@ -277,7 +281,7 @@ func TestReactorVotingPowerChange(t *testing.T) { // wait till everyone makes block 1 timeoutWaitGroup(t, nVals, func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() }, css) //--------------------------------------------------------------------------- @@ -288,10 +292,10 @@ func TestReactorVotingPowerChange(t *testing.T) { updateValidatorTx := kvstore.MakeValSetChangeTx(val1PubKeyABCI, 25) previousTotalVotingPower := css[0].GetRoundState().LastValidators.TotalVotingPower() - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx) - waitForAndValidateBlockWithTx(t, nVals, activeVals, eventChans, css, updateValidatorTx) - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css) - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) + waitForAndValidateBlockWithTx(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower()) @@ -300,10 +304,10 @@ func TestReactorVotingPowerChange(t *testing.T) { updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKeyABCI, 2) previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower() - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx) - waitForAndValidateBlockWithTx(t, nVals, activeVals, eventChans, css, updateValidatorTx) - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css) - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) + waitForAndValidateBlockWithTx(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower()) @@ -312,10 +316,10 @@ func TestReactorVotingPowerChange(t *testing.T) { updateValidatorTx = kvstore.MakeValSetChangeTx(val1PubKeyABCI, 26) previousTotalVotingPower = css[0].GetRoundState().LastValidators.TotalVotingPower() - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css, updateValidatorTx) - waitForAndValidateBlockWithTx(t, nVals, activeVals, eventChans, css, updateValidatorTx) - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css) - waitForAndValidateBlock(t, nVals, activeVals, eventChans, css) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) + waitForAndValidateBlockWithTx(t, nVals, activeVals, blocksSubs, css, updateValidatorTx) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) + waitForAndValidateBlock(t, nVals, activeVals, blocksSubs, css) if css[0].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { t.Fatalf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[0].GetRoundState().LastValidators.TotalVotingPower()) @@ -329,7 +333,7 @@ func TestReactorValidatorSetChanges(t *testing.T) { defer cleanup() logger := log.TestingLogger() - reactors, eventChans, eventBuses := startConsensusNet(t, css, nPeers) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, nPeers) defer stopConsensusNet(logger, reactors, eventBuses) // map of active validators @@ -341,7 +345,7 @@ func TestReactorValidatorSetChanges(t *testing.T) { // wait till everyone makes block 1 timeoutWaitGroup(t, nPeers, func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() }, css) //--------------------------------------------------------------------------- @@ -354,22 +358,22 @@ func TestReactorValidatorSetChanges(t *testing.T) { // wait till everyone makes block 2 // ensure the commit includes all validators // send newValTx to change vals in block 3 - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx1) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css, newValidatorTx1) // wait till everyone makes block 3. // it includes the commit for block 2, which is by the original validator set - waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx1) + waitForAndValidateBlockWithTx(t, nPeers, activeVals, blocksSubs, css, newValidatorTx1) // wait till everyone makes block 4. // it includes the commit for block 3, which is by the original validator set - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css) // the commits for block 4 should be with the updated validator set activeVals[string(newValidatorPubKey1.Address())] = struct{}{} // wait till everyone makes block 5 // it includes the commit for block 4, which should have the updated validator set - waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, eventChans, css) + waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, blocksSubs, css) //--------------------------------------------------------------------------- logger.Info("---------------------------- Testing changing the voting power of one validator") @@ -379,10 +383,10 @@ func TestReactorValidatorSetChanges(t *testing.T) { updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) previousTotalVotingPower := css[nVals].GetRoundState().LastValidators.TotalVotingPower() - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, updateValidatorTx1) - waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, updateValidatorTx1) - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css) - waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, eventChans, css) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css, updateValidatorTx1) + waitForAndValidateBlockWithTx(t, nPeers, activeVals, blocksSubs, css, updateValidatorTx1) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css) + waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, blocksSubs, css) if css[nVals].GetRoundState().LastValidators.TotalVotingPower() == previousTotalVotingPower { t.Errorf("expected voting power to change (before: %d, after: %d)", previousTotalVotingPower, css[nVals].GetRoundState().LastValidators.TotalVotingPower()) @@ -399,12 +403,12 @@ func TestReactorValidatorSetChanges(t *testing.T) { newVal3ABCI := types.TM2PB.PubKey(newValidatorPubKey3) newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower) - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3) - waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, newValidatorTx2, newValidatorTx3) - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css, newValidatorTx2, newValidatorTx3) + waitForAndValidateBlockWithTx(t, nPeers, activeVals, blocksSubs, css, newValidatorTx2, newValidatorTx3) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css) activeVals[string(newValidatorPubKey2.Address())] = struct{}{} activeVals[string(newValidatorPubKey3.Address())] = struct{}{} - waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, eventChans, css) + waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, blocksSubs, css) //--------------------------------------------------------------------------- logger.Info("---------------------------- Testing removing two validators at once") @@ -412,12 +416,12 @@ func TestReactorValidatorSetChanges(t *testing.T) { removeValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, 0) removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0) - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3) - waitForAndValidateBlockWithTx(t, nPeers, activeVals, eventChans, css, removeValidatorTx2, removeValidatorTx3) - waitForAndValidateBlock(t, nPeers, activeVals, eventChans, css) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css, removeValidatorTx2, removeValidatorTx3) + waitForAndValidateBlockWithTx(t, nPeers, activeVals, blocksSubs, css, removeValidatorTx2, removeValidatorTx3) + waitForAndValidateBlock(t, nPeers, activeVals, blocksSubs, css) delete(activeVals, string(newValidatorPubKey2.Address())) delete(activeVals, string(newValidatorPubKey3.Address())) - waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, eventChans, css) + waitForBlockWithUpdatedValsAndValidateIt(t, nPeers, activeVals, blocksSubs, css) } // Check we can make blocks with skip_timeout_commit=false @@ -430,23 +434,27 @@ func TestReactorWithTimeoutCommit(t *testing.T) { css[i].config.SkipTimeoutCommit = false } - reactors, eventChans, eventBuses := startConsensusNet(t, css, N-1) + reactors, blocksSubs, eventBuses := startConsensusNet(t, css, N-1) defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) // wait till everyone makes the first new block timeoutWaitGroup(t, N-1, func(j int) { - <-eventChans[j] + <-blocksSubs[j].Out() }, css) } -func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}, eventChans []chan interface{}, css []*ConsensusState, txs ...[]byte) { +func waitForAndValidateBlock( + t *testing.T, + n int, + activeVals map[string]struct{}, + blocksSubs []types.Subscription, + css []*ConsensusState, + txs ...[]byte, +) { timeoutWaitGroup(t, n, func(j int) { css[j].Logger.Debug("waitForAndValidateBlock") - newBlockI, ok := <-eventChans[j] - if !ok { - return - } - newBlock := newBlockI.(types.EventDataNewBlock).Block + msg := <-blocksSubs[j].Out() + newBlock := msg.Data().(types.EventDataNewBlock).Block css[j].Logger.Debug("waitForAndValidateBlock: Got block", "height", newBlock.Height) err := validateBlock(newBlock, activeVals) assert.Nil(t, err) @@ -457,17 +465,21 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{} }, css) } -func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]struct{}, eventChans []chan interface{}, css []*ConsensusState, txs ...[]byte) { +func waitForAndValidateBlockWithTx( + t *testing.T, + n int, + activeVals map[string]struct{}, + blocksSubs []types.Subscription, + css []*ConsensusState, + txs ...[]byte, +) { timeoutWaitGroup(t, n, func(j int) { ntxs := 0 BLOCK_TX_LOOP: for { css[j].Logger.Debug("waitForAndValidateBlockWithTx", "ntxs", ntxs) - newBlockI, ok := <-eventChans[j] - if !ok { - return - } - newBlock := newBlockI.(types.EventDataNewBlock).Block + msg := <-blocksSubs[j].Out() + newBlock := msg.Data().(types.EventDataNewBlock).Block css[j].Logger.Debug("waitForAndValidateBlockWithTx: Got block", "height", newBlock.Height) err := validateBlock(newBlock, activeVals) assert.Nil(t, err) @@ -488,18 +500,21 @@ func waitForAndValidateBlockWithTx(t *testing.T, n int, activeVals map[string]st }, css) } -func waitForBlockWithUpdatedValsAndValidateIt(t *testing.T, n int, updatedVals map[string]struct{}, eventChans []chan interface{}, css []*ConsensusState) { +func waitForBlockWithUpdatedValsAndValidateIt( + t *testing.T, + n int, + updatedVals map[string]struct{}, + blocksSubs []types.Subscription, + css []*ConsensusState, +) { timeoutWaitGroup(t, n, func(j int) { var newBlock *types.Block LOOP: for { css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt") - newBlockI, ok := <-eventChans[j] - if !ok { - return - } - newBlock = newBlockI.(types.EventDataNewBlock).Block + msg := <-blocksSubs[j].Out() + newBlock = msg.Data().(types.EventDataNewBlock).Block if newBlock.LastCommit.Size() == len(updatedVals) { css[j].Logger.Debug("waitForBlockWithUpdatedValsAndValidateIt: Got block", "height", newBlock.Height) break LOOP diff --git a/consensus/replay.go b/consensus/replay.go index 5453c700e..0d75561b9 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -42,7 +42,7 @@ var crc32c = crc32.MakeTable(crc32.Castagnoli) // Unmarshal and apply a single message to the consensus state as if it were // received in receiveRoutine. Lines that start with "#" are ignored. // NOTE: receiveRoutine should not be running. -func (cs *ConsensusState) readReplayMessage(msg *TimedWALMessage, newStepCh chan interface{}) error { +func (cs *ConsensusState) readReplayMessage(msg *TimedWALMessage, newStepSub types.Subscription) error { // Skip meta messages which exist for demarcating boundaries. if _, ok := msg.Msg.(EndHeightMessage); ok { return nil @@ -54,15 +54,17 @@ func (cs *ConsensusState) readReplayMessage(msg *TimedWALMessage, newStepCh chan cs.Logger.Info("Replay: New Step", "height", m.Height, "round", m.Round, "step", m.Step) // these are playback checks ticker := time.After(time.Second * 2) - if newStepCh != nil { + if newStepSub != nil { select { - case mi := <-newStepCh: - m2 := mi.(types.EventDataRoundState) + case stepMsg := <-newStepSub.Out(): + m2 := stepMsg.Data().(types.EventDataRoundState) if m.Height != m2.Height || m.Round != m2.Round || m.Step != m2.Step { return fmt.Errorf("RoundState mismatch. Got %v; Expected %v", m2, m) } + case <-newStepSub.Cancelled(): + return fmt.Errorf("Failed to read off newStepSub.Out(). newStepSub was cancelled") case <-ticker: - return fmt.Errorf("Failed to read off newStepCh") + return fmt.Errorf("Failed to read off newStepSub.Out()") } } case msgInfo: diff --git a/consensus/replay_file.go b/consensus/replay_file.go index 15246d221..d45f9c5a4 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -51,25 +51,13 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error { cs.startForReplay() // ensure all new step events are regenerated as expected - newStepCh := make(chan interface{}, 1) ctx := context.Background() - err := cs.eventBus.Subscribe(ctx, subscriber, types.EventQueryNewRoundStep, newStepCh) + newStepSub, err := cs.eventBus.Subscribe(ctx, subscriber, types.EventQueryNewRoundStep) if err != nil { return errors.Errorf("failed to subscribe %s to %v", subscriber, types.EventQueryNewRoundStep) } - defer func() { - // drain newStepCh to make sure we don't block - LOOP: - for { - select { - case <-newStepCh: - default: - break LOOP - } - } - cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep) - }() + defer cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep) // just open the file for reading, no need to use wal fp, err := os.OpenFile(file, os.O_RDONLY, 0600) @@ -94,7 +82,7 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error { return err } - if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil { + if err := pb.cs.readReplayMessage(msg, newStepSub); err != nil { return err } @@ -131,7 +119,7 @@ func newPlayback(fileName string, fp *os.File, cs *ConsensusState, genState sm.S } // go back count steps by resetting the state and running (pb.count - count) steps -func (pb *playback) replayReset(count int, newStepCh chan interface{}) error { +func (pb *playback) replayReset(count int, newStepSub types.Subscription) error { pb.cs.Stop() pb.cs.Wait() @@ -161,7 +149,7 @@ func (pb *playback) replayReset(count int, newStepCh chan interface{}) error { } else if err != nil { return err } - if err := pb.cs.readReplayMessage(msg, newStepCh); err != nil { + if err := pb.cs.readReplayMessage(msg, newStepSub); err != nil { return err } pb.count++ @@ -225,27 +213,15 @@ func (pb *playback) replayConsoleLoop() int { ctx := context.Background() // ensure all new step events are regenerated as expected - newStepCh := make(chan interface{}, 1) - err := pb.cs.eventBus.Subscribe(ctx, subscriber, types.EventQueryNewRoundStep, newStepCh) + newStepSub, err := pb.cs.eventBus.Subscribe(ctx, subscriber, types.EventQueryNewRoundStep) if err != nil { cmn.Exit(fmt.Sprintf("failed to subscribe %s to %v", subscriber, types.EventQueryNewRoundStep)) } - defer func() { - // drain newStepCh to make sure we don't block - LOOP: - for { - select { - case <-newStepCh: - default: - break LOOP - } - } - pb.cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep) - }() + defer pb.cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep) if len(tokens) == 1 { - if err := pb.replayReset(1, newStepCh); err != nil { + if err := pb.replayReset(1, newStepSub); err != nil { pb.cs.Logger.Error("Replay reset error", "err", err) } } else { @@ -255,7 +231,7 @@ func (pb *playback) replayConsoleLoop() int { } else if i > pb.count { fmt.Printf("argument to back must not be larger than the current count (%d)\n", pb.count) } else { - if err := pb.replayReset(i, newStepCh); err != nil { + if err := pb.replayReset(i, newStepSub); err != nil { pb.cs.Logger.Error("Replay reset error", "err", err) } } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 2146f21d1..74a6e0fbe 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -78,13 +78,14 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, consensusReplayConfig * // in the WAL itself. Assuming the consensus state is running, replay of any // WAL, including the empty one, should eventually be followed by a new // block, or else something is wrong. - newBlockCh := make(chan interface{}, 1) - err = cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock, newBlockCh) + newBlockSub, err := cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock) require.NoError(t, err) select { - case <-newBlockCh: + case <-newBlockSub.Out(): + case <-newBlockSub.Cancelled(): + t.Fatal("newBlockSub was cancelled") case <-time.After(120 * time.Second): - t.Fatalf("Timed out waiting for new block (see trace above)") + t.Fatal("Timed out waiting for new block (see trace above)") } } diff --git a/consensus/state_test.go b/consensus/state_test.go index 69de825e7..a4d01e584 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -1620,11 +1620,10 @@ func TestStateOutputVoteStats(t *testing.T) { } // subscribe subscribes test client to the given query and returns a channel with cap = 1. -func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan interface{} { - out := make(chan interface{}, 1) - err := eventBus.Subscribe(context.Background(), testSubscriber, q, out) +func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message { + sub, err := eventBus.Subscribe(context.Background(), testSubscriber, q) if err != nil { panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q)) } - return out + return sub.Out() } diff --git a/libs/pubsub/example_test.go b/libs/pubsub/example_test.go index 4e4634de5..a43696266 100644 --- a/libs/pubsub/example_test.go +++ b/libs/pubsub/example_test.go @@ -19,10 +19,9 @@ func TestExample(t *testing.T) { defer s.Stop() ctx := context.Background() - ch := make(chan interface{}, 1) - err := s.Subscribe(ctx, "example-client", query.MustParse("abci.account.name='John'"), ch) + subscription, err := s.Subscribe(ctx, "example-client", query.MustParse("abci.account.name='John'")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Tombstone", pubsub.NewTagMap(map[string]string{"abci.account.name": "John"})) + err = s.PublishWithTags(ctx, "Tombstone", map[string]string{"abci.account.name": "John"}) require.NoError(t, err) - assertReceive(t, "Tombstone", ch) + assertReceive(t, "Tombstone", subscription.Out()) } diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index a39c8c731..6e86a63c9 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -10,41 +10,25 @@ // match, this message will be pushed to all clients, subscribed to that query. // See query subpackage for our implementation. // -// Due to the blocking send implementation, a single subscriber can freeze an -// entire server by not reading messages before it unsubscribes. To avoid such -// scenario, subscribers must either: +// Example: // -// a) make sure they continue to read from the out channel until -// Unsubscribe(All) is called +// q, err := query.New("account.name='John'") +// if err != nil { +// return err +// } +// ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) +// defer cancel() +// subscription, err := pubsub.Subscribe(ctx, "johns-transactions", q) +// if err != nil { +// return err +// } // -// s.Subscribe(ctx, sub, qry, out) -// go func() { -// for msg := range out { -// // handle msg -// // will exit automatically when out is closed by Unsubscribe(All) -// } -// }() -// s.UnsubscribeAll(ctx, sub) -// -// b) drain the out channel before calling Unsubscribe(All) -// -// s.Subscribe(ctx, sub, qry, out) -// defer func() { -// // drain out to make sure we don't block -// LOOP: -// for { -// select { -// case <-out: -// default: -// break LOOP -// } -// } -// s.UnsubscribeAll(ctx, sub) -// }() -// for msg := range out { -// // handle msg -// if err != nil { -// return err +// for { +// select { +// case msg <- subscription.Out(): +// // handle msg.Data() and msg.Tags() +// case <-subscription.Cancelled(): +// return subscription.Err() // } // } // @@ -77,21 +61,25 @@ var ( ErrAlreadySubscribed = errors.New("already subscribed") ) -type cmd struct { - op operation - query Query - ch chan<- interface{} - clientID string - msg interface{} - tags TagMap -} - // Query defines an interface for a query to be used for subscribing. type Query interface { - Matches(tags TagMap) bool + Matches(tags map[string]string) bool String() string } +type cmd struct { + op operation + + // subscribe, unsubscribe + query Query + subscription *Subscription + clientID string + + // publish + msg interface{} + tags map[string]string +} + // Server allows clients to subscribe/unsubscribe for messages, publishing // messages with or without tags, and manages internal state. type Server struct { @@ -107,37 +95,6 @@ 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]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 string, ok bool) { - value, ok = ts[key] - return -} - -// Len returns the number of tags. -func (ts tagMap) Len() int { - return len(ts) -} - // NewServer returns a new server. See the commentary on the Option functions // for a detailed description of how to configure buffering. If no options are // provided, the resulting server's queue is unbuffered. @@ -174,11 +131,34 @@ func (s *Server) BufferCapacity() int { return s.cmdsCap } -// Subscribe creates a subscription for the given client. It accepts a channel -// on which messages matching the given query can be received. An error will be -// returned to the caller if the context is canceled or if subscription already -// exist for pair clientID and query. -func (s *Server) Subscribe(ctx context.Context, clientID string, query Query, out chan<- interface{}) error { +// Subscribe creates a subscription for the given client. +// +// An error will be returned to the caller if the context is canceled or if +// subscription already exist for pair clientID and query. +// +// outCapacity can be used to set a capacity for Subscription#Out channel (1 by +// default). Panics if outCapacity is less than or equal to zero. If you want +// an unbuffered channel, use SubscribeUnbuffered. +func (s *Server) Subscribe(ctx context.Context, clientID string, query Query, outCapacity ...int) (*Subscription, error) { + outCap := 1 + if len(outCapacity) > 0 { + if outCapacity[0] <= 0 { + panic("Negative or zero capacity. Use SubscribeUnbuffered if you want an unbuffered channel") + } + outCap = outCapacity[0] + } + + return s.subscribe(ctx, clientID, query, outCap) +} + +// SubscribeUnbuffered does the same as Subscribe, except it returns a +// subscription with unbuffered channel. Use with caution as it can freeze the +// server. +func (s *Server) SubscribeUnbuffered(ctx context.Context, clientID string, query Query) (*Subscription, error) { + return s.subscribe(ctx, clientID, query, 0) +} + +func (s *Server) subscribe(ctx context.Context, clientID string, query Query, outCapacity int) (*Subscription, error) { s.mtx.RLock() clientSubscriptions, ok := s.subscriptions[clientID] if ok { @@ -186,22 +166,23 @@ func (s *Server) Subscribe(ctx context.Context, clientID string, query Query, ou } s.mtx.RUnlock() if ok { - return ErrAlreadySubscribed + return nil, ErrAlreadySubscribed } + subscription := NewSubscription(outCapacity) select { - case s.cmds <- cmd{op: sub, clientID: clientID, query: query, ch: out}: + case s.cmds <- cmd{op: sub, clientID: clientID, query: query, subscription: subscription}: s.mtx.Lock() if _, ok = s.subscriptions[clientID]; !ok { s.subscriptions[clientID] = make(map[string]struct{}) } s.subscriptions[clientID][query.String()] = struct{}{} s.mtx.Unlock() - return nil + return subscription, nil case <-ctx.Done(): - return ctx.Err() + return nil, ctx.Err() case <-s.Quit(): - return nil + return nil, nil } } @@ -261,13 +242,13 @@ 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]string))) + return s.PublishWithTags(ctx, msg, make(map[string]string)) } // PublishWithTags publishes the given message with the set of tags. The set is // matched with clients queries. If there is a match, the message is sent to // the client. -func (s *Server) PublishWithTags(ctx context.Context, msg interface{}, tags TagMap) error { +func (s *Server) PublishWithTags(ctx context.Context, msg interface{}, tags map[string]string) error { select { case s.cmds <- cmd{op: pub, msg: msg, tags: tags}: return nil @@ -285,10 +266,8 @@ func (s *Server) OnStop() { // NOTE: not goroutine safe type state struct { - // query string -> client -> ch - queryToChanMap map[string]map[string]chan<- interface{} - // client -> query string -> struct{} - clientToQueryMap map[string]map[string]struct{} + // query string -> client -> subscription + subscriptions map[string]map[string]*Subscription // query string -> queryPlusRefCount queries map[string]*queryPlusRefCount } @@ -303,9 +282,8 @@ type queryPlusRefCount struct { // OnStart implements Service.OnStart by starting the server. func (s *Server) OnStart() error { go s.loop(state{ - queryToChanMap: make(map[string]map[string]chan<- interface{}), - clientToQueryMap: make(map[string]map[string]struct{}), - queries: make(map[string]*queryPlusRefCount), + subscriptions: make(map[string]map[string]*Subscription), + queries: make(map[string]*queryPlusRefCount), }) return nil } @@ -321,75 +299,57 @@ loop: switch cmd.op { case unsub: if cmd.query != nil { - state.remove(cmd.clientID, cmd.query) + state.remove(cmd.clientID, cmd.query.String(), ErrUnsubscribed) } else { - state.removeAll(cmd.clientID) + state.removeClient(cmd.clientID, ErrUnsubscribed) } case shutdown: - for clientID := range state.clientToQueryMap { - state.removeAll(clientID) - } + state.removeAll(nil) break loop case sub: - state.add(cmd.clientID, cmd.query, cmd.ch) + state.add(cmd.clientID, cmd.query, cmd.subscription) case pub: state.send(cmd.msg, cmd.tags) } } } -func (state *state) add(clientID string, q Query, ch chan<- interface{}) { +func (state *state) add(clientID string, q Query, subscription *Subscription) { qStr := q.String() - // initialize clientToChannelMap per query if needed - if _, ok := state.queryToChanMap[qStr]; !ok { - state.queryToChanMap[qStr] = make(map[string]chan<- interface{}) + // initialize subscription for this client per query if needed + if _, ok := state.subscriptions[qStr]; !ok { + state.subscriptions[qStr] = make(map[string]*Subscription) } - // create subscription - state.queryToChanMap[qStr][clientID] = ch + state.subscriptions[qStr][clientID] = subscription - // initialize queries if needed + // initialize query if needed if _, ok := state.queries[qStr]; !ok { state.queries[qStr] = &queryPlusRefCount{q: q, refCount: 0} } // increment reference counter state.queries[qStr].refCount++ - - // add client if needed - if _, ok := state.clientToQueryMap[clientID]; !ok { - state.clientToQueryMap[clientID] = make(map[string]struct{}) - } - state.clientToQueryMap[clientID][qStr] = struct{}{} } -func (state *state) remove(clientID string, q Query) { - qStr := q.String() - - clientToChannelMap, ok := state.queryToChanMap[qStr] +func (state *state) remove(clientID string, qStr string, reason error) { + clientSubscriptions, ok := state.subscriptions[qStr] if !ok { return } - ch, ok := clientToChannelMap[clientID] + subscription, ok := clientSubscriptions[clientID] if !ok { return } - close(ch) + subscription.cancel(reason) - // remove the query from client map. - // if client is not subscribed to anything else, remove it. - delete(state.clientToQueryMap[clientID], qStr) - if len(state.clientToQueryMap[clientID]) == 0 { - delete(state.clientToQueryMap, clientID) - } - - // remove the client from query map. + // remove client from query map. // if query has no other clients subscribed, remove it. - delete(state.queryToChanMap[qStr], clientID) - if len(state.queryToChanMap[qStr]) == 0 { - delete(state.queryToChanMap, qStr) + delete(state.subscriptions[qStr], clientID) + if len(state.subscriptions[qStr]) == 0 { + delete(state.subscriptions, qStr) } // decrease ref counter in queries @@ -400,41 +360,38 @@ func (state *state) remove(clientID string, q Query) { } } -func (state *state) removeAll(clientID string) { - queryMap, ok := state.clientToQueryMap[clientID] - if !ok { - return - } - - for qStr := range queryMap { - ch := state.queryToChanMap[qStr][clientID] - close(ch) - - // remove the client from query map. - // if query has no other clients subscribed, remove it. - delete(state.queryToChanMap[qStr], clientID) - if len(state.queryToChanMap[qStr]) == 0 { - delete(state.queryToChanMap, qStr) - } - - // decrease ref counter in queries - state.queries[qStr].refCount-- - // remove the query if nobody else is using it - if state.queries[qStr].refCount == 0 { - delete(state.queries, qStr) +func (state *state) removeClient(clientID string, reason error) { + for qStr, clientSubscriptions := range state.subscriptions { + if _, ok := clientSubscriptions[clientID]; ok { + state.remove(clientID, qStr, reason) } } - - // remove the client. - delete(state.clientToQueryMap, clientID) } -func (state *state) send(msg interface{}, tags TagMap) { - for qStr, clientToChannelMap := range state.queryToChanMap { +func (state *state) removeAll(reason error) { + for qStr, clientSubscriptions := range state.subscriptions { + for clientID := range clientSubscriptions { + state.remove(clientID, qStr, reason) + } + } +} + +func (state *state) send(msg interface{}, tags map[string]string) { + for qStr, clientSubscriptions := range state.subscriptions { q := state.queries[qStr].q if q.Matches(tags) { - for _, ch := range clientToChannelMap { - ch <- msg + for clientID, subscription := range clientSubscriptions { + if cap(subscription.out) == 0 { + // block on unbuffered channel + subscription.out <- Message{msg, tags} + } else { + // don't block on buffered channels + select { + case subscription.out <- Message{msg, tags}: + default: + state.remove(clientID, qStr, ErrOutOfCapacity) + } + } } } } diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index bb660d9e0..e2bd50e6c 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -27,16 +27,97 @@ func TestSubscribe(t *testing.T) { defer s.Stop() ctx := context.Background() - ch := make(chan interface{}, 1) - err := s.Subscribe(ctx, clientID, query.Empty{}, ch) + subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) require.NoError(t, err) err = s.Publish(ctx, "Ka-Zar") require.NoError(t, err) - assertReceive(t, "Ka-Zar", ch) + assertReceive(t, "Ka-Zar", subscription.Out()) - err = s.Publish(ctx, "Quicksilver") + published := make(chan struct{}) + go func() { + defer close(published) + + err := s.Publish(ctx, "Quicksilver") + assert.NoError(t, err) + + err = s.Publish(ctx, "Asylum") + assert.NoError(t, err) + }() + + select { + case <-published: + assertReceive(t, "Quicksilver", subscription.Out()) + assertCancelled(t, subscription, pubsub.ErrOutOfCapacity) + case <-time.After(100 * time.Millisecond): + t.Fatal("Expected Publish(Asylum) not to block") + } +} + +func TestSubscribeWithCapacity(t *testing.T) { + s := pubsub.NewServer() + s.SetLogger(log.TestingLogger()) + s.Start() + defer s.Stop() + + ctx := context.Background() + assert.Panics(t, func() { + s.Subscribe(ctx, clientID, query.Empty{}, -1) + }) + assert.Panics(t, func() { + s.Subscribe(ctx, clientID, query.Empty{}, 0) + }) + subscription, err := s.Subscribe(ctx, clientID, query.Empty{}, 1) require.NoError(t, err) - assertReceive(t, "Quicksilver", ch) + err = s.Publish(ctx, "Aggamon") + require.NoError(t, err) + assertReceive(t, "Aggamon", subscription.Out()) +} + +func TestSubscribeUnbuffered(t *testing.T) { + s := pubsub.NewServer() + s.SetLogger(log.TestingLogger()) + s.Start() + defer s.Stop() + + ctx := context.Background() + subscription, err := s.SubscribeUnbuffered(ctx, clientID, query.Empty{}) + require.NoError(t, err) + + published := make(chan struct{}) + go func() { + defer close(published) + + err := s.Publish(ctx, "Ultron") + assert.NoError(t, err) + + err = s.Publish(ctx, "Darkhawk") + assert.NoError(t, err) + }() + + select { + case <-published: + t.Fatal("Expected Publish(Darkhawk) to block") + case <-time.After(100 * time.Millisecond): + assertReceive(t, "Ultron", subscription.Out()) + assertReceive(t, "Darkhawk", subscription.Out()) + } +} + +func TestSlowClientIsRemovedWithErrOutOfCapacity(t *testing.T) { + s := pubsub.NewServer() + s.SetLogger(log.TestingLogger()) + s.Start() + defer s.Stop() + + ctx := context.Background() + subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) + require.NoError(t, err) + err = s.Publish(ctx, "Fat Cobra") + require.NoError(t, err) + err = s.Publish(ctx, "Viper") + require.NoError(t, err) + + assertCancelled(t, subscription, pubsub.ErrOutOfCapacity) } func TestDifferentClients(t *testing.T) { @@ -46,27 +127,24 @@ func TestDifferentClients(t *testing.T) { defer s.Stop() ctx := context.Background() - ch1 := make(chan interface{}, 1) - err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'"), ch1) + subscription1, err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Iceman", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock"})) + err = s.PublishWithTags(ctx, "Iceman", map[string]string{"tm.events.type": "NewBlock"}) require.NoError(t, err) - assertReceive(t, "Iceman", ch1) + assertReceive(t, "Iceman", subscription1.Out()) - ch2 := make(chan interface{}, 1) - err = s.Subscribe(ctx, "client-2", query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'"), ch2) + subscription2, err := s.Subscribe(ctx, "client-2", query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Ultimo", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock", "abci.account.name": "Igor"})) + err = s.PublishWithTags(ctx, "Ultimo", map[string]string{"tm.events.type": "NewBlock", "abci.account.name": "Igor"}) require.NoError(t, err) - assertReceive(t, "Ultimo", ch1) - assertReceive(t, "Ultimo", ch2) + assertReceive(t, "Ultimo", subscription1.Out()) + assertReceive(t, "Ultimo", subscription2.Out()) - 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) + subscription3, err := s.Subscribe(ctx, "client-3", query.MustParse("tm.events.type='NewRoundStep' AND abci.account.name='Igor' AND abci.invoice.number = 10")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Valeria Richards", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewRoundStep"})) + err = s.PublishWithTags(ctx, "Valeria Richards", map[string]string{"tm.events.type": "NewRoundStep"}) require.NoError(t, err) - assert.Zero(t, len(ch3)) + assert.Zero(t, len(subscription3.Out())) } func TestClientSubscribesTwice(t *testing.T) { @@ -78,20 +156,19 @@ func TestClientSubscribesTwice(t *testing.T) { ctx := context.Background() q := query.MustParse("tm.events.type='NewBlock'") - ch1 := make(chan interface{}, 1) - err := s.Subscribe(ctx, clientID, q, ch1) + subscription1, err := s.Subscribe(ctx, clientID, q) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Goblin Queen", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock"})) + err = s.PublishWithTags(ctx, "Goblin Queen", map[string]string{"tm.events.type": "NewBlock"}) require.NoError(t, err) - assertReceive(t, "Goblin Queen", ch1) + assertReceive(t, "Goblin Queen", subscription1.Out()) - ch2 := make(chan interface{}, 1) - err = s.Subscribe(ctx, clientID, q, ch2) + subscription2, err := s.Subscribe(ctx, clientID, q) require.Error(t, err) + require.Nil(t, subscription2) - err = s.PublishWithTags(ctx, "Spider-Man", pubsub.NewTagMap(map[string]string{"tm.events.type": "NewBlock"})) + err = s.PublishWithTags(ctx, "Spider-Man", map[string]string{"tm.events.type": "NewBlock"}) require.NoError(t, err) - assertReceive(t, "Spider-Man", ch1) + assertReceive(t, "Spider-Man", subscription1.Out()) } func TestUnsubscribe(t *testing.T) { @@ -101,18 +178,16 @@ func TestUnsubscribe(t *testing.T) { defer s.Stop() ctx := context.Background() - ch := make(chan interface{}) - err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"), ch) + subscription, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) err = s.Publish(ctx, "Nick Fury") require.NoError(t, err) - assert.Zero(t, len(ch), "Should not receive anything after Unsubscribe") + assert.Zero(t, len(subscription.Out()), "Should not receive anything after Unsubscribe") - _, ok := <-ch - assert.False(t, ok) + assertCancelled(t, subscription, pubsub.ErrUnsubscribed) } func TestClientUnsubscribesTwice(t *testing.T) { @@ -122,8 +197,7 @@ func TestClientUnsubscribesTwice(t *testing.T) { defer s.Stop() ctx := context.Background() - ch := make(chan interface{}) - err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"), ch) + _, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) @@ -141,18 +215,16 @@ func TestResubscribe(t *testing.T) { defer s.Stop() ctx := context.Background() - ch := make(chan interface{}) - err := s.Subscribe(ctx, clientID, query.Empty{}, ch) + subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) require.NoError(t, err) err = s.Unsubscribe(ctx, clientID, query.Empty{}) require.NoError(t, err) - ch = make(chan interface{}) - err = s.Subscribe(ctx, clientID, query.Empty{}, ch) + subscription, err = s.Subscribe(ctx, clientID, query.Empty{}) require.NoError(t, err) err = s.Publish(ctx, "Cable") require.NoError(t, err) - assertReceive(t, "Cable", ch) + assertReceive(t, "Cable", subscription.Out()) } func TestUnsubscribeAll(t *testing.T) { @@ -162,10 +234,9 @@ func TestUnsubscribeAll(t *testing.T) { defer s.Stop() ctx := context.Background() - ch1, ch2 := make(chan interface{}, 1), make(chan interface{}, 1) - err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'"), ch1) + subscription1, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) - err = s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlockHeader'"), ch2) + subscription2, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlockHeader'")) require.NoError(t, err) err = s.UnsubscribeAll(ctx, clientID) @@ -173,13 +244,11 @@ func TestUnsubscribeAll(t *testing.T) { err = s.Publish(ctx, "Nick Fury") require.NoError(t, err) - assert.Zero(t, len(ch1), "Should not receive anything after UnsubscribeAll") - assert.Zero(t, len(ch2), "Should not receive anything after UnsubscribeAll") + assert.Zero(t, len(subscription1.Out()), "Should not receive anything after UnsubscribeAll") + assert.Zero(t, len(subscription2.Out()), "Should not receive anything after UnsubscribeAll") - _, ok := <-ch1 - assert.False(t, ok) - _, ok = <-ch2 - assert.False(t, ok) + assertCancelled(t, subscription1, pubsub.ErrUnsubscribed) + assertCancelled(t, subscription2, pubsub.ErrUnsubscribed) } func TestBufferCapacity(t *testing.T) { @@ -217,18 +286,26 @@ func benchmarkNClients(n int, b *testing.B) { ctx := context.Background() for i := 0; i < n; i++ { - ch := make(chan interface{}) + subscription, err := s.Subscribe(ctx, clientID, query.MustParse(fmt.Sprintf("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = %d", i))) + if err != nil { + b.Fatal(err) + } go func() { - for range ch { + for { + select { + case <-subscription.Out(): + continue + case <-subscription.Cancelled(): + return + } } }() - s.Subscribe(ctx, clientID, query.MustParse(fmt.Sprintf("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = %d", i)), ch) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - s.PublishWithTags(ctx, "Gamora", pubsub.NewTagMap(map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": string(i)})) + s.PublishWithTags(ctx, "Gamora", map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": string(i)}) } } @@ -240,18 +317,26 @@ func benchmarkNClientsOneQuery(n int, b *testing.B) { ctx := context.Background() q := query.MustParse("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = 1") for i := 0; i < n; i++ { - ch := make(chan interface{}) + subscription, err := s.Subscribe(ctx, clientID, q) + if err != nil { + b.Fatal(err) + } go func() { - for range ch { + for { + select { + case <-subscription.Out(): + continue + case <-subscription.Cancelled(): + return + } } }() - s.Subscribe(ctx, clientID, q, ch) } b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - s.PublishWithTags(ctx, "Gamora", pubsub.NewTagMap(map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": "1"})) + s.PublishWithTags(ctx, "Gamora", map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": "1"}) } } @@ -259,14 +344,18 @@ func benchmarkNClientsOneQuery(n int, b *testing.B) { /// HELPERS /////////////////////////////////////////////////////////////////////////////// -func assertReceive(t *testing.T, expected interface{}, ch <-chan interface{}, msgAndArgs ...interface{}) { +func assertReceive(t *testing.T, expected interface{}, ch <-chan pubsub.Message, msgAndArgs ...interface{}) { select { case actual := <-ch: - if actual != nil { - assert.Equal(t, expected, actual, msgAndArgs...) - } + assert.Equal(t, expected, actual.Data(), msgAndArgs...) case <-time.After(1 * time.Second): t.Errorf("Expected to receive %v from the channel, got nothing after 1s", expected) debug.PrintStack() } } + +func assertCancelled(t *testing.T, subscription *pubsub.Subscription, err error) { + _, ok := <-subscription.Cancelled() + assert.False(t, ok) + assert.Equal(t, err, subscription.Err()) +} diff --git a/libs/pubsub/query/empty.go b/libs/pubsub/query/empty.go index 17d7acefa..83271f047 100644 --- a/libs/pubsub/query/empty.go +++ b/libs/pubsub/query/empty.go @@ -1,13 +1,11 @@ package query -import "github.com/tendermint/tendermint/libs/pubsub" - // Empty query matches any set of tags. type Empty struct { } // Matches always returns true. -func (Empty) Matches(tags pubsub.TagMap) bool { +func (Empty) Matches(tags map[string]string) bool { return true } diff --git a/libs/pubsub/query/empty_test.go b/libs/pubsub/query/empty_test.go index 6183b6bd4..141fb9515 100644 --- a/libs/pubsub/query/empty_test.go +++ b/libs/pubsub/query/empty_test.go @@ -5,14 +5,13 @@ import ( "github.com/stretchr/testify/assert" - "github.com/tendermint/tendermint/libs/pubsub" "github.com/tendermint/tendermint/libs/pubsub/query" ) func TestEmptyQueryMatchesAnything(t *testing.T) { q := query.Empty{} - 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"}))) + assert.True(t, q.Matches(map[string]string{})) + assert.True(t, q.Matches(map[string]string{"Asher": "Roth"})) + assert.True(t, q.Matches(map[string]string{"Route": "66"})) + assert.True(t, q.Matches(map[string]string{"Route": "66", "Billy": "Blue"})) } diff --git a/libs/pubsub/query/query.go b/libs/pubsub/query/query.go index ec187486e..189110a3e 100644 --- a/libs/pubsub/query/query.go +++ b/libs/pubsub/query/query.go @@ -14,8 +14,6 @@ import ( "strconv" "strings" "time" - - "github.com/tendermint/tendermint/libs/pubsub" ) // Query holds the query string and the query parser. @@ -154,8 +152,8 @@ func (q *Query) Conditions() []Condition { // // For example, query "name=John" matches tags = {"name": "John"}. More // examples could be found in parser_test.go and query_test.go. -func (q *Query) Matches(tags pubsub.TagMap) bool { - if tags.Len() == 0 { +func (q *Query) Matches(tags map[string]string) bool { + if len(tags) == 0 { return false } @@ -240,9 +238,9 @@ func (q *Query) Matches(tags pubsub.TagMap) bool { // value from it to the operand using the operator. // // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" } -func match(tag string, op Operator, operand reflect.Value, tags pubsub.TagMap) bool { +func match(tag string, op Operator, operand reflect.Value, tags map[string]string) bool { // look up the tag from the query in tags - value, ok := tags.Get(tag) + value, ok := tags[tag] if !ok { return false } diff --git a/libs/pubsub/query/query_test.go b/libs/pubsub/query/query_test.go index d1810f466..a3d83b259 100644 --- a/libs/pubsub/query/query_test.go +++ b/libs/pubsub/query/query_test.go @@ -8,7 +8,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/libs/pubsub" "github.com/tendermint/tendermint/libs/pubsub/query" ) @@ -53,9 +52,9 @@ func TestMatches(t *testing.T) { } if tc.matches { - assert.True(t, q.Matches(pubsub.NewTagMap(tc.tags)), "Query '%s' should match %v", tc.s, tc.tags) + assert.True(t, q.Matches(tc.tags), "Query '%s' should match %v", tc.s, tc.tags) } else { - assert.False(t, q.Matches(pubsub.NewTagMap(tc.tags)), "Query '%s' should not match %v", tc.s, tc.tags) + assert.False(t, q.Matches(tc.tags), "Query '%s' should not match %v", tc.s, tc.tags) } } } diff --git a/libs/pubsub/subscription.go b/libs/pubsub/subscription.go new file mode 100644 index 000000000..2660439f5 --- /dev/null +++ b/libs/pubsub/subscription.go @@ -0,0 +1,89 @@ +package pubsub + +import ( + "errors" + "sync" +) + +var ( + // ErrUnsubscribed is returned by Err when a client unsubscribes. + ErrUnsubscribed = errors.New("client unsubscribed") + + // ErrOutOfCapacity is returned by Err when a client is not pulling messages + // fast enough. Note the client's subscription will be terminated. + ErrOutOfCapacity = errors.New("client is not pulling messages fast enough") +) + +// A Subscription represents a client subscription for a particular query and +// consists of three things: +// 1) channel onto which messages and tags are published +// 2) channel which is closed if a client is too slow or choose to unsubscribe +// 3) err indicating the reason for (2) +type Subscription struct { + out chan Message + + cancelled chan struct{} + mtx sync.RWMutex + err error +} + +// NewSubscription returns a new subscription with the given outCapacity. +func NewSubscription(outCapacity int) *Subscription { + return &Subscription{ + out: make(chan Message, outCapacity), + cancelled: make(chan struct{}), + } +} + +// Out returns a channel onto which messages and tags are published. +// Unsubscribe/UnsubscribeAll does not close the channel to avoid clients from +// receiving a nil message. +func (s *Subscription) Out() <-chan Message { + return s.out +} + +// Cancelled returns a channel that's closed when the subscription is +// terminated and supposed to be used in a select statement. +func (s *Subscription) Cancelled() <-chan struct{} { + return s.cancelled +} + +// Err returns nil if the channel returned by Cancelled is not yet closed. +// If the channel is closed, Err returns a non-nil error explaining why: +// - ErrUnsubscribed if the subscriber choose to unsubscribe, +// - ErrOutOfCapacity if the subscriber is not pulling messages fast enough +// and the channel returned by Out became full, +// After Err returns a non-nil error, successive calls to Err return the same +// error. +func (s *Subscription) Err() error { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.err +} + +func (s *Subscription) cancel(err error) { + s.mtx.Lock() + s.err = err + s.mtx.Unlock() + close(s.cancelled) +} + +// Message glues data and tags together. +type Message struct { + data interface{} + tags map[string]string +} + +func NewMessage(data interface{}, tags map[string]string) Message { + return Message{data, tags} +} + +// Data returns an original data published. +func (msg Message) Data() interface{} { + return msg.data +} + +// Tags returns tags, which matched the client's query. +func (msg Message) Tags() map[string]string { + return msg.tags +} diff --git a/node/node_test.go b/node/node_test.go index e370e5f24..0fce0dd96 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -42,11 +42,12 @@ func TestNodeStartStop(t *testing.T) { t.Logf("Started node %v", n.sw.NodeInfo()) // wait for the node to produce a block - blockCh := make(chan interface{}) - err = n.EventBus().Subscribe(context.Background(), "node_test", types.EventQueryNewBlock, blockCh) + blocksSub, err := n.EventBus().Subscribe(context.Background(), "node_test", types.EventQueryNewBlock) require.NoError(t, err) select { - case <-blockCh: + case <-blocksSub.Out(): + case <-blocksSub.Cancelled(): + t.Fatal("blocksSub was cancelled") case <-time.After(10 * time.Second): t.Fatal("timed out waiting for the node to produce a block") } diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go index 2e80a3063..ec63fb3be 100644 --- a/rpc/client/helpers.go +++ b/rpc/client/helpers.go @@ -59,32 +59,20 @@ func WaitForOneEvent(c EventsClient, evtTyp string, timeout time.Duration) (type const subscriber = "helpers" ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() - evts := make(chan interface{}, 1) // register for the next event of this type - query := types.QueryForEvent(evtTyp) - err := c.Subscribe(ctx, subscriber, query, evts) + sub, err := c.Subscribe(ctx, subscriber, types.QueryForEvent(evtTyp)) if err != nil { return nil, errors.Wrap(err, "failed to subscribe") } - // make sure to unregister after the test is over - defer func() { - // drain evts to make sure we don't block - LOOP: - for { - select { - case <-evts: - default: - break LOOP - } - } - c.UnsubscribeAll(ctx, subscriber) - }() + defer c.UnsubscribeAll(ctx, subscriber) select { - case evt := <-evts: - return evt.(types.TMEventData), nil + case msg := <-sub.Out(): + return msg.Data().(types.TMEventData), nil + case <-sub.Cancelled(): + return nil, errors.New("subscription was cancelled") case <-ctx.Done(): return nil, errors.New("timed out waiting for event") } diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 1a1d88c47..a1dee9913 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -249,6 +249,28 @@ func (c *HTTP) Validators(height *int64) (*ctypes.ResultValidators, error) { /** websocket event stuff here... **/ +type subscription struct { + out chan tmpubsub.Message + cancelled chan struct{} + + mtx sync.RWMutex + err error +} + +func (s *subscription) Out() <-chan tmpubsub.Message { + return s.out +} + +func (s *subscription) Cancelled() <-chan struct{} { + return s.cancelled +} + +func (s *subscription) Err() error { + s.mtx.RLock() + defer s.mtx.RUnlock() + return s.err +} + type WSEvents struct { cmn.BaseService cdc *amino.Codec @@ -256,8 +278,9 @@ type WSEvents struct { endpoint string ws *rpcclient.WSClient - mtx sync.RWMutex - subscriptions map[string]chan<- interface{} + mtx sync.RWMutex + // query -> subscription + subscriptions map[string]*subscription } func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents { @@ -265,7 +288,7 @@ func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents { cdc: cdc, endpoint: endpoint, remote: remote, - subscriptions: make(map[string]chan<- interface{}), + subscriptions: make(map[string]*subscription), } wsEvents.BaseService = *cmn.NewBaseService(nil, "WSEvents", wsEvents) @@ -295,21 +318,29 @@ func (w *WSEvents) OnStop() { } } -func (w *WSEvents) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error { +func (w *WSEvents) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (types.Subscription, error) { q := query.String() err := w.ws.Subscribe(ctx, q) if err != nil { - return err + return nil, err + } + + outCap := 1 + if len(outCapacity) > 0 && outCapacity[0] >= 0 { + outCap = outCapacity[0] } w.mtx.Lock() // subscriber param is ignored because Tendermint will override it with // remote IP anyway. - w.subscriptions[q] = out + w.subscriptions[q] = &subscription{ + out: make(chan tmpubsub.Message, outCap), + cancelled: make(chan struct{}), + } w.mtx.Unlock() - return nil + return w.subscriptions[q], nil } func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error { @@ -321,9 +352,12 @@ func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber string, query tmp } w.mtx.Lock() - ch, ok := w.subscriptions[q] + sub, ok := w.subscriptions[q] if ok { - close(ch) + close(sub.cancelled) + sub.mtx.Lock() + sub.err = errors.New("unsubscribed") + sub.mtx.Unlock() delete(w.subscriptions, q) } w.mtx.Unlock() @@ -338,10 +372,13 @@ func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error } w.mtx.Lock() - for _, ch := range w.subscriptions { - close(ch) + for _, sub := range w.subscriptions { + close(sub.cancelled) + sub.mtx.Lock() + sub.err = errors.New("unsubscribed") + sub.mtx.Unlock() } - w.subscriptions = make(map[string]chan<- interface{}) + w.subscriptions = make(map[string]*subscription) w.mtx.Unlock() return nil @@ -381,8 +418,8 @@ func (w *WSEvents) eventListener() { // NOTE: writing also happens inside mutex so we can't close a channel in // Unsubscribe/UnsubscribeAll. w.mtx.RLock() - if ch, ok := w.subscriptions[result.Query]; ok { - ch <- result.Data + if sub, ok := w.subscriptions[result.Query]; ok { + sub.out <- tmpubsub.NewMessage(result.Data, result.Tags) } w.mtx.RUnlock() case <-w.Quit(): diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index ba8fb3f17..33a1ce225 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -140,8 +140,8 @@ func (Local) TxSearch(query string, prove bool, page, perPage int) (*ctypes.Resu return core.TxSearch(query, prove, page, perPage) } -func (c *Local) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error { - return c.EventBus.Subscribe(ctx, subscriber, query, out) +func (c *Local) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (types.Subscription, error) { + return c.EventBus.Subscribe(ctx, subscriber, query, outCapacity...) } func (c *Local) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error { diff --git a/rpc/core/events.go b/rpc/core/events.go index 21979f018..22c7ea782 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -101,16 +101,30 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscri ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) defer cancel() - ch := make(chan interface{}) - err = eventBusFor(wsCtx).Subscribe(ctx, addr, q, ch) + sub, err := eventBusFor(wsCtx).Subscribe(ctx, addr, q) if err != nil { return nil, err } go func() { - for event := range ch { - tmResult := &ctypes.ResultEvent{Query: query, Data: event.(tmtypes.TMEventData)} - wsCtx.TryWriteRPCResponse(rpctypes.NewRPCSuccessResponse(wsCtx.Codec(), rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", wsCtx.Request.ID)), tmResult)) + for { + select { + case msg := <-sub.Out(): + resultEvent := &ctypes.ResultEvent{Query: query, Data: msg.Data(), Tags: msg.Tags()} + wsCtx.TryWriteRPCResponse( + rpctypes.NewRPCSuccessResponse( + wsCtx.Codec(), + rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", wsCtx.Request.ID)), + resultEvent, + )) + case <-sub.Cancelled(): + wsCtx.TryWriteRPCResponse( + rpctypes.RPCServerError(rpctypes.JSONRPCStringID( + fmt.Sprintf("%v#event", wsCtx.Request.ID)), + fmt.Errorf("subscription was cancelled (reason: %v)", sub.Err()), + )) + return + } } }() diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index d6dcc93e4..0295bf7f3 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -169,26 +169,14 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // Subscribe to tx being committed in block. ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) defer cancel() - deliverTxResCh := make(chan interface{}, 1) q := types.EventQueryTxFor(tx) - err := eventBus.Subscribe(ctx, "mempool", q, deliverTxResCh) + deliverTxSub, err := eventBus.Subscribe(ctx, "mempool", q) if err != nil { err = errors.Wrap(err, "failed to subscribe to tx") logger.Error("Error on broadcast_tx_commit", "err", err) return nil, err } - defer func() { - // drain deliverTxResCh to make sure we don't block - LOOP: - for { - select { - case <-deliverTxResCh: - default: - break LOOP - } - } - eventBus.Unsubscribe(context.Background(), "mempool", q) - }() + defer eventBus.Unsubscribe(context.Background(), "mempool", q) // Broadcast tx and wait for CheckTx result checkTxResCh := make(chan *abci.Response, 1) @@ -213,17 +201,22 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // TODO: configurable? var deliverTxTimeout = rpcserver.WriteTimeout / 2 select { - case deliverTxResMsg, ok := <-deliverTxResCh: // The tx was included in a block. - if !ok { - return nil, errors.New("Error on broadcastTxCommit: expected DeliverTxResult, got nil. Did the Tendermint stop?") - } - deliverTxRes := deliverTxResMsg.(types.EventDataTx) + case msg := <-deliverTxSub.Out(): // The tx was included in a block. + deliverTxRes := msg.Data().(types.EventDataTx) return &ctypes.ResultBroadcastTxCommit{ CheckTx: *checkTxRes, DeliverTx: deliverTxRes.Result, Hash: tx.Hash(), Height: deliverTxRes.Height, }, nil + case <-deliverTxSub.Cancelled(): + err = errors.New("deliverTxSub was cancelled. Did the Tendermint stop?") + logger.Error("Error on broadcastTxCommit", "err", err) + return &ctypes.ResultBroadcastTxCommit{ + CheckTx: *checkTxRes, + DeliverTx: abci.ResponseDeliverTx{}, + Hash: tx.Hash(), + }, err case <-time.After(deliverTxTimeout): err = errors.New("Timed out waiting for tx to be included in a block") logger.Error("Error on broadcastTxCommit", "err", err) diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index f3ce4054b..85f2746d4 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -205,4 +205,5 @@ type ( type ResultEvent struct { Query string `json:"query"` Data types.TMEventData `json:"data"` + Tags map[string]string `json:"tags"` } diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index edab88fe5..80eb4308a 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -526,6 +526,7 @@ func (wsc *wsConnection) OnStart() error { func (wsc *wsConnection) OnStop() { // Both read and write loops close the websocket connection when they exit their loops. // The writeChan is never closed, to allow WriteRPCResponse() to fail. + if wsc.eventSub != nil { wsc.eventSub.UnsubscribeAll(context.TODO(), wsc.remoteAddr) } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index e0753a03b..d4e82b10c 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -12,6 +12,7 @@ import ( amino "github.com/tendermint/go-amino" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" + tmtypes "github.com/tendermint/tendermint/types" ) // a wrapper to emulate a sum type: jsonrpcid = string | int @@ -244,19 +245,19 @@ type WSRPCConnection interface { Codec() *amino.Codec } -// EventSubscriber mirros tendermint/tendermint/types.EventBusSubscriber -type EventSubscriber interface { - Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error - Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error - UnsubscribeAll(ctx context.Context, subscriber string) error -} - // websocket-only RPCFuncs take this as the first parameter. type WSRPCContext struct { Request RPCRequest WSRPCConnection } +// EventSubscriber mirrors tendermint/tendermint/types.EventBusSubscriber +type EventSubscriber interface { + Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (tmtypes.Subscription, error) + Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error + UnsubscribeAll(ctx context.Context, subscriber string) error +} + //---------------------------------------- // SOCKETS // diff --git a/state/execution_test.go b/state/execution_test.go index 143faa1ab..94336851c 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -317,8 +317,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { defer eventBus.Stop() blockExec.SetEventBus(eventBus) - updatesCh := make(chan interface{}, 1) - err = eventBus.Subscribe(context.Background(), "TestEndBlockValidatorUpdates", types.EventQueryValidatorSetUpdates, updatesCh) + updatesSub, err := eventBus.Subscribe(context.Background(), "TestEndBlockValidatorUpdates", types.EventQueryValidatorSetUpdates) require.NoError(t, err) block := makeBlock(state, 1) @@ -342,13 +341,15 @@ func TestEndBlockValidatorUpdates(t *testing.T) { // test we threw an event select { - case e := <-updatesCh: - event, ok := e.(types.EventDataValidatorSetUpdates) - require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", e) + case msg := <-updatesSub.Out(): + event, ok := msg.Data().(types.EventDataValidatorSetUpdates) + require.True(t, ok, "Expected event of type EventDataValidatorSetUpdates, got %T", msg.Data()) if assert.NotEmpty(t, event.ValidatorUpdates) { assert.Equal(t, pubkey, event.ValidatorUpdates[0].PubKey) assert.EqualValues(t, 10, event.ValidatorUpdates[0].VotingPower) } + case <-updatesSub.Cancelled(): + t.Fatalf("updatesSub was cancelled (reason: %v)", updatesSub.Err()) case <-time.After(1 * time.Second): t.Fatal("Did not receive EventValidatorSetUpdates within 1 sec.") } diff --git a/state/txindex/indexer_service.go b/state/txindex/indexer_service.go index 088252f5e..7a7299c78 100644 --- a/state/txindex/indexer_service.go +++ b/state/txindex/indexer_service.go @@ -31,35 +31,40 @@ func NewIndexerService(idr TxIndexer, eventBus *types.EventBus) *IndexerService // OnStart implements cmn.Service by subscribing for all transactions // and indexing them by tags. func (is *IndexerService) OnStart() error { - blockHeadersCh := make(chan interface{}) - if err := is.eventBus.Subscribe(context.Background(), subscriber, types.EventQueryNewBlockHeader, blockHeadersCh); err != nil { + // Use SubscribeUnbuffered here to ensure both subscriptions does not get + // cancelled due to not pulling messages fast enough. Cause this might + // sometimes happen when there are no other subscribers. + + blockHeadersSub, err := is.eventBus.SubscribeUnbuffered(context.Background(), subscriber, types.EventQueryNewBlockHeader) + if err != nil { return err } - txsCh := make(chan interface{}) - if err := is.eventBus.Subscribe(context.Background(), subscriber, types.EventQueryTx, txsCh); err != nil { + txsSub, err := is.eventBus.SubscribeUnbuffered(context.Background(), subscriber, types.EventQueryTx) + if err != nil { return err } go func() { for { - e, ok := <-blockHeadersCh - if !ok { - return - } - header := e.(types.EventDataNewBlockHeader).Header + msg := <-blockHeadersSub.Out() + header := msg.Data().(types.EventDataNewBlockHeader).Header batch := NewBatch(header.NumTxs) for i := int64(0); i < header.NumTxs; i++ { - e, ok := <-txsCh - if !ok { - is.Logger.Error("Failed to index all transactions due to closed transactions channel", "height", header.Height, "numTxs", header.NumTxs, "numProcessed", i) - return + msg2 := <-txsSub.Out() + txResult := msg2.Data().(types.EventDataTx).TxResult + if err = batch.Add(&txResult); err != nil { + is.Logger.Error("Can't add tx to batch", + "height", header.Height, + "index", txResult.Index, + "err", err) } - txResult := e.(types.EventDataTx).TxResult - batch.Add(&txResult) } - is.idr.AddBatch(batch) - is.Logger.Info("Indexed block", "height", header.Height) + if err = is.idr.AddBatch(batch); err != nil { + is.Logger.Error("Failed to index block", "height", header.Height, "err", err) + } else { + is.Logger.Info("Indexed block", "height", header.Height) + } } }() return nil diff --git a/state/txindex/indexer_service_test.go b/state/txindex/indexer_service_test.go new file mode 100644 index 000000000..982d7b8c4 --- /dev/null +++ b/state/txindex/indexer_service_test.go @@ -0,0 +1,64 @@ +package txindex_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/state/txindex" + "github.com/tendermint/tendermint/state/txindex/kv" + "github.com/tendermint/tendermint/types" +) + +func TestIndexerServiceIndexesBlocks(t *testing.T) { + // event bus + eventBus := types.NewEventBus() + eventBus.SetLogger(log.TestingLogger()) + err := eventBus.Start() + require.NoError(t, err) + defer eventBus.Stop() + + // tx indexer + store := db.NewMemDB() + txIndexer := kv.NewTxIndex(store, kv.IndexAllTags()) + + service := txindex.NewIndexerService(txIndexer, eventBus) + service.SetLogger(log.TestingLogger()) + err = service.Start() + require.NoError(t, err) + defer service.Stop() + + // publish block with txs + eventBus.PublishEventNewBlockHeader(types.EventDataNewBlockHeader{ + Header: types.Header{Height: 1, NumTxs: 2}, + }) + txResult1 := &types.TxResult{ + Height: 1, + Index: uint32(0), + Tx: types.Tx("foo"), + Result: abci.ResponseDeliverTx{Code: 0}, + } + eventBus.PublishEventTx(types.EventDataTx{*txResult1}) + txResult2 := &types.TxResult{ + Height: 1, + Index: uint32(1), + Tx: types.Tx("bar"), + Result: abci.ResponseDeliverTx{Code: 0}, + } + eventBus.PublishEventTx(types.EventDataTx{*txResult2}) + + time.Sleep(100 * time.Millisecond) + + // check the result + res, err := txIndexer.Get(types.Tx("foo").Hash()) + assert.NoError(t, err) + assert.Equal(t, txResult1, res) + res, err = txIndexer.Get(types.Tx("bar").Hash()) + assert.NoError(t, err) + assert.Equal(t, txResult2, res) +} diff --git a/types/event_bus.go b/types/event_bus.go index 055cbd3fe..2aa84a4a0 100644 --- a/types/event_bus.go +++ b/types/event_bus.go @@ -12,11 +12,17 @@ import ( const defaultCapacity = 0 type EventBusSubscriber interface { - Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error + Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (Subscription, error) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error UnsubscribeAll(ctx context.Context, subscriber string) error } +type Subscription interface { + Out() <-chan tmpubsub.Message + Cancelled() <-chan struct{} + Err() error +} + // EventBus is a common bus for all events going through the system. All calls // are proxied to underlying pubsub server. All events must be published using // EventBus to ensure correct data types. @@ -52,8 +58,14 @@ func (b *EventBus) OnStop() { b.pubsub.Stop() } -func (b *EventBus) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, out chan<- interface{}) error { - return b.pubsub.Subscribe(ctx, subscriber, query, out) +func (b *EventBus) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (Subscription, error) { + return b.pubsub.Subscribe(ctx, subscriber, query, outCapacity...) +} + +// This method can be used for a local consensus explorer and synchronous +// testing. Do not use for for public facing / untrusted subscriptions! +func (b *EventBus) SubscribeUnbuffered(ctx context.Context, subscriber string, query tmpubsub.Query) (Subscription, error) { + return b.pubsub.SubscribeUnbuffered(ctx, subscriber, query) } func (b *EventBus) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error { @@ -67,7 +79,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]string{EventTypeKey: eventType})) + b.pubsub.PublishWithTags(ctx, eventData, map[string]string{EventTypeKey: eventType}) return nil } @@ -95,7 +107,7 @@ func (b *EventBus) PublishEventNewBlock(data EventDataNewBlock) error { logIfTagExists(EventTypeKey, tags, b.Logger) tags[EventTypeKey] = EventNewBlock - b.pubsub.PublishWithTags(ctx, data, tmpubsub.NewTagMap(tags)) + b.pubsub.PublishWithTags(ctx, data, tags) return nil } @@ -111,7 +123,7 @@ func (b *EventBus) PublishEventNewBlockHeader(data EventDataNewBlockHeader) erro logIfTagExists(EventTypeKey, tags, b.Logger) tags[EventTypeKey] = EventNewBlockHeader - b.pubsub.PublishWithTags(ctx, data, tmpubsub.NewTagMap(tags)) + b.pubsub.PublishWithTags(ctx, data, tags) return nil } @@ -142,7 +154,7 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error { logIfTagExists(TxHeightKey, tags, b.Logger) tags[TxHeightKey] = fmt.Sprintf("%d", data.Height) - b.pubsub.PublishWithTags(ctx, data, tmpubsub.NewTagMap(tags)) + b.pubsub.PublishWithTags(ctx, data, tags) return nil } diff --git a/types/event_bus_test.go b/types/event_bus_test.go index 6845927be..508b423a6 100644 --- a/types/event_bus_test.go +++ b/types/event_bus_test.go @@ -24,23 +24,20 @@ func TestEventBusPublishEventTx(t *testing.T) { tx := Tx("foo") result := abci.ResponseDeliverTx{Data: []byte("bar"), Tags: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}} - 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' AND baz=1", tx.Hash()) - err = eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query), txEventsCh) + txsSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) require.NoError(t, err) done := make(chan struct{}) go func() { - for e := range txEventsCh { - edt := e.(EventDataTx) - assert.Equal(t, int64(1), edt.Height) - assert.Equal(t, uint32(0), edt.Index) - assert.Equal(t, tx, edt.Tx) - assert.Equal(t, result, edt.Result) - close(done) - } + msg := <-txsSub.Out() + edt := msg.Data().(EventDataTx) + assert.Equal(t, int64(1), edt.Height) + assert.Equal(t, uint32(0), edt.Index) + assert.Equal(t, tx, edt.Tx) + assert.Equal(t, result, edt.Result) + close(done) }() err = eventBus.PublishEventTx(EventDataTx{TxResult{ @@ -68,22 +65,19 @@ func TestEventBusPublishEventNewBlock(t *testing.T) { resultBeginBlock := abci.ResponseBeginBlock{Tags: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}} resultEndBlock := abci.ResponseEndBlock{Tags: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}} - txEventsCh := make(chan interface{}) - // PublishEventNewBlock adds the tm.event tag, so the query below should work query := "tm.event='NewBlock' AND baz=1 AND foz=2" - err = eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query), txEventsCh) + blocksSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) require.NoError(t, err) done := make(chan struct{}) go func() { - for e := range txEventsCh { - edt := e.(EventDataNewBlock) - assert.Equal(t, block, edt.Block) - assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) - assert.Equal(t, resultEndBlock, edt.ResultEndBlock) - close(done) - } + msg := <-blocksSub.Out() + edt := msg.Data().(EventDataNewBlock) + assert.Equal(t, block, edt.Block) + assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) + assert.Equal(t, resultEndBlock, edt.ResultEndBlock) + close(done) }() err = eventBus.PublishEventNewBlock(EventDataNewBlock{ @@ -110,22 +104,19 @@ func TestEventBusPublishEventNewBlockHeader(t *testing.T) { resultBeginBlock := abci.ResponseBeginBlock{Tags: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}} resultEndBlock := abci.ResponseEndBlock{Tags: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}} - txEventsCh := make(chan interface{}) - // PublishEventNewBlockHeader adds the tm.event tag, so the query below should work query := "tm.event='NewBlockHeader' AND baz=1 AND foz=2" - err = eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query), txEventsCh) + headersSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) require.NoError(t, err) done := make(chan struct{}) go func() { - for e := range txEventsCh { - edt := e.(EventDataNewBlockHeader) - assert.Equal(t, block.Header, edt.Header) - assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) - assert.Equal(t, resultEndBlock, edt.ResultEndBlock) - close(done) - } + msg := <-headersSub.Out() + edt := msg.Data().(EventDataNewBlockHeader) + assert.Equal(t, block.Header, edt.Header) + assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock) + assert.Equal(t, resultEndBlock, edt.ResultEndBlock) + close(done) }() err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{ @@ -148,18 +139,19 @@ func TestEventBusPublish(t *testing.T) { require.NoError(t, err) defer eventBus.Stop() - eventsCh := make(chan interface{}) - err = eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, eventsCh) + const numEventsExpected = 14 + + sub, err := eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, numEventsExpected) require.NoError(t, err) - const numEventsExpected = 14 done := make(chan struct{}) go func() { numEvents := 0 - for range eventsCh { + for range sub.Out() { numEvents++ if numEvents >= numEventsExpected { close(done) + return } } }() @@ -243,15 +235,22 @@ func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *tes q := EventQueryNewBlock for i := 0; i < numClients; i++ { - ch := make(chan interface{}) - go func() { - for range ch { - } - }() if randQueries { q = randQuery() } - eventBus.Subscribe(ctx, fmt.Sprintf("client-%d", i), q, ch) + sub, err := eventBus.Subscribe(ctx, fmt.Sprintf("client-%d", i), q) + if err != nil { + b.Fatal(err) + } + go func() { + for { + select { + case <-sub.Out(): + case <-sub.Cancelled(): + return + } + } + }() } eventType := EventNewBlock From e0adc5e807c8c2ea4ac03f8eeeb006b1a154d453 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Sat, 23 Feb 2019 16:25:57 +0100 Subject: [PATCH 068/259] secret connection check all zeroes (#3347) * reject the shared secret if is all zeros in case the blacklist was not sufficient * Add test that verifies lower order pub-keys are rejected at the DH step * Update changelog * fix typo in test-comment --- CHANGELOG_PENDING.md | 4 +++- p2p/conn/secret_connection.go | 28 ++++++++++++++++++++++++---- p2p/conn/secret_connection_test.go | 17 +++++++++++++++++ 3 files changed, 44 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c123f1c34..65fc4bd25 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,4 +22,6 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: ### BUG FIXES: -- [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) \ No newline at end of file + +- [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection +- [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index d2ba6fb51..2a06945b0 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -29,7 +29,10 @@ const aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag const aeadKeySize = chacha20poly1305.KeySize const aeadNonceSize = chacha20poly1305.NonceSize -var ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer") +var ( + ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer") + ErrSharedSecretIsZero = errors.New("shared secret is all zeroes") +) // SecretConnection implements net.Conn. // It is an implementation of the STS protocol. @@ -90,7 +93,10 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (* locIsLeast := bytes.Equal(locEphPub[:], loEphPub[:]) // Compute common diffie hellman secret using X25519. - dhSecret := computeDHSecret(remEphPub, locEphPriv) + dhSecret, err := computeDHSecret(remEphPub, locEphPriv) + if err != nil { + return nil, err + } // generate the secret used for receiving, sending, challenge via hkdf-sha2 on dhSecret recvSecret, sendSecret, challenge := deriveSecretAndChallenge(dhSecret, locIsLeast) @@ -230,9 +236,12 @@ func (sc *SecretConnection) SetWriteDeadline(t time.Time) error { func genEphKeys() (ephPub, ephPriv *[32]byte) { var err error + // TODO: Probably not a problem but ask Tony: different from the rust implementation (uses x25519-dalek), + // we do not "clamp" the private key scalar: + // see: https://github.com/dalek-cryptography/x25519-dalek/blob/34676d336049df2bba763cc076a75e47ae1f170f/src/x25519.rs#L56-L74 ephPub, ephPriv, err = box.GenerateKey(crand.Reader) if err != nil { - panic("Could not generate ephemeral keypairs") + panic("Could not generate ephemeral key-pair") } return } @@ -349,9 +358,20 @@ func deriveSecretAndChallenge(dhSecret *[32]byte, locIsLeast bool) (recvSecret, return } -func computeDHSecret(remPubKey, locPrivKey *[32]byte) (shrKey *[32]byte) { +// computeDHSecret computes a shared secret Diffie-Hellman secret +// from a the own local private key and the others public key. +// +// It returns an error if the computed shared secret is all zeroes. +func computeDHSecret(remPubKey, locPrivKey *[32]byte) (shrKey *[32]byte, err error) { shrKey = new([32]byte) curve25519.ScalarMult(shrKey, locPrivKey, remPubKey) + + // reject if the returned shared secret is all zeroes + // related to: https://github.com/tendermint/tendermint/issues/3010 + zero := new([32]byte) + if subtle.ConstantTimeCompare(shrKey[:], zero[:]) == 1 { + return nil, ErrSharedSecretIsZero + } return } diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 5c389ee3a..7e264e913 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -100,8 +100,12 @@ func TestSecretConnectionHandshake(t *testing.T) { } } +// Test that shareEphPubKey rejects lower order public keys based on an +// (incomplete) blacklist. func TestShareLowOrderPubkey(t *testing.T) { var fooConn, barConn = makeKVStoreConnPair() + defer fooConn.Close() + defer barConn.Close() locEphPub, _ := genEphKeys() // all blacklisted low order points: @@ -126,6 +130,19 @@ func TestShareLowOrderPubkey(t *testing.T) { } } +// Test that additionally that the Diffie-Hellman shared secret is non-zero. +// The shared secret would be zero for lower order pub-keys (but tested against the blacklist only). +func TestComputeDHFailsOnLowOrder(t *testing.T) { + _, locPrivKey := genEphKeys() + for _, remLowOrderPubKey := range blacklist { + shared, err := computeDHSecret(&remLowOrderPubKey, locPrivKey) + assert.Error(t, err) + + assert.Equal(t, err, ErrSharedSecretIsZero) + assert.Empty(t, shared) + } +} + func TestConcurrentWrite(t *testing.T) { fooSecConn, barSecConn := makeSecretConnPair(t) fooWriteText := cmn.RandStr(dataMaxSize) From 41f91318e9e253a1b59cc1cc128ad412941b8e2a Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 23 Feb 2019 19:32:31 +0400 Subject: [PATCH 069/259] bound mempool memory usage (#3248) * bound mempool memory usage Closes #3079 * rename SizeBytes to TxsTotalBytes and other small fixes after Zarko's review * rename MaxBytes to MaxTxsTotalBytes * make ErrMempoolIsFull more informative * expose mempool's txs_total_bytes via RPC * test full response * fixes after Ethan's review * config: rename mempool.size to mempool.max_txs https://github.com/tendermint/tendermint/pull/3248#discussion_r254034004 * test more cases https://github.com/tendermint/tendermint/pull/3248#discussion_r254036532 * simplify test * Revert "config: rename mempool.size to mempool.max_txs" This reverts commit 39bfa3696177aa46195000b90655419a975d6ff7. * rename count back to n_txs to make a change non-breaking * rename max_txs_total_bytes to max_txs_bytes * format code * fix TestWALPeriodicSync The test was sometimes failing due to processFlushTicks being called too early. The solution is to call wal#Start later in the test. * Apply suggestions from code review --- CHANGELOG_PENDING.md | 3 ++ config/config.go | 23 +++++---- config/toml.go | 9 +++- docs/tendermint-core/configuration.md | 9 +++- mempool/mempool.go | 47 +++++++++++++++--- mempool/mempool_test.go | 70 ++++++++++++++++++++++++++- rpc/client/rpc_test.go | 12 +++-- rpc/core/mempool.go | 45 ++++++++++------- rpc/core/types/responses.go | 6 ++- 9 files changed, 180 insertions(+), 44 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 65fc4bd25..bf8040722 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,6 +18,9 @@ Special thanks to external contributors on this release: * P2P Protocol ### FEATURES: +- [mempool] \#3079 bound mempool memory usage (`mempool.max_txs_bytes` is set to 1GB by default; see config.toml) + mempool's current `txs_total_bytes` is exposed via `total_bytes` field in + `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. ### IMPROVEMENTS: diff --git a/config/config.go b/config/config.go index fc50d7c61..c7f966bf0 100644 --- a/config/config.go +++ b/config/config.go @@ -530,12 +530,13 @@ func DefaultFuzzConnConfig() *FuzzConnConfig { // MempoolConfig defines the configuration options for the Tendermint mempool type MempoolConfig struct { - RootDir string `mapstructure:"home"` - Recheck bool `mapstructure:"recheck"` - Broadcast bool `mapstructure:"broadcast"` - WalPath string `mapstructure:"wal_dir"` - Size int `mapstructure:"size"` - CacheSize int `mapstructure:"cache_size"` + RootDir string `mapstructure:"home"` + Recheck bool `mapstructure:"recheck"` + Broadcast bool `mapstructure:"broadcast"` + WalPath string `mapstructure:"wal_dir"` + Size int `mapstructure:"size"` + MaxTxsBytes int64 `mapstructure:"max_txs_bytes"` + CacheSize int `mapstructure:"cache_size"` } // DefaultMempoolConfig returns a default configuration for the Tendermint mempool @@ -544,10 +545,11 @@ func DefaultMempoolConfig() *MempoolConfig { Recheck: true, Broadcast: true, WalPath: "", - // Each signature verification takes .5ms, size reduced until we implement + // Each signature verification takes .5ms, Size reduced until we implement // ABCI Recheck - Size: 5000, - CacheSize: 10000, + Size: 5000, + MaxTxsBytes: 1024 * 1024 * 1024, // 1GB + CacheSize: 10000, } } @@ -574,6 +576,9 @@ func (cfg *MempoolConfig) ValidateBasic() error { if cfg.Size < 0 { return errors.New("size can't be negative") } + if cfg.MaxTxsBytes < 0 { + return errors.New("max_txs_bytes can't be negative") + } if cfg.CacheSize < 0 { return errors.New("cache_size can't be negative") } diff --git a/config/toml.go b/config/toml.go index b023e4e57..d71bfb519 100644 --- a/config/toml.go +++ b/config/toml.go @@ -237,10 +237,15 @@ recheck = {{ .Mempool.Recheck }} broadcast = {{ .Mempool.Broadcast }} wal_dir = "{{ js .Mempool.WalPath }}" -# size of the mempool +# Maximum number of transactions in the mempool size = {{ .Mempool.Size }} -# size of the cache (used to filter transactions we saw earlier) +# Limit the total size of all txs in the mempool. +# This only accounts for raw transactions (e.g. given 1MB transactions and +# max_txs_bytes=5MB, mempool will only accept 5 transactions). +max_txs_bytes = {{ .Mempool.MaxTxsBytes }} + +# Size of the cache (used to filter transactions we saw earlier) in transactions cache_size = {{ .Mempool.CacheSize }} ##### consensus configuration options ##### diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index 0d9a58c4b..f753d2123 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -183,10 +183,15 @@ recheck = true broadcast = true wal_dir = "" -# size of the mempool +# Maximum number of transactions in the mempool size = 5000 -# size of the cache (used to filter transactions we saw earlier) +# Limit the total size of all txs in the mempool. +# This only accounts for raw transactions (e.g. given 1MB transactions and +# max_txs_bytes=5MB, mempool will only accept 5 transactions). +max_txs_bytes = 1073741824 + +# Size of the cache (used to filter transactions we saw earlier) in transactions cache_size = 10000 ##### consensus configuration options ##### diff --git a/mempool/mempool.go b/mempool/mempool.go index 8550f2f81..41ee59cb4 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -63,13 +63,26 @@ var ( // ErrTxInCache is returned to the client if we saw tx earlier ErrTxInCache = errors.New("Tx already exists in cache") - // ErrMempoolIsFull means Tendermint & an application can't handle that much load - ErrMempoolIsFull = errors.New("Mempool is full") - // ErrTxTooLarge means the tx is too big to be sent in a message to other peers ErrTxTooLarge = fmt.Errorf("Tx too large. Max size is %d", maxTxSize) ) +// ErrMempoolIsFull means Tendermint & an application can't handle that much load +type ErrMempoolIsFull struct { + numTxs int + maxTxs int + + txsBytes int64 + maxTxsBytes int64 +} + +func (e ErrMempoolIsFull) Error() string { + return fmt.Sprintf( + "Mempool is full: number of txs %d (max: %d), total txs bytes %d (max: %d)", + e.numTxs, e.maxTxs, + e.txsBytes, e.maxTxsBytes) +} + // ErrPreCheck is returned when tx is too big type ErrPreCheck struct { Reason error @@ -147,6 +160,9 @@ type Mempool struct { preCheck PreCheckFunc postCheck PostCheckFunc + // Atomic integers + txsBytes int64 // see TxsBytes + // Keep a cache of already-seen txs. // This reduces the pressure on the proxyApp. cache txCache @@ -265,8 +281,13 @@ func (mem *Mempool) Size() int { return mem.txs.Len() } -// Flushes the mempool connection to ensure async resCb calls are done e.g. -// from CheckTx. +// TxsBytes returns the total size of all txs in the mempool. +func (mem *Mempool) TxsBytes() int64 { + return atomic.LoadInt64(&mem.txsBytes) +} + +// FlushAppConn flushes the mempool connection to ensure async resCb calls are +// done e.g. from CheckTx. func (mem *Mempool) FlushAppConn() error { return mem.proxyAppConn.FlushSync() } @@ -282,6 +303,8 @@ func (mem *Mempool) Flush() { mem.txs.Remove(e) e.DetachPrev() } + + _ = atomic.SwapInt64(&mem.txsBytes, 0) } // TxsFront returns the first transaction in the ordered list for peer @@ -308,8 +331,15 @@ func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) { // use defer to unlock mutex because application (*local client*) might panic defer mem.proxyMtx.Unlock() - if mem.Size() >= mem.config.Size { - return ErrMempoolIsFull + var ( + memSize = mem.Size() + txsBytes = mem.TxsBytes() + ) + if memSize >= mem.config.Size || + int64(len(tx))+txsBytes > mem.config.MaxTxsBytes { + return ErrMempoolIsFull{ + memSize, mem.config.Size, + txsBytes, mem.config.MaxTxsBytes} } // The size of the corresponding amino-encoded TxMessage @@ -383,6 +413,7 @@ func (mem *Mempool) resCbNormal(req *abci.Request, res *abci.Response) { tx: tx, } mem.txs.PushBack(memTx) + atomic.AddInt64(&mem.txsBytes, int64(len(tx))) mem.logger.Info("Added good transaction", "tx", TxID(tx), "res", r, @@ -424,6 +455,7 @@ func (mem *Mempool) resCbRecheck(req *abci.Request, res *abci.Response) { // Tx became invalidated due to newly committed block. mem.logger.Info("Tx is no longer valid", "tx", TxID(tx), "res", r, "err", postCheckErr) mem.txs.Remove(mem.recheckCursor) + atomic.AddInt64(&mem.txsBytes, int64(-len(tx))) mem.recheckCursor.DetachPrev() // remove from cache (it might be good later) @@ -597,6 +629,7 @@ func (mem *Mempool) removeTxs(txs types.Txs) []types.Tx { if _, ok := txsMap[string(memTx.tx)]; ok { // remove from clist mem.txs.Remove(e) + atomic.AddInt64(&mem.txsBytes, int64(-len(memTx.tx))) e.DetachPrev() // NOTE: we don't remove committed txs from the cache. diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 5d737e190..5928fbc56 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -30,8 +30,10 @@ import ( type cleanupFunc func() func newMempoolWithApp(cc proxy.ClientCreator) (*Mempool, cleanupFunc) { - config := cfg.ResetTestRoot("mempool_test") + return newMempoolWithAppAndConfig(cc, cfg.ResetTestRoot("mempool_test")) +} +func newMempoolWithAppAndConfig(cc proxy.ClientCreator, config *cfg.Config) (*Mempool, cleanupFunc) { appConnMem, _ := cc.NewABCIClient() appConnMem.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "mempool")) err := appConnMem.Start() @@ -462,6 +464,72 @@ func TestMempoolMaxMsgSize(t *testing.T) { } +func TestMempoolTxsBytes(t *testing.T) { + app := kvstore.NewKVStoreApplication() + cc := proxy.NewLocalClientCreator(app) + config := cfg.ResetTestRoot("mempool_test") + config.Mempool.MaxTxsBytes = 10 + mempool, cleanup := newMempoolWithAppAndConfig(cc, config) + defer cleanup() + + // 1. zero by default + assert.EqualValues(t, 0, mempool.TxsBytes()) + + // 2. len(tx) after CheckTx + err := mempool.CheckTx([]byte{0x01}, nil) + require.NoError(t, err) + assert.EqualValues(t, 1, mempool.TxsBytes()) + + // 3. zero again after tx is removed by Update + mempool.Update(1, []types.Tx{[]byte{0x01}}, nil, nil) + assert.EqualValues(t, 0, mempool.TxsBytes()) + + // 4. zero after Flush + err = mempool.CheckTx([]byte{0x02, 0x03}, nil) + require.NoError(t, err) + assert.EqualValues(t, 2, mempool.TxsBytes()) + + mempool.Flush() + assert.EqualValues(t, 0, mempool.TxsBytes()) + + // 5. ErrMempoolIsFull is returned when/if MaxTxsBytes limit is reached. + err = mempool.CheckTx([]byte{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04}, nil) + require.NoError(t, err) + err = mempool.CheckTx([]byte{0x05}, nil) + if assert.Error(t, err) { + assert.IsType(t, ErrMempoolIsFull{}, err) + } + + // 6. zero after tx is rechecked and removed due to not being valid anymore + app2 := counter.NewCounterApplication(true) + cc = proxy.NewLocalClientCreator(app2) + mempool, cleanup = newMempoolWithApp(cc) + defer cleanup() + + txBytes := make([]byte, 8) + binary.BigEndian.PutUint64(txBytes, uint64(0)) + + err = mempool.CheckTx(txBytes, nil) + require.NoError(t, err) + assert.EqualValues(t, 8, mempool.TxsBytes()) + + appConnCon, _ := cc.NewABCIClient() + appConnCon.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "consensus")) + err = appConnCon.Start() + require.Nil(t, err) + defer appConnCon.Stop() + res, err := appConnCon.DeliverTxSync(txBytes) + require.NoError(t, err) + require.EqualValues(t, 0, res.Code) + res2, err := appConnCon.CommitSync() + require.NoError(t, err) + require.NotEmpty(t, res2.Data) + + // Pretend like we committed nothing so txBytes gets rechecked and removed. + mempool.Update(1, []types.Tx{}, nil, nil) + assert.EqualValues(t, 0, mempool.TxsBytes()) +} + func checksumIt(data []byte) string { h := sha256.New() h.Write(data) diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index d3dc90b85..ba9bc3af7 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -290,9 +290,13 @@ func TestUnconfirmedTxs(t *testing.T) { for i, c := range GetClients() { mc, ok := c.(client.MempoolClient) require.True(t, ok, "%d", i) - txs, err := mc.UnconfirmedTxs(1) + res, err := mc.UnconfirmedTxs(1) require.Nil(t, err, "%d: %+v", i, err) - assert.Exactly(t, types.Txs{tx}, types.Txs(txs.Txs)) + + assert.Equal(t, 1, res.Count) + assert.Equal(t, 1, res.Total) + assert.Equal(t, mempool.TxsBytes(), res.TotalBytes) + assert.Exactly(t, types.Txs{tx}, types.Txs(res.Txs)) } mempool.Flush() @@ -311,7 +315,9 @@ func TestNumUnconfirmedTxs(t *testing.T) { res, err := mc.NumUnconfirmedTxs() require.Nil(t, err, "%d: %+v", i, err) - assert.Equal(t, mempoolSize, res.N) + assert.Equal(t, mempoolSize, res.Count) + assert.Equal(t, mempoolSize, res.Total) + assert.Equal(t, mempool.TxsBytes(), res.TotalBytes) } mempool.Flush() diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 0295bf7f3..d4993074d 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -248,27 +248,32 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // // ```json // { -// "error": "", -// "result": { -// "txs": [], -// "n_txs": "0" -// }, -// "id": "", -// "jsonrpc": "2.0" -// } +// "result" : { +// "txs" : [], +// "total_bytes" : "0", +// "n_txs" : "0", +// "total" : "0" +// }, +// "jsonrpc" : "2.0", +// "id" : "" +// } +// ``` // // ### Query Parameters // // | Parameter | Type | Default | Required | Description | // |-----------+------+---------+----------+--------------------------------------| // | limit | int | 30 | false | Maximum number of entries (max: 100) | -// ``` func UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { // reuse per_page validator limit = validatePerPage(limit) txs := mempool.ReapMaxTxs(limit) - return &ctypes.ResultUnconfirmedTxs{N: len(txs), Txs: txs}, nil + return &ctypes.ResultUnconfirmedTxs{ + Count: len(txs), + Total: mempool.Size(), + TotalBytes: mempool.TxsBytes(), + Txs: txs}, nil } // Get number of unconfirmed transactions. @@ -291,15 +296,19 @@ func UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { // // ```json // { -// "error": "", -// "result": { -// "txs": null, -// "n_txs": "0" -// }, -// "id": "", -// "jsonrpc": "2.0" +// "jsonrpc" : "2.0", +// "id" : "", +// "result" : { +// "n_txs" : "0", +// "total_bytes" : "0", +// "txs" : null, +// "total" : "0" +// } // } // ``` func NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { - return &ctypes.ResultUnconfirmedTxs{N: mempool.Size()}, nil + return &ctypes.ResultUnconfirmedTxs{ + Count: mempool.Size(), + Total: mempool.Size(), + TotalBytes: mempool.TxsBytes()}, nil } diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 85f2746d4..74457b38a 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -178,8 +178,10 @@ type ResultTxSearch struct { // List of mempool txs type ResultUnconfirmedTxs struct { - N int `json:"n_txs"` - Txs []types.Tx `json:"txs"` + Count int `json:"n_txs"` + Total int `json:"total"` + TotalBytes int64 `json:"total_bytes"` + Txs []types.Tx `json:"txs"` } // Info abci msg From cdf3a74f4899b3c716703b932e682307752cd6b6 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 23 Feb 2019 19:48:28 +0400 Subject: [PATCH 070/259] Unclean shutdown on SIGINT / SIGTERM (#3308) * libs/common: TrapSignal accepts logger as a first parameter and does not block anymore * previously it was dumping "captured ..." msg to os.Stdout * TrapSignal should not be responsible for blocking thread of execution Refs #3238 * exit with zero (0) code upon receiving SIGTERM/SIGINT Refs #3238 * fix formatting in docs/app-dev/abci-cli.md Co-Authored-By: melekes * fix formatting in docs/app-dev/abci-cli.md Co-Authored-By: melekes --- CHANGELOG_PENDING.md | 4 +++ abci/cmd/abci-cli/abci-cli.go | 18 ++++++----- cmd/priv_val_server/main.go | 6 +++- cmd/tendermint/commands/lite.go | 12 ++++--- cmd/tendermint/commands/run_node.go | 22 +++++-------- docs/app-dev/abci-cli.md | 16 ++++++---- libs/autofile/cmd/logjack.go | 49 ++++++++++++++++------------- libs/common/os.go | 15 +++++---- libs/common/os_test.go | 4 +-- rpc/lib/test/main.go | 18 ++++++----- tools/tm-bench/main.go | 19 ++++------- tools/tm-monitor/main.go | 6 +++- 12 files changed, 103 insertions(+), 86 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index bf8040722..f8614606d 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -12,6 +12,9 @@ Special thanks to external contributors on this release: * Apps * Go API +- [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore + * previously it was dumping "captured ..." msg to os.Stdout + * TrapSignal should not be responsible for blocking thread of execution * Blockchain Protocol @@ -23,6 +26,7 @@ Special thanks to external contributors on this release: `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. ### IMPROVEMENTS: +- [libs/common] \#3238 exit with zero (0) code upon receiving SIGTERM/SIGINT ### BUG FIXES: diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index 8823f7be8..7e55569cb 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -636,9 +636,7 @@ func cmdQuery(cmd *cobra.Command, args []string) error { } func cmdCounter(cmd *cobra.Command, args []string) error { - app := counter.NewCounterApplication(flagSerial) - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) // Start the listener @@ -651,12 +649,14 @@ func cmdCounter(cmd *cobra.Command, args []string) error { return err } - // Wait forever - cmn.TrapSignal(func() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { // Cleanup srv.Stop() }) - return nil + + // Run forever. + select {} } func cmdKVStore(cmd *cobra.Command, args []string) error { @@ -681,12 +681,14 @@ func cmdKVStore(cmd *cobra.Command, args []string) error { return err } - // Wait forever - cmn.TrapSignal(func() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { // Cleanup srv.Stop() }) - return nil + + // Run forever. + select {} } //-------------------------------------------------------------------------------- diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 768b9cf63..6d5406924 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -54,10 +54,14 @@ func main() { panic(err) } - cmn.TrapSignal(func() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { err := rs.Stop() if err != nil { panic(err) } }) + + // Run forever. + select {} } diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index eb2817b60..94259cb0b 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -59,6 +59,11 @@ func ensureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) { } func runProxy(cmd *cobra.Command, args []string) error { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { + // TODO: close up shop + }) + nodeAddr, err := ensureAddrHasSchemeOrDefaultToTCP(nodeAddr) if err != nil { return err @@ -86,9 +91,6 @@ func runProxy(cmd *cobra.Command, args []string) error { return cmn.ErrorWrap(err, "starting proxy") } - cmn.TrapSignal(func() { - // TODO: close up shop - }) - - return nil + // Run forever + select {} } diff --git a/cmd/tendermint/commands/run_node.go b/cmd/tendermint/commands/run_node.go index c720de7d8..fa63b4944 100644 --- a/cmd/tendermint/commands/run_node.go +++ b/cmd/tendermint/commands/run_node.go @@ -2,12 +2,10 @@ package commands import ( "fmt" - "os" - "os/signal" - "syscall" "github.com/spf13/cobra" + cmn "github.com/tendermint/tendermint/libs/common" nm "github.com/tendermint/tendermint/node" ) @@ -57,25 +55,19 @@ func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command { return fmt.Errorf("Failed to create node: %v", err) } - // Stop upon receiving SIGTERM or CTRL-C - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - go func() { - for sig := range c { - logger.Error(fmt.Sprintf("captured %v, exiting...", sig)) - if n.IsRunning() { - n.Stop() - } - os.Exit(1) + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { + if n.IsRunning() { + n.Stop() } - }() + }) if err := n.Start(); err != nil { return fmt.Errorf("Failed to start node: %v", err) } logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo()) - // Run forever + // Run forever. select {} }, } diff --git a/docs/app-dev/abci-cli.md b/docs/app-dev/abci-cli.md index ba6f05897..3e6cced87 100644 --- a/docs/app-dev/abci-cli.md +++ b/docs/app-dev/abci-cli.md @@ -89,12 +89,14 @@ func cmdKVStore(cmd *cobra.Command, args []string) error { return err } - // Wait forever - cmn.TrapSignal(func() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { // Cleanup srv.Stop() }) - return nil + + // Run forever. + select {} } ``` @@ -238,12 +240,14 @@ func cmdCounter(cmd *cobra.Command, args []string) error { return err } - // Wait forever - cmn.TrapSignal(func() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { // Cleanup srv.Stop() }) - return nil + + // Run forever. + select {} } ``` diff --git a/libs/autofile/cmd/logjack.go b/libs/autofile/cmd/logjack.go index ead3f8305..e1bd5167d 100644 --- a/libs/autofile/cmd/logjack.go +++ b/libs/autofile/cmd/logjack.go @@ -29,7 +29,21 @@ func parseFlags() (headPath string, chopSize int64, limitSize int64, version boo return } +type fmtLogger struct{} + +func (fmtLogger) Info(msg string, keyvals ...interface{}) { + strs := make([]string, len(keyvals)) + for i, kv := range keyvals { + strs[i] = fmt.Sprintf("%v", kv) + } + fmt.Printf("%s %s\n", msg, strings.Join(strs, ",")) +} + func main() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(fmtLogger{}, func() { + fmt.Println("logjack shutting down") + }) // Read options headPath, chopSize, limitSize, version := parseFlags() @@ -51,29 +65,22 @@ func main() { os.Exit(1) } - go func() { - // Forever, read from stdin and write to AutoFile. - buf := make([]byte, readBufferSize) - for { - n, err := os.Stdin.Read(buf) - group.Write(buf[:n]) - group.Flush() - if err != nil { - group.Stop() - if err == io.EOF { - os.Exit(0) - } else { - fmt.Println("logjack errored") - os.Exit(1) - } + // Forever read from stdin and write to AutoFile. + buf := make([]byte, readBufferSize) + for { + n, err := os.Stdin.Read(buf) + group.Write(buf[:n]) + group.Flush() + if err != nil { + group.Stop() + if err == io.EOF { + os.Exit(0) + } else { + fmt.Println("logjack errored") + os.Exit(1) } } - }() - - // Trap signal - cmn.TrapSignal(func() { - fmt.Println("logjack shutting down") - }) + } } func parseBytesize(chopSize string) int64 { diff --git a/libs/common/os.go b/libs/common/os.go index 501bb5640..7c3fad7ee 100644 --- a/libs/common/os.go +++ b/libs/common/os.go @@ -34,21 +34,24 @@ func GoPath() string { return path } -// TrapSignal catches the SIGTERM and executes cb function. After that it exits -// with code 1. -func TrapSignal(cb func()) { +type logger interface { + Info(msg string, keyvals ...interface{}) +} + +// TrapSignal catches the SIGTERM/SIGINT and executes cb function. After that it exits +// with code 0. +func TrapSignal(logger logger, cb func()) { c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt, syscall.SIGTERM) go func() { for sig := range c { - fmt.Printf("captured %v, exiting...\n", sig) + logger.Info(fmt.Sprintf("captured %v, exiting...", sig)) if cb != nil { cb() } - os.Exit(1) + os.Exit(0) } }() - select {} } // Kill the running process by sending itself SIGTERM. diff --git a/libs/common/os_test.go b/libs/common/os_test.go index bf65f0c99..e8a23ebd6 100644 --- a/libs/common/os_test.go +++ b/libs/common/os_test.go @@ -5,7 +5,7 @@ import ( "testing" ) -func TestGoPath(t *testing.T) { +func TestOSGoPath(t *testing.T) { // restore original gopath upon exit path := os.Getenv("GOPATH") defer func() { @@ -28,7 +28,7 @@ func TestGoPath(t *testing.T) { } } -func TestGoPathWithoutEnvVar(t *testing.T) { +func TestOSGoPathWithoutEnvVar(t *testing.T) { // restore original gopath upon exit path := os.Getenv("GOPATH") defer func() { diff --git a/rpc/lib/test/main.go b/rpc/lib/test/main.go index 0a9684d76..b2f94580a 100644 --- a/rpc/lib/test/main.go +++ b/rpc/lib/test/main.go @@ -24,17 +24,19 @@ type Result struct { } func main() { - mux := http.NewServeMux() - cdc := amino.NewCodec() - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + var ( + mux = http.NewServeMux() + cdc = amino.NewCodec() + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + ) + + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() {}) + rpcserver.RegisterRPCFuncs(mux, routes, cdc, logger) listener, err := rpcserver.Listen("0.0.0.0:8008", rpcserver.Config{}) if err != nil { cmn.Exit(err.Error()) } - go rpcserver.StartHTTPServer(listener, mux, logger) - // Wait forever - cmn.TrapSignal(func() { - }) - + rpcserver.StartHTTPServer(listener, mux, logger) } diff --git a/tools/tm-bench/main.go b/tools/tm-bench/main.go index 87f12ef34..432ebc8f4 100644 --- a/tools/tm-bench/main.go +++ b/tools/tm-bench/main.go @@ -4,14 +4,13 @@ import ( "flag" "fmt" "os" - "os/signal" "strings" "sync" - "syscall" "time" "github.com/go-kit/kit/log/term" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" tmrpc "github.com/tendermint/tendermint/rpc/client" ) @@ -94,18 +93,12 @@ Examples: "broadcast_tx_"+broadcastTxMethod, ) - // Quit when interrupted or received SIGTERM. - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt, syscall.SIGTERM) - go func() { - for sig := range c { - fmt.Printf("captured %v, exiting...\n", sig) - for _, t := range transacters { - t.Stop() - } - os.Exit(1) + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { + for _, t := range transacters { + t.Stop() } - }() + }) // Wait until transacters have begun until we get the start time. timeStart := time.Now() diff --git a/tools/tm-monitor/main.go b/tools/tm-monitor/main.go index 6e4aea5f9..bcabcf73e 100644 --- a/tools/tm-monitor/main.go +++ b/tools/tm-monitor/main.go @@ -58,13 +58,17 @@ Examples: ton.Start() } - cmn.TrapSignal(func() { + // Stop upon receiving SIGTERM or CTRL-C. + cmn.TrapSignal(logger, func() { if !noton { ton.Stop() } monitor.Stop() listener.Close() }) + + // Run forever. + select {} } func startMonitor(endpoints string) *monitor.Monitor { From 6797d8585167a9dc1e51843420ae4ab7eaf848a0 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Mon, 25 Feb 2019 06:06:21 +0100 Subject: [PATCH 071/259] p2p: fix comment in secret connection (#3348) Just a minor followup on the review if #3347: Fixes a comment. [#3347 (comment)](https://github.com/tendermint/tendermint/pull/3347#discussion_r259582330) --- p2p/conn/secret_connection.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index 2a06945b0..36d6ee1bb 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -358,8 +358,8 @@ func deriveSecretAndChallenge(dhSecret *[32]byte, locIsLeast bool) (recvSecret, return } -// computeDHSecret computes a shared secret Diffie-Hellman secret -// from a the own local private key and the others public key. +// computeDHSecret computes a Diffie-Hellman shared secret key +// from our own local private key and the other's public key. // // It returns an error if the computed shared secret is all zeroes. func computeDHSecret(remPubKey, locPrivKey *[32]byte) (shrKey *[32]byte, err error) { From ec9bff52346d9629f690147674326a41fcefd20f Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 25 Feb 2019 09:11:07 +0400 Subject: [PATCH 072/259] rename WAL#Flush to WAL#FlushAndSync (#3345) * rename WAL#Flush to WAL#FlushAndSync - rename auto#Flush to auto#FlushAndSync - cleanup WAL interface to not leak implementation details! * remove Group() * add WALReader interface and return it in SearchForEndHeight() - add interface assertions Refs #3337 * replace WALReader with io.ReadCloser --- consensus/replay_test.go | 9 ++++---- consensus/state.go | 4 ++-- consensus/wal.go | 41 ++++++++++++++++++++++------------- consensus/wal_generator.go | 9 +++----- consensus/wal_test.go | 13 ++++++----- libs/autofile/cmd/logjack.go | 2 +- libs/autofile/group.go | 18 +++++++++------- libs/autofile/group_test.go | 42 ++++++++++++++++++------------------ 8 files changed, 76 insertions(+), 62 deletions(-) diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 74a6e0fbe..86dca7657 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -19,7 +19,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" - auto "github.com/tendermint/tendermint/libs/autofile" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" @@ -201,6 +200,8 @@ type crashingWAL struct { lastPanickedForMsgIndex int // last message for which we panicked } +var _ WAL = &crashingWAL{} + // WALWriteError indicates a WAL crash. type WALWriteError struct { msg string @@ -248,15 +249,15 @@ func (w *crashingWAL) WriteSync(m WALMessage) { w.Write(m) } -func (w *crashingWAL) Group() *auto.Group { return w.next.Group() } -func (w *crashingWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) { +func (w *crashingWAL) FlushAndSync() error { return w.next.FlushAndSync() } + +func (w *crashingWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { return w.next.SearchForEndHeight(height, options) } func (w *crashingWAL) Start() error { return w.next.Start() } func (w *crashingWAL) Stop() error { return w.next.Stop() } func (w *crashingWAL) Wait() { w.next.Wait() } -func (w *crashingWAL) Flush() error { return w.Group().Flush() } //------------------------------------------------------------------------------------------ // Handshake Tests diff --git a/consensus/state.go b/consensus/state.go index 8ae17ea6c..865cd553f 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -910,7 +910,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) { } // Flush the WAL. Otherwise, we may not recompute the same proposal to sign, and the privValidator will refuse to sign anything. - cs.wal.Flush() + cs.wal.FlushAndSync() // Make proposal propBlockId := types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()} @@ -1678,7 +1678,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool, func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) { // Flush the WAL. Otherwise, we may not recompute the same vote to sign, and the privValidator will refuse to sign anything. - cs.wal.Flush() + cs.wal.FlushAndSync() addr := cs.privValidator.GetPubKey().Address() valIndex, _ := cs.Validators.GetByAddress(addr) diff --git a/consensus/wal.go b/consensus/wal.go index 2d544857d..c63c6b940 100644 --- a/consensus/wal.go +++ b/consensus/wal.go @@ -57,10 +57,11 @@ func RegisterWALMessages(cdc *amino.Codec) { type WAL interface { Write(WALMessage) WriteSync(WALMessage) - Group() *auto.Group - SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) - Flush() error + FlushAndSync() error + SearchForEndHeight(height int64, options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) + + // service methods Start() error Stop() error Wait() @@ -82,6 +83,8 @@ type baseWAL struct { flushInterval time.Duration } +var _ WAL = &baseWAL{} + // NewWAL returns a new write-ahead logger based on `baseWAL`, which implements // WAL. It's flushed and synced to disk every 2s and once when stopped. func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) { @@ -125,16 +128,19 @@ func (wal *baseWAL) OnStart() error { wal.WriteSync(EndHeightMessage{0}) } err = wal.group.Start() + if err != nil { + return err + } wal.flushTicker = time.NewTicker(wal.flushInterval) go wal.processFlushTicks() - return err + return nil } func (wal *baseWAL) processFlushTicks() { for { select { case <-wal.flushTicker.C: - if err := wal.Flush(); err != nil { + if err := wal.FlushAndSync(); err != nil { wal.Logger.Error("Periodic WAL flush failed", "err", err) } case <-wal.Quit(): @@ -143,9 +149,10 @@ func (wal *baseWAL) processFlushTicks() { } } -// Flush will attempt to flush and fsync the underlying group's data to disk. -func (wal *baseWAL) Flush() error { - return wal.group.Flush() +// FlushAndSync flushes and fsync's the underlying group's data to disk. +// See auto#FlushAndSync +func (wal *baseWAL) FlushAndSync() error { + return wal.group.FlushAndSync() } // Stop the underlying autofile group. @@ -153,7 +160,7 @@ func (wal *baseWAL) Flush() error { // before cleaning up files. func (wal *baseWAL) OnStop() { wal.flushTicker.Stop() - wal.Flush() + wal.FlushAndSync() wal.group.Stop() wal.group.Close() } @@ -187,7 +194,7 @@ func (wal *baseWAL) WriteSync(msg WALMessage) { } wal.Write(msg) - if err := wal.Flush(); err != nil { + if err := wal.FlushAndSync(); err != nil { panic(fmt.Sprintf("Error flushing consensus wal buf to file. Error: %v \n", err)) } } @@ -203,8 +210,11 @@ type WALSearchOptions struct { // 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 +func (wal *baseWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { + var ( + msg *TimedWALMessage + gr *auto.GroupReader + ) lastHeightFound := int64(-1) // NOTE: starting from the last file in the group because we're usually @@ -371,13 +381,14 @@ func (dec *WALDecoder) Decode() (*TimedWALMessage, error) { type nilWAL struct{} +var _ WAL = nilWAL{} + func (nilWAL) Write(m WALMessage) {} func (nilWAL) WriteSync(m WALMessage) {} -func (nilWAL) Group() *auto.Group { return nil } -func (nilWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) { +func (nilWAL) FlushAndSync() error { return nil } +func (nilWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { return nil, false, nil } func (nilWAL) Start() error { return nil } func (nilWAL) Stop() error { return nil } func (nilWAL) Wait() {} -func (nilWAL) Flush() error { return nil } diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 9375d5dcc..1a4cfb9ff 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -13,7 +13,6 @@ import ( "github.com/tendermint/tendermint/abci/example/kvstore" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" - auto "github.com/tendermint/tendermint/libs/autofile" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -192,14 +191,12 @@ func (w *byteBufferWAL) WriteSync(m WALMessage) { w.Write(m) } -func (w *byteBufferWAL) Group() *auto.Group { - panic("not implemented") -} -func (w *byteBufferWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (gr *auto.GroupReader, found bool, err error) { +func (w *byteBufferWAL) FlushAndSync() error { return nil } + +func (w *byteBufferWAL) SearchForEndHeight(height int64, options *WALSearchOptions) (rd io.ReadCloser, found bool, err error) { return nil, false, nil } func (w *byteBufferWAL) Start() error { return nil } func (w *byteBufferWAL) Stop() error { return nil } func (w *byteBufferWAL) Wait() {} -func (w *byteBufferWAL) Flush() error { return nil } diff --git a/consensus/wal_test.go b/consensus/wal_test.go index 0bb750567..5cb73fb7f 100644 --- a/consensus/wal_test.go +++ b/consensus/wal_test.go @@ -32,8 +32,10 @@ func TestWALTruncate(t *testing.T) { walFile := filepath.Join(walDir, "wal") - //this magic number 4K can truncate the content when RotateFile. defaultHeadSizeLimit(10M) is hard to simulate. - //this magic number 1 * time.Millisecond make RotateFile check frequently. defaultGroupCheckDuration(5s) is hard to simulate. + // this magic number 4K can truncate the content when RotateFile. + // defaultHeadSizeLimit(10M) is hard to simulate. + // this magic number 1 * time.Millisecond make RotateFile check frequently. + // defaultGroupCheckDuration(5s) is hard to simulate. wal, err := NewWAL(walFile, autofile.GroupHeadSizeLimit(4096), autofile.GroupCheckDuration(1*time.Millisecond), @@ -49,14 +51,15 @@ func TestWALTruncate(t *testing.T) { wal.Wait() }() - //60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), when headBuf is full, truncate content will Flush to the file. - //at this time, RotateFile is called, truncate content exist in each file. + // 60 block's size nearly 70K, greater than group's headBuf size(4096 * 10), + // when headBuf is full, truncate content will Flush to the file. at this + // time, RotateFile is called, truncate content exist in each file. err = WALGenerateNBlocks(t, wal.Group(), 60) require.NoError(t, err) time.Sleep(1 * time.Millisecond) //wait groupCheckDuration, make sure RotateFile run - wal.Group().Flush() + wal.FlushAndSync() h := int64(50) gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{}) diff --git a/libs/autofile/cmd/logjack.go b/libs/autofile/cmd/logjack.go index e1bd5167d..20002eff3 100644 --- a/libs/autofile/cmd/logjack.go +++ b/libs/autofile/cmd/logjack.go @@ -70,7 +70,7 @@ func main() { for { n, err := os.Stdin.Read(buf) group.Write(buf[:n]) - group.Flush() + group.FlushAndSync() if err != nil { group.Stop() if err == io.EOF { diff --git a/libs/autofile/group.go b/libs/autofile/group.go index cafbb6d7e..d1ea0de75 100644 --- a/libs/autofile/group.go +++ b/libs/autofile/group.go @@ -131,21 +131,23 @@ func GroupTotalSizeLimit(limit int64) func(*Group) { } } -// OnStart implements Service by starting the goroutine that checks file and -// group limits. +// OnStart implements cmn.Service by starting the goroutine that checks file +// and group limits. func (g *Group) OnStart() error { g.ticker = time.NewTicker(g.groupCheckDuration) go g.processTicks() return nil } -// OnStop implements Service by stopping the goroutine described above. +// OnStop implements cmn.Service by stopping the goroutine described above. // NOTE: g.Head must be closed separately using Close. func (g *Group) OnStop() { g.ticker.Stop() - g.Flush() // flush any uncommitted data + g.FlushAndSync() } +// Wait blocks until all internal goroutines are finished. Supposed to be +// called after Stop. func (g *Group) Wait() { // wait for processTicks routine to finish <-g.doneProcessTicks @@ -153,7 +155,7 @@ func (g *Group) Wait() { // Close closes the head file. The group must be stopped by this moment. func (g *Group) Close() { - g.Flush() // flush any uncommitted data + g.FlushAndSync() g.mtx.Lock() _ = g.Head.closeFile() @@ -216,9 +218,9 @@ func (g *Group) Buffered() int { return g.headBuf.Buffered() } -// Flush writes any buffered data to the underlying file and commits the -// current content of the file to stable storage. -func (g *Group) Flush() error { +// FlushAndSync writes any buffered data to the underlying file and commits the +// current content of the file to stable storage (fsync). +func (g *Group) FlushAndSync() error { g.mtx.Lock() defer g.mtx.Unlock() err := g.headBuf.Flush() diff --git a/libs/autofile/group_test.go b/libs/autofile/group_test.go index e173e4996..68870df87 100644 --- a/libs/autofile/group_test.go +++ b/libs/autofile/group_test.go @@ -55,7 +55,7 @@ func TestCheckHeadSizeLimit(t *testing.T) { err := g.WriteLine(cmn.RandStr(999)) require.NoError(t, err, "Error appending to head") } - g.Flush() + g.FlushAndSync() assertGroupInfo(t, g.ReadGroupInfo(), 0, 0, 999000, 999000) // Even calling checkHeadSizeLimit manually won't rotate it. @@ -65,7 +65,7 @@ func TestCheckHeadSizeLimit(t *testing.T) { // Write 1000 more bytes. err := g.WriteLine(cmn.RandStr(999)) require.NoError(t, err, "Error appending to head") - g.Flush() + g.FlushAndSync() // Calling checkHeadSizeLimit this time rolls it. g.checkHeadSizeLimit() @@ -74,7 +74,7 @@ func TestCheckHeadSizeLimit(t *testing.T) { // Write 1000 more bytes. err = g.WriteLine(cmn.RandStr(999)) require.NoError(t, err, "Error appending to head") - g.Flush() + g.FlushAndSync() // Calling checkHeadSizeLimit does nothing. g.checkHeadSizeLimit() @@ -85,7 +85,7 @@ func TestCheckHeadSizeLimit(t *testing.T) { err = g.WriteLine(cmn.RandStr(999)) require.NoError(t, err, "Error appending to head") } - g.Flush() + g.FlushAndSync() assertGroupInfo(t, g.ReadGroupInfo(), 0, 1, 2000000, 1000000) // Calling checkHeadSizeLimit rolls it again. @@ -95,7 +95,7 @@ func TestCheckHeadSizeLimit(t *testing.T) { // Write 1000 more bytes. _, err = g.Head.Write([]byte(cmn.RandStr(999) + "\n")) require.NoError(t, err, "Error appending to head") - g.Flush() + g.FlushAndSync() assertGroupInfo(t, g.ReadGroupInfo(), 0, 2, 2001000, 1000) // Calling checkHeadSizeLimit does nothing. @@ -212,12 +212,12 @@ func TestRotateFile(t *testing.T) { g.WriteLine("Line 1") g.WriteLine("Line 2") g.WriteLine("Line 3") - g.Flush() + g.FlushAndSync() g.RotateFile() g.WriteLine("Line 4") g.WriteLine("Line 5") g.WriteLine("Line 6") - g.Flush() + g.FlushAndSync() // Read g.Head.Path+"000" body1, err := ioutil.ReadFile(g.Head.Path + ".000") @@ -244,13 +244,13 @@ func TestFindLast1(t *testing.T) { g.WriteLine("Line 2") g.WriteLine("# a") g.WriteLine("Line 3") - g.Flush() + g.FlushAndSync() g.RotateFile() g.WriteLine("Line 4") g.WriteLine("Line 5") g.WriteLine("Line 6") g.WriteLine("# b") - g.Flush() + g.FlushAndSync() match, found, err := g.FindLast("#") assert.NoError(t, err) @@ -267,14 +267,14 @@ func TestFindLast2(t *testing.T) { g.WriteLine("Line 1") g.WriteLine("Line 2") g.WriteLine("Line 3") - g.Flush() + g.FlushAndSync() g.RotateFile() g.WriteLine("# a") g.WriteLine("Line 4") g.WriteLine("Line 5") g.WriteLine("# b") g.WriteLine("Line 6") - g.Flush() + g.FlushAndSync() match, found, err := g.FindLast("#") assert.NoError(t, err) @@ -293,12 +293,12 @@ func TestFindLast3(t *testing.T) { g.WriteLine("Line 2") g.WriteLine("# b") g.WriteLine("Line 3") - g.Flush() + g.FlushAndSync() g.RotateFile() g.WriteLine("Line 4") g.WriteLine("Line 5") g.WriteLine("Line 6") - g.Flush() + g.FlushAndSync() match, found, err := g.FindLast("#") assert.NoError(t, err) @@ -315,12 +315,12 @@ func TestFindLast4(t *testing.T) { g.WriteLine("Line 1") g.WriteLine("Line 2") g.WriteLine("Line 3") - g.Flush() + g.FlushAndSync() g.RotateFile() g.WriteLine("Line 4") g.WriteLine("Line 5") g.WriteLine("Line 6") - g.Flush() + g.FlushAndSync() match, found, err := g.FindLast("#") assert.NoError(t, err) @@ -336,7 +336,7 @@ func TestWrite(t *testing.T) { written := []byte("Medusa") g.Write(written) - g.Flush() + g.FlushAndSync() read := make([]byte, len(written)) gr, err := g.NewReader(0) @@ -357,11 +357,11 @@ func TestGroupReaderRead(t *testing.T) { professor := []byte("Professor Monster") g.Write(professor) - g.Flush() + g.FlushAndSync() g.RotateFile() frankenstein := []byte("Frankenstein's Monster") g.Write(frankenstein) - g.Flush() + g.FlushAndSync() totalWrittenLength := len(professor) + len(frankenstein) read := make([]byte, totalWrittenLength) @@ -386,12 +386,12 @@ func TestGroupReaderRead2(t *testing.T) { professor := []byte("Professor Monster") g.Write(professor) - g.Flush() + g.FlushAndSync() g.RotateFile() frankenstein := []byte("Frankenstein's Monster") frankensteinPart := []byte("Frankenstein") g.Write(frankensteinPart) // note writing only a part - g.Flush() + g.FlushAndSync() totalLength := len(professor) + len(frankenstein) read := make([]byte, totalLength) @@ -427,7 +427,7 @@ func TestMaxIndex(t *testing.T) { assert.Zero(t, g.MaxIndex(), "MaxIndex should be zero at the beginning") g.WriteLine("Line 1") - g.Flush() + g.FlushAndSync() g.RotateFile() assert.Equal(t, 1, g.MaxIndex(), "MaxIndex should point to the last file") From d6e2fb453d20841828cfbe1f6a7e8ca593c1b564 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 28 Feb 2019 11:31:59 +0400 Subject: [PATCH 073/259] update docs (#3349) * docs: explain create_empty_blocks configurations Closes #3307 * Vagrantfile: install nodejs for docs * update docs instructions npm install does not make sense since there's no packages.json file * explain broadcast_tx_* tx format Closes #536 * docs: explain how transaction ordering works Closes #2904 * bring in consensus parameters explained * example for create_empty_blocks_interval * bring in explanation from https://github.com/tendermint/tendermint/issues/2487#issuecomment-424899799 * link to formatting instead of duplicating info --- Vagrantfile | 4 ++ docs/.vuepress/config.js | 3 +- docs/DOCS_README.md | 5 +- docs/spec/reactors/mempool/functionality.md | 2 +- docs/tendermint-core/configuration.md | 71 +++++++++++++++++++++ docs/tendermint-core/mempool.md | 41 ++++++++++++ rpc/core/mempool.go | 21 +++++- 7 files changed, 141 insertions(+), 6 deletions(-) create mode 100644 docs/tendermint-core/mempool.md diff --git a/Vagrantfile b/Vagrantfile index 320f3b1c3..515a48791 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -34,6 +34,10 @@ Vagrant.configure("2") do |config| mv go /usr/local rm -f go1.11.linux-amd64.tar.gz + # install nodejs (for docs) + curl -sL https://deb.nodesource.com/setup_11.x | bash - + apt-get install -y nodejs + # cleanup apt-get autoremove -y diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index fb3620e34..0b54d2011 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -63,7 +63,8 @@ module.exports = { "/tendermint-core/light-client-protocol", "/tendermint-core/metrics", "/tendermint-core/secure-p2p", - "/tendermint-core/validators" + "/tendermint-core/validators", + "/tendermint-core/mempool" ] }, { diff --git a/docs/DOCS_README.md b/docs/DOCS_README.md index c91d03911..49c2030a2 100644 --- a/docs/DOCS_README.md +++ b/docs/DOCS_README.md @@ -66,11 +66,12 @@ To build and serve the documentation locally, run: ``` # from this directory -npm install npm install -g vuepress ``` -then change the following line in the `config.js`: +NOTE: the command may require `sudo`. + +then change the following line in the `.vuepress/config.js`: ``` base: "/docs/", diff --git a/docs/spec/reactors/mempool/functionality.md b/docs/spec/reactors/mempool/functionality.md index 4064def0b..ea902225d 100644 --- a/docs/spec/reactors/mempool/functionality.md +++ b/docs/spec/reactors/mempool/functionality.md @@ -40,4 +40,4 @@ However, we only store valid txs in the cache, not invalid ones. This is because invalid txs could become good later. Txs that are included in a block aren't removed from the cache, as they still may be getting received over the p2p network. -These txs are stored in the cache by their hash, to mitigate memory concerns. \ No newline at end of file +These txs are stored in the cache by their hash, to mitigate memory concerns. diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index f753d2123..4e188aae7 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -268,3 +268,74 @@ max_open_connections = 3 # Instrumentation namespace namespace = "tendermint" ``` + +## Empty blocks VS no empty blocks + +**create_empty_blocks = true** + +If `create_empty_blocks` is set to `true` in your config, blocks will be +created ~ every second (with default consensus parameters). You can regulate +the delay between blocks by changing the `timeout_commit`. E.g. `timeout_commit += "10s"` should result in ~ 10 second blocks. + +**create_empty_blocks = false** + +In this setting, blocks are created when transactions received. + +Note after the block H, Tendermint creates something we call a "proof block" +(only if the application hash changed) H+1. The reason for this is to support +proofs. If you have a transaction in block H that changes the state to X, the +new application hash will only be included in block H+1. If after your +transaction is committed, you want to get a lite-client proof for the new state +(X), you need the new block to be committed in order to do that because the new +block has the new application hash for the state X. That's why we make a new +(empty) block if the application hash changes. Otherwise, you won't be able to +make a proof for the new state. + +Plus, if you set `create_empty_blocks_interval` to something other than the +default (`0`), Tendermint will be creating empty blocks even in the absence of +transactions every `create_empty_blocks_interval`. For instance, with +`create_empty_blocks = false` and `create_empty_blocks_interval = "30s"`, +Tendermint will only create blocks if there are transactions, or after waiting +30 seconds without receiving any transactions. + +## Consensus timeouts explained + +There's a variety of information about timeouts in [Running in +production](./running-in-production.html) + +You can also find more detailed technical explanation in the spec: [The latest +gossip on BFT consensus](https://arxiv.org/abs/1807.04938). + +``` +[consensus] +... + +timeout_propose = "3s" +timeout_propose_delta = "500ms" +timeout_prevote = "1s" +timeout_prevote_delta = "500ms" +timeout_precommit = "1s" +timeout_precommit_delta = "500ms" +timeout_commit = "1s" +``` + +Note that in a successful round, the only timeout that we absolutely wait no +matter what is `timeout_commit`. + +Here's a brief summary of the timeouts: + +- `timeout_propose` = how long we wait for a proposal block before prevoting + nil +- `timeout_propose_delta` = how much timeout_propose increases with each round +- `timeout_prevote` = how long we wait after receiving +2/3 prevotes for + anything (ie. not a single block or nil) +- `timeout_prevote_delta` = how much the timeout_prevote increases with each + round +- `timeout_precommit` = how long we wait after receiving +2/3 precommits for + anything (ie. not a single block or nil) +- `timeout_precommit_delta` = how much the timeout_precommit increases with + each round +- `timeout_commit` = how long we wait after committing a block, before starting + on the new height (this gives us a chance to receive some more precommits, + even though we already have +2/3) diff --git a/docs/tendermint-core/mempool.md b/docs/tendermint-core/mempool.md new file mode 100644 index 000000000..883853ace --- /dev/null +++ b/docs/tendermint-core/mempool.md @@ -0,0 +1,41 @@ +# Mempool + +## Transaction ordering + +Currently, there's no ordering of transactions other than the order they've +arrived (via RPC or from other nodes). + +So the only way to specify the order is to send them to a single node. + +valA: + - tx1 + - tx2 + - tx3 + +If the transactions are split up across different nodes, there's no way to +ensure they are processed in the expected order. + +valA: + - tx1 + - tx2 + +valB: + - tx3 + +If valB is the proposer, the order might be: + + - tx3 + - tx1 + - tx2 + +If valA is the proposer, the order might be: + + - tx1 + - tx2 + - tx3 + +That said, if the transactions contain some internal value, like an +order/nonce/sequence number, the application can reject transactions that are +out of order. So if a node receives tx3, then tx1, it can reject tx3 and then +accept tx1. The sender can then retry sending tx3, which should probably be +rejected until the node has seen tx2. diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index d4993074d..42aa56afd 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -16,7 +16,13 @@ import ( //----------------------------------------------------------------------------- // NOTE: tx should be signed, but this is only checked at the app level (not by Tendermint!) -// Returns right away, with no response +// Returns right away, with no response. Does not wait for CheckTx nor +// DeliverTx results. +// +// Please refer to +// https://tendermint.com/docs/tendermint-core/using-tendermint.html#formatting +// for formatting/encoding rules. +// // // ```shell // curl 'localhost:26657/broadcast_tx_async?tx="123"' @@ -61,7 +67,11 @@ func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { return &ctypes.ResultBroadcastTx{Hash: tx.Hash()}, nil } -// Returns with the response from CheckTx. +// Returns with the response from CheckTx. Does not wait for DeliverTx result. +// +// Please refer to +// https://tendermint.com/docs/tendermint-core/using-tendermint.html#formatting +// for formatting/encoding rules. // // ```shell // curl 'localhost:26657/broadcast_tx_sync?tx="456"' @@ -116,12 +126,19 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { }, nil } +// Returns with the responses from CheckTx and DeliverTx. +// // CONTRACT: only returns error if mempool.CheckTx() errs or if we timeout // waiting for tx to commit. // // If CheckTx or DeliverTx fail, no error will be returned, but the returned result // will contain a non-OK ABCI code. // +// Please refer to +// https://tendermint.com/docs/tendermint-core/using-tendermint.html#formatting +// for formatting/encoding rules. +// +// // ```shell // curl 'localhost:26657/broadcast_tx_commit?tx="789"' // ``` From 853dd34d31bee6de593be94df87cbb508688249e Mon Sep 17 00:00:00 2001 From: Juan Leni Date: Thu, 28 Feb 2019 08:48:20 +0100 Subject: [PATCH 074/259] privval: improve Remote Signer implementation (#3351) This issue is related to #3107 This is a first renaming/refactoring step before reworking and removing heartbeats. As discussed with @Liamsi , we preferred to go for a couple of independent and separate PRs to simplify review work. The changes: Help to clarify the relation between the validator and remote signer endpoints Differentiate between timeouts and deadlines Prepare to encapsulate networking related code behind RemoteSigner in the next PR My intention is to separate and encapsulate the "network related" code from the actual signer. SignerRemote ---(uses/contains)--> SignerValidatorEndpoint <--(connects to)--> SignerServiceEndpoint ---> SignerService (future.. not here yet but would like to decouple too) All reconnection/heartbeat/whatever code goes in the endpoints. Signer[Remote/Service] do not need to know about that. I agree Endpoint may not be the perfect name. I tried to find something "Go-ish" enough. It is a common name in go-kit, kubernetes, etc. Right now: SignerValidatorEndpoint: handles the listener contains SignerRemote Implements the PrivValidator interface connects and sets a connection object in a contained SignerRemote delegates PrivValidator some calls to SignerRemote which in turn uses the conn object that was set externally SignerRemote: Implements the PrivValidator interface read/writes from a connection object directly handles heartbeats SignerServiceEndpoint: Does most things in a single place delegates to a PrivValidator IIRC. * cleanup * Refactoring step 1 * Refactoring step 2 * move messages to another file * mark for future work / next steps * mark deprecated classes in docs * Fix linter problems * additional linter fixes --- cmd/priv_val_server/main.go | 4 +- node/node.go | 2 +- node/node_test.go | 12 +- privval/client.go | 240 --------- privval/client_test.go | 461 ---------------- privval/doc.go | 12 +- privval/errors.go | 22 + privval/file.go | 20 +- privval/{old_file.go => file_deprecated.go} | 1 + ...d_file_test.go => file_deprecated_test.go} | 0 privval/messages.go | 61 +++ privval/remote_signer_test.go | 90 ---- privval/server.go | 168 ------ .../{remote_signer.go => signer_remote.go} | 119 +---- privval/signer_remote_test.go | 68 +++ privval/signer_service_endpoint.go | 139 +++++ privval/signer_validator_endpoint.go | 230 ++++++++ privval/signer_validator_endpoint_test.go | 505 ++++++++++++++++++ privval/socket_dialers.go | 43 ++ privval/socket_dialers_test.go | 26 + privval/{socket.go => socket_listeners.go} | 85 +-- ...ocket_test.go => socket_listeners_test.go} | 28 +- privval/utils.go | 20 + privval/utils_test.go | 14 + .../internal/test_harness.go | 14 +- .../internal/test_harness_test.go | 14 +- 26 files changed, 1246 insertions(+), 1152 deletions(-) delete mode 100644 privval/client.go delete mode 100644 privval/client_test.go create mode 100644 privval/errors.go rename privval/{old_file.go => file_deprecated.go} (98%) rename privval/{old_file_test.go => file_deprecated_test.go} (100%) create mode 100644 privval/messages.go delete mode 100644 privval/remote_signer_test.go delete mode 100644 privval/server.go rename privval/{remote_signer.go => signer_remote.go} (50%) create mode 100644 privval/signer_remote_test.go create mode 100644 privval/signer_service_endpoint.go create mode 100644 privval/signer_validator_endpoint.go create mode 100644 privval/signer_validator_endpoint_test.go create mode 100644 privval/socket_dialers.go create mode 100644 privval/socket_dialers_test.go rename privval/{socket.go => socket_listeners.go} (60%) rename privval/{socket_test.go => socket_listeners_test.go} (71%) create mode 100644 privval/utils.go create mode 100644 privval/utils_test.go diff --git a/cmd/priv_val_server/main.go b/cmd/priv_val_server/main.go index 6d5406924..c86bced81 100644 --- a/cmd/priv_val_server/main.go +++ b/cmd/priv_val_server/main.go @@ -35,7 +35,7 @@ func main() { pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath) - var dialer privval.Dialer + var dialer privval.SocketDialer protocol, address := cmn.ProtocolAndAddress(*addr) switch protocol { case "unix": @@ -48,7 +48,7 @@ func main() { os.Exit(1) } - rs := privval.NewRemoteSigner(logger, *chainID, pv, dialer) + rs := privval.NewSignerServiceEndpoint(logger, *chainID, pv, dialer) err := rs.Start() if err != nil { panic(err) diff --git a/node/node.go b/node/node.go index e5ddd09cc..2b803502f 100644 --- a/node/node.go +++ b/node/node.go @@ -914,7 +914,7 @@ func createAndStartPrivValidatorSocketClient( ) } - pvsc := privval.NewSocketVal(logger.With("module", "privval"), listener) + pvsc := privval.NewSignerValidatorEndpoint(logger.With("module", "privval"), listener) if err := pvsc.Start(); err != nil { return nil, errors.Wrap(err, "failed to start private validator") } diff --git a/node/node_test.go b/node/node_test.go index 0fce0dd96..ebc3f2102 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -132,13 +132,13 @@ func TestNodeSetPrivValTCP(t *testing.T) { config.BaseConfig.PrivValidatorListenAddr = addr dialer := privval.DialTCPFn(addr, 100*time.Millisecond, ed25519.GenPrivKey()) - pvsc := privval.NewRemoteSigner( + pvsc := privval.NewSignerServiceEndpoint( log.TestingLogger(), config.ChainID(), types.NewMockPV(), dialer, ) - privval.RemoteSignerConnDeadline(100 * time.Millisecond)(pvsc) + privval.SignerServiceEndpointTimeoutReadWrite(100 * time.Millisecond)(pvsc) go func() { err := pvsc.Start() @@ -150,7 +150,7 @@ func TestNodeSetPrivValTCP(t *testing.T) { n, err := DefaultNewNode(config, log.TestingLogger()) require.NoError(t, err) - assert.IsType(t, &privval.SocketVal{}, n.PrivValidator()) + assert.IsType(t, &privval.SignerValidatorEndpoint{}, n.PrivValidator()) } // address without a protocol must result in error @@ -174,13 +174,13 @@ func TestNodeSetPrivValIPC(t *testing.T) { config.BaseConfig.PrivValidatorListenAddr = "unix://" + tmpfile dialer := privval.DialUnixFn(tmpfile) - pvsc := privval.NewRemoteSigner( + pvsc := privval.NewSignerServiceEndpoint( log.TestingLogger(), config.ChainID(), types.NewMockPV(), dialer, ) - privval.RemoteSignerConnDeadline(100 * time.Millisecond)(pvsc) + privval.SignerServiceEndpointTimeoutReadWrite(100 * time.Millisecond)(pvsc) go func() { err := pvsc.Start() @@ -190,7 +190,7 @@ func TestNodeSetPrivValIPC(t *testing.T) { n, err := DefaultNewNode(config, log.TestingLogger()) require.NoError(t, err) - assert.IsType(t, &privval.SocketVal{}, n.PrivValidator()) + assert.IsType(t, &privval.SignerValidatorEndpoint{}, n.PrivValidator()) } diff --git a/privval/client.go b/privval/client.go deleted file mode 100644 index 11151fee3..000000000 --- a/privval/client.go +++ /dev/null @@ -1,240 +0,0 @@ -package privval - -import ( - "errors" - "fmt" - "net" - "sync" - "time" - - "github.com/tendermint/tendermint/crypto" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/types" -) - -const ( - defaultConnHeartBeatSeconds = 2 - defaultDialRetries = 10 -) - -// Socket errors. -var ( - ErrUnexpectedResponse = errors.New("received unexpected response") -) - -var ( - connHeartbeat = time.Second * defaultConnHeartBeatSeconds -) - -// SocketValOption sets an optional parameter on the SocketVal. -type SocketValOption func(*SocketVal) - -// SocketValHeartbeat sets the period on which to check the liveness of the -// connected Signer connections. -func SocketValHeartbeat(period time.Duration) SocketValOption { - return func(sc *SocketVal) { sc.connHeartbeat = period } -} - -// SocketVal implements PrivValidator. -// It listens for an external process to dial in and uses -// the socket to request signatures. -type SocketVal struct { - cmn.BaseService - - listener net.Listener - - // ping - cancelPing chan struct{} - pingTicker *time.Ticker - connHeartbeat time.Duration - - // signer is mutable since it can be - // reset if the connection fails. - // failures are detected by a background - // ping routine. - // All messages are request/response, so we hold the mutex - // so only one request/response pair can happen at a time. - // Methods on the underlying net.Conn itself - // are already gorountine safe. - mtx sync.Mutex - signer *RemoteSignerClient -} - -// Check that SocketVal implements PrivValidator. -var _ types.PrivValidator = (*SocketVal)(nil) - -// NewSocketVal returns an instance of SocketVal. -func NewSocketVal( - logger log.Logger, - listener net.Listener, -) *SocketVal { - sc := &SocketVal{ - listener: listener, - connHeartbeat: connHeartbeat, - } - - sc.BaseService = *cmn.NewBaseService(logger, "SocketVal", sc) - - return sc -} - -//-------------------------------------------------------- -// Implement PrivValidator - -// GetPubKey implements PrivValidator. -func (sc *SocketVal) GetPubKey() crypto.PubKey { - sc.mtx.Lock() - defer sc.mtx.Unlock() - return sc.signer.GetPubKey() -} - -// SignVote implements PrivValidator. -func (sc *SocketVal) SignVote(chainID string, vote *types.Vote) error { - sc.mtx.Lock() - defer sc.mtx.Unlock() - return sc.signer.SignVote(chainID, vote) -} - -// SignProposal implements PrivValidator. -func (sc *SocketVal) SignProposal(chainID string, proposal *types.Proposal) error { - sc.mtx.Lock() - defer sc.mtx.Unlock() - return sc.signer.SignProposal(chainID, proposal) -} - -//-------------------------------------------------------- -// More thread safe methods proxied to the signer - -// Ping is used to check connection health. -func (sc *SocketVal) Ping() error { - sc.mtx.Lock() - defer sc.mtx.Unlock() - return sc.signer.Ping() -} - -// Close closes the underlying net.Conn. -func (sc *SocketVal) Close() { - sc.mtx.Lock() - defer sc.mtx.Unlock() - if sc.signer != nil { - if err := sc.signer.Close(); err != nil { - sc.Logger.Error("OnStop", "err", err) - } - } - - if sc.listener != nil { - if err := sc.listener.Close(); err != nil { - sc.Logger.Error("OnStop", "err", err) - } - } -} - -//-------------------------------------------------------- -// Service start and stop - -// OnStart implements cmn.Service. -func (sc *SocketVal) OnStart() error { - if closed, err := sc.reset(); err != nil { - sc.Logger.Error("OnStart", "err", err) - return err - } else if closed { - return fmt.Errorf("listener is closed") - } - - // Start a routine to keep the connection alive - sc.cancelPing = make(chan struct{}, 1) - sc.pingTicker = time.NewTicker(sc.connHeartbeat) - go func() { - for { - select { - case <-sc.pingTicker.C: - err := sc.Ping() - if err != nil { - sc.Logger.Error("Ping", "err", err) - if err == ErrUnexpectedResponse { - return - } - - closed, err := sc.reset() - if err != nil { - sc.Logger.Error("Reconnecting to remote signer failed", "err", err) - continue - } - if closed { - sc.Logger.Info("listener is closing") - return - } - - sc.Logger.Info("Re-created connection to remote signer", "impl", sc) - } - case <-sc.cancelPing: - sc.pingTicker.Stop() - return - } - } - }() - - return nil -} - -// OnStop implements cmn.Service. -func (sc *SocketVal) OnStop() { - if sc.cancelPing != nil { - close(sc.cancelPing) - } - sc.Close() -} - -//-------------------------------------------------------- -// Connection and signer management - -// waits to accept and sets a new connection. -// connection is closed in OnStop. -// returns true if the listener is closed -// (ie. it returns a nil conn). -func (sc *SocketVal) reset() (closed bool, err error) { - sc.mtx.Lock() - defer sc.mtx.Unlock() - - // first check if the conn already exists and close it. - if sc.signer != nil { - if err := sc.signer.Close(); err != nil { - sc.Logger.Error("error closing socket val connection during reset", "err", err) - } - } - - // wait for a new conn - conn, err := sc.acceptConnection() - if err != nil { - return false, err - } - - // listener is closed - if conn == nil { - return true, nil - } - - sc.signer, err = NewRemoteSignerClient(conn) - if err != nil { - // failed to fetch the pubkey. close out the connection. - if err := conn.Close(); err != nil { - sc.Logger.Error("error closing connection", "err", err) - } - return false, err - } - return false, nil -} - -// Attempt to accept a connection. -// Times out after the listener's acceptDeadline -func (sc *SocketVal) acceptConnection() (net.Conn, error) { - conn, err := sc.listener.Accept() - if err != nil { - if !sc.IsRunning() { - return nil, nil // Ignore error from listener closing. - } - return nil, err - } - return conn, nil -} diff --git a/privval/client_test.go b/privval/client_test.go deleted file mode 100644 index 1aea58cf0..000000000 --- a/privval/client_test.go +++ /dev/null @@ -1,461 +0,0 @@ -package privval - -import ( - "fmt" - "net" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/crypto/ed25519" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - - "github.com/tendermint/tendermint/types" -) - -var ( - testAcceptDeadline = defaultAcceptDeadlineSeconds * time.Second - - testConnDeadline = 100 * time.Millisecond - testConnDeadline2o3 = 66 * time.Millisecond // 2/3 of the other one - - testHeartbeatTimeout = 10 * time.Millisecond - testHeartbeatTimeout3o2 = 6 * time.Millisecond // 3/2 of the other one -) - -type socketTestCase struct { - addr string - dialer Dialer -} - -func socketTestCases(t *testing.T) []socketTestCase { - tcpAddr := fmt.Sprintf("tcp://%s", testFreeTCPAddr(t)) - unixFilePath, err := testUnixAddr() - require.NoError(t, err) - unixAddr := fmt.Sprintf("unix://%s", unixFilePath) - return []socketTestCase{ - { - addr: tcpAddr, - dialer: DialTCPFn(tcpAddr, testConnDeadline, ed25519.GenPrivKey()), - }, - { - addr: unixAddr, - dialer: DialUnixFn(unixFilePath), - }, - } -} - -func TestSocketPVAddress(t *testing.T) { - for _, tc := range socketTestCases(t) { - // Execute the test within a closure to ensure the deferred statements - // are called between each for loop iteration, for isolated test cases. - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - ) - defer sc.Stop() - defer rs.Stop() - - serverAddr := rs.privVal.GetPubKey().Address() - clientAddr := sc.GetPubKey().Address() - - assert.Equal(t, serverAddr, clientAddr) - }() - } -} - -func TestSocketPVPubKey(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - ) - defer sc.Stop() - defer rs.Stop() - - clientKey := sc.GetPubKey() - - privvalPubKey := rs.privVal.GetPubKey() - - assert.Equal(t, privvalPubKey, clientKey) - }() - } -} - -func TestSocketPVProposal(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - - ts = time.Now() - privProposal = &types.Proposal{Timestamp: ts} - clientProposal = &types.Proposal{Timestamp: ts} - ) - defer sc.Stop() - defer rs.Stop() - - require.NoError(t, rs.privVal.SignProposal(chainID, privProposal)) - require.NoError(t, sc.SignProposal(chainID, clientProposal)) - assert.Equal(t, privProposal.Signature, clientProposal.Signature) - }() - } -} - -func TestSocketPVVote(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) - }() - } -} - -func TestSocketPVVoteResetDeadline(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - time.Sleep(testConnDeadline2o3) - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) - - // This would exceed the deadline if it was not extended by the previous message - time.Sleep(testConnDeadline2o3) - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) - }() - } -} - -func TestSocketPVVoteKeepalive(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) - - ts = time.Now() - vType = types.PrecommitType - want = &types.Vote{Timestamp: ts, Type: vType} - have = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - time.Sleep(testConnDeadline * 2) - - require.NoError(t, rs.privVal.SignVote(chainID, want)) - require.NoError(t, sc.SignVote(chainID, have)) - assert.Equal(t, want.Signature, have.Signature) - }() - } -} - -func TestSocketPVDeadline(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - listenc = make(chan struct{}) - thisConnTimeout = 100 * time.Millisecond - sc = newSocketVal(log.TestingLogger(), tc.addr, thisConnTimeout) - ) - - go func(sc *SocketVal) { - defer close(listenc) - - // Note: the TCP connection times out at the accept() phase, - // whereas the Unix domain sockets connection times out while - // attempting to fetch the remote signer's public key. - assert.True(t, IsConnTimeout(sc.Start())) - - assert.False(t, sc.IsRunning()) - }(sc) - - for { - _, err := cmn.Connect(tc.addr) - if err == nil { - break - } - } - - <-listenc - }() - } -} - -func TestRemoteSignVoteErrors(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV(), tc.addr, tc.dialer) - - ts = time.Now() - vType = types.PrecommitType - vote = &types.Vote{Timestamp: ts, Type: vType} - ) - defer sc.Stop() - defer rs.Stop() - - err := sc.SignVote("", vote) - require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) - - err = rs.privVal.SignVote(chainID, vote) - require.Error(t, err) - err = sc.SignVote(chainID, vote) - require.Error(t, err) - }() - } -} - -func TestRemoteSignProposalErrors(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - chainID = cmn.RandStr(12) - sc, rs = testSetupSocketPair(t, chainID, types.NewErroringMockPV(), tc.addr, tc.dialer) - - ts = time.Now() - proposal = &types.Proposal{Timestamp: ts} - ) - defer sc.Stop() - defer rs.Stop() - - err := sc.SignProposal("", proposal) - require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) - - err = rs.privVal.SignProposal(chainID, proposal) - require.Error(t, err) - - err = sc.SignProposal(chainID, proposal) - require.Error(t, err) - }() - } -} - -func TestErrUnexpectedResponse(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - logger = log.TestingLogger() - chainID = cmn.RandStr(12) - readyc = make(chan struct{}) - errc = make(chan error, 1) - - rs = NewRemoteSigner( - logger, - chainID, - types.NewMockPV(), - tc.dialer, - ) - sc = newSocketVal(logger, tc.addr, testConnDeadline) - ) - - testStartSocketPV(t, readyc, sc) - defer sc.Stop() - RemoteSignerConnDeadline(time.Millisecond)(rs) - RemoteSignerConnRetries(100)(rs) - // we do not want to Start() the remote signer here and instead use the connection to - // reply with intentionally wrong replies below: - rsConn, err := rs.connect() - defer rsConn.Close() - require.NoError(t, err) - require.NotNil(t, rsConn) - // send over public key to get the remote signer running: - go testReadWriteResponse(t, &PubKeyResponse{}, rsConn) - <-readyc - - // Proposal: - go func(errc chan error) { - errc <- sc.SignProposal(chainID, &types.Proposal{}) - }(errc) - // read request and write wrong response: - go testReadWriteResponse(t, &SignedVoteResponse{}, rsConn) - err = <-errc - require.Error(t, err) - require.Equal(t, err, ErrUnexpectedResponse) - - // Vote: - go func(errc chan error) { - errc <- sc.SignVote(chainID, &types.Vote{}) - }(errc) - // read request and write wrong response: - go testReadWriteResponse(t, &SignedProposalResponse{}, rsConn) - err = <-errc - require.Error(t, err) - require.Equal(t, err, ErrUnexpectedResponse) - }() - } -} - -func TestRetryConnToRemoteSigner(t *testing.T) { - for _, tc := range socketTestCases(t) { - func() { - var ( - logger = log.TestingLogger() - chainID = cmn.RandStr(12) - readyc = make(chan struct{}) - - rs = NewRemoteSigner( - logger, - chainID, - types.NewMockPV(), - tc.dialer, - ) - thisConnTimeout = testConnDeadline - sc = newSocketVal(logger, tc.addr, thisConnTimeout) - ) - // Ping every: - SocketValHeartbeat(testHeartbeatTimeout)(sc) - - RemoteSignerConnDeadline(testConnDeadline)(rs) - RemoteSignerConnRetries(10)(rs) - - testStartSocketPV(t, readyc, sc) - defer sc.Stop() - require.NoError(t, rs.Start()) - assert.True(t, rs.IsRunning()) - - <-readyc - time.Sleep(testHeartbeatTimeout * 2) - - rs.Stop() - rs2 := NewRemoteSigner( - logger, - chainID, - types.NewMockPV(), - tc.dialer, - ) - // let some pings pass - time.Sleep(testHeartbeatTimeout3o2) - require.NoError(t, rs2.Start()) - assert.True(t, rs2.IsRunning()) - defer rs2.Stop() - - // give the client some time to re-establish the conn to the remote signer - // should see sth like this in the logs: - // - // E[10016-01-10|17:12:46.128] Ping err="remote signer timed out" - // I[10016-01-10|17:16:42.447] Re-created connection to remote signer impl=SocketVal - time.Sleep(testConnDeadline * 2) - }() - } -} - -func newSocketVal(logger log.Logger, addr string, connDeadline time.Duration) *SocketVal { - proto, address := cmn.ProtocolAndAddress(addr) - ln, err := net.Listen(proto, address) - logger.Info("Listening at", "proto", proto, "address", address) - if err != nil { - panic(err) - } - var svln net.Listener - if proto == "unix" { - unixLn := NewUnixListener(ln) - UnixListenerAcceptDeadline(testAcceptDeadline)(unixLn) - UnixListenerConnDeadline(connDeadline)(unixLn) - svln = unixLn - } else { - tcpLn := NewTCPListener(ln, ed25519.GenPrivKey()) - TCPListenerAcceptDeadline(testAcceptDeadline)(tcpLn) - TCPListenerConnDeadline(connDeadline)(tcpLn) - svln = tcpLn - } - return NewSocketVal(logger, svln) -} - -func testSetupSocketPair( - t *testing.T, - chainID string, - privValidator types.PrivValidator, - addr string, - dialer Dialer, -) (*SocketVal, *RemoteSigner) { - var ( - logger = log.TestingLogger() - privVal = privValidator - readyc = make(chan struct{}) - rs = NewRemoteSigner( - logger, - chainID, - privVal, - dialer, - ) - - thisConnTimeout = testConnDeadline - sc = newSocketVal(logger, addr, thisConnTimeout) - ) - - SocketValHeartbeat(testHeartbeatTimeout)(sc) - RemoteSignerConnDeadline(testConnDeadline)(rs) - RemoteSignerConnRetries(1e6)(rs) - - testStartSocketPV(t, readyc, sc) - - require.NoError(t, rs.Start()) - assert.True(t, rs.IsRunning()) - - <-readyc - - return sc, rs -} - -func testReadWriteResponse(t *testing.T, resp RemoteSignerMsg, rsConn net.Conn) { - _, err := readMsg(rsConn) - require.NoError(t, err) - - err = writeMsg(rsConn, resp) - require.NoError(t, err) -} - -func testStartSocketPV(t *testing.T, readyc chan struct{}, sc *SocketVal) { - go func(sc *SocketVal) { - require.NoError(t, sc.Start()) - assert.True(t, sc.IsRunning()) - - readyc <- struct{}{} - }(sc) -} - -// testFreeTCPAddr claims a free port so we don't block on listener being ready. -func testFreeTCPAddr(t *testing.T) string { - ln, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - defer ln.Close() - - return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port) -} diff --git a/privval/doc.go b/privval/doc.go index ed378c190..80869a6a7 100644 --- a/privval/doc.go +++ b/privval/doc.go @@ -6,16 +6,16 @@ FilePV FilePV is the simplest implementation and developer default. It uses one file for the private key and another to store state. -SocketVal +SignerValidatorEndpoint -SocketVal establishes a connection to an external process, like a Key Management Server (KMS), using a socket. -SocketVal listens for the external KMS process to dial in. -SocketVal takes a listener, which determines the type of connection +SignerValidatorEndpoint establishes a connection to an external process, like a Key Management Server (KMS), using a socket. +SignerValidatorEndpoint listens for the external KMS process to dial in. +SignerValidatorEndpoint takes a listener, which determines the type of connection (ie. encrypted over tcp, or unencrypted over unix). -RemoteSigner +SignerServiceEndpoint -RemoteSigner is a simple wrapper around a net.Conn. It's used by both IPCVal and TCPVal. +SignerServiceEndpoint is a simple wrapper around a net.Conn. It's used by both IPCVal and TCPVal. */ package privval diff --git a/privval/errors.go b/privval/errors.go new file mode 100644 index 000000000..75fb25fc6 --- /dev/null +++ b/privval/errors.go @@ -0,0 +1,22 @@ +package privval + +import ( + "fmt" +) + +// Socket errors. +var ( + ErrUnexpectedResponse = fmt.Errorf("received unexpected response") + ErrConnTimeout = fmt.Errorf("remote signer timed out") +) + +// RemoteSignerError allows (remote) validators to include meaningful error descriptions in their reply. +type RemoteSignerError struct { + // TODO(ismail): create an enum of known errors + Code int + Description string +} + +func (e *RemoteSignerError) Error() string { + return fmt.Sprintf("signerServiceEndpoint returned error #%d: %s", e.Code, e.Description) +} diff --git a/privval/file.go b/privval/file.go index 8eb38e806..1cb88f7c0 100644 --- a/privval/file.go +++ b/privval/file.go @@ -49,7 +49,7 @@ type FilePVKey struct { func (pvKey FilePVKey) Save() { outFile := pvKey.filePath if outFile == "" { - panic("Cannot save PrivValidator key: filePath not set") + panic("cannot save PrivValidator key: filePath not set") } jsonBytes, err := cdc.MarshalJSONIndent(pvKey, "", " ") @@ -86,17 +86,17 @@ type FilePVLastSignState struct { func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bool, error) { if lss.Height > height { - return false, fmt.Errorf("Height regression. Got %v, last height %v", height, lss.Height) + return false, fmt.Errorf("height regression. Got %v, last height %v", height, lss.Height) } if lss.Height == height { if lss.Round > round { - return false, fmt.Errorf("Round regression at height %v. Got %v, last round %v", height, round, lss.Round) + return false, fmt.Errorf("round regression at height %v. Got %v, last round %v", height, round, lss.Round) } if lss.Round == round { if lss.Step > step { - return false, fmt.Errorf("Step regression at height %v round %v. Got %v, last step %v", height, round, step, lss.Step) + return false, fmt.Errorf("step regression at height %v round %v. Got %v, last step %v", height, round, step, lss.Step) } else if lss.Step == step { if lss.SignBytes != nil { if lss.Signature == nil { @@ -104,7 +104,7 @@ func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bo } return true, nil } - return false, errors.New("No SignBytes found") + return false, errors.New("no SignBytes found") } } } @@ -115,7 +115,7 @@ func (lss *FilePVLastSignState) CheckHRS(height int64, round int, step int8) (bo func (lss *FilePVLastSignState) Save() { outFile := lss.filePath if outFile == "" { - panic("Cannot save FilePVLastSignState: filePath not set") + panic("cannot save FilePVLastSignState: filePath not set") } jsonBytes, err := cdc.MarshalJSONIndent(lss, "", " ") if err != nil { @@ -237,7 +237,7 @@ func (pv *FilePV) GetPubKey() crypto.PubKey { // chainID. Implements PrivValidator. func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error { if err := pv.signVote(chainID, vote); err != nil { - return fmt.Errorf("Error signing vote: %v", err) + return fmt.Errorf("error signing vote: %v", err) } return nil } @@ -246,7 +246,7 @@ func (pv *FilePV) SignVote(chainID string, vote *types.Vote) error { // the chainID. Implements PrivValidator. func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error { if err := pv.signProposal(chainID, proposal); err != nil { - return fmt.Errorf("Error signing proposal: %v", err) + return fmt.Errorf("error signing proposal: %v", err) } return nil } @@ -303,7 +303,7 @@ func (pv *FilePV) signVote(chainID string, vote *types.Vote) error { vote.Timestamp = timestamp vote.Signature = lss.Signature } else { - err = fmt.Errorf("Conflicting data") + err = fmt.Errorf("conflicting data") } return err } @@ -345,7 +345,7 @@ func (pv *FilePV) signProposal(chainID string, proposal *types.Proposal) error { proposal.Timestamp = timestamp proposal.Signature = lss.Signature } else { - err = fmt.Errorf("Conflicting data") + err = fmt.Errorf("conflicting data") } return err } diff --git a/privval/old_file.go b/privval/file_deprecated.go similarity index 98% rename from privval/old_file.go rename to privval/file_deprecated.go index ec72c1834..d010de763 100644 --- a/privval/old_file.go +++ b/privval/file_deprecated.go @@ -10,6 +10,7 @@ import ( ) // OldFilePV is the old version of the FilePV, pre v0.28.0. +// Deprecated: Use FilePV instead. type OldFilePV struct { Address types.Address `json:"address"` PubKey crypto.PubKey `json:"pub_key"` diff --git a/privval/old_file_test.go b/privval/file_deprecated_test.go similarity index 100% rename from privval/old_file_test.go rename to privval/file_deprecated_test.go diff --git a/privval/messages.go b/privval/messages.go new file mode 100644 index 000000000..6774a2795 --- /dev/null +++ b/privval/messages.go @@ -0,0 +1,61 @@ +package privval + +import ( + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/types" +) + +// RemoteSignerMsg is sent between SignerServiceEndpoint and the SignerServiceEndpoint client. +type RemoteSignerMsg interface{} + +func RegisterRemoteSignerMsg(cdc *amino.Codec) { + cdc.RegisterInterface((*RemoteSignerMsg)(nil), nil) + cdc.RegisterConcrete(&PubKeyRequest{}, "tendermint/remotesigner/PubKeyRequest", nil) + cdc.RegisterConcrete(&PubKeyResponse{}, "tendermint/remotesigner/PubKeyResponse", nil) + cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/remotesigner/SignVoteRequest", nil) + cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/remotesigner/SignedVoteResponse", nil) + cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/remotesigner/SignProposalRequest", nil) + cdc.RegisterConcrete(&SignedProposalResponse{}, "tendermint/remotesigner/SignedProposalResponse", nil) + cdc.RegisterConcrete(&PingRequest{}, "tendermint/remotesigner/PingRequest", nil) + cdc.RegisterConcrete(&PingResponse{}, "tendermint/remotesigner/PingResponse", nil) +} + +// PubKeyRequest requests the consensus public key from the remote signer. +type PubKeyRequest struct{} + +// PubKeyResponse is a PrivValidatorSocket message containing the public key. +type PubKeyResponse struct { + PubKey crypto.PubKey + Error *RemoteSignerError +} + +// SignVoteRequest is a PrivValidatorSocket message containing a vote. +type SignVoteRequest struct { + Vote *types.Vote +} + +// SignedVoteResponse is a PrivValidatorSocket message containing a signed vote along with a potenial error message. +type SignedVoteResponse struct { + Vote *types.Vote + Error *RemoteSignerError +} + +// SignProposalRequest is a PrivValidatorSocket message containing a Proposal. +type SignProposalRequest struct { + Proposal *types.Proposal +} + +// SignedProposalResponse is a PrivValidatorSocket message containing a proposal response +type SignedProposalResponse struct { + Proposal *types.Proposal + Error *RemoteSignerError +} + +// PingRequest is a PrivValidatorSocket message to keep the connection alive. +type PingRequest struct { +} + +// PingRequest is a PrivValidatorSocket response to keep the connection alive. +type PingResponse struct { +} diff --git a/privval/remote_signer_test.go b/privval/remote_signer_test.go deleted file mode 100644 index cb2a600db..000000000 --- a/privval/remote_signer_test.go +++ /dev/null @@ -1,90 +0,0 @@ -package privval - -import ( - "net" - "testing" - "time" - - "github.com/pkg/errors" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/types" -) - -// TestRemoteSignerRetryTCPOnly will test connection retry attempts over TCP. We -// don't need this for Unix sockets because the OS instantly knows the state of -// both ends of the socket connection. This basically causes the -// RemoteSigner.dialer() call inside RemoteSigner.connect() to return -// successfully immediately, putting an instant stop to any retry attempts. -func TestRemoteSignerRetryTCPOnly(t *testing.T) { - var ( - attemptc = make(chan int) - retries = 2 - ) - - ln, err := net.Listen("tcp", "127.0.0.1:0") - require.NoError(t, err) - - go func(ln net.Listener, attemptc chan<- int) { - attempts := 0 - - for { - conn, err := ln.Accept() - require.NoError(t, err) - - err = conn.Close() - require.NoError(t, err) - - attempts++ - - if attempts == retries { - attemptc <- attempts - break - } - } - }(ln, attemptc) - - rs := NewRemoteSigner( - log.TestingLogger(), - cmn.RandStr(12), - types.NewMockPV(), - DialTCPFn(ln.Addr().String(), testConnDeadline, ed25519.GenPrivKey()), - ) - defer rs.Stop() - - RemoteSignerConnDeadline(time.Millisecond)(rs) - RemoteSignerConnRetries(retries)(rs) - - assert.Equal(t, rs.Start(), ErrDialRetryMax) - - select { - case attempts := <-attemptc: - assert.Equal(t, retries, attempts) - case <-time.After(100 * time.Millisecond): - t.Error("expected remote to observe connection attempts") - } -} - -func TestIsConnTimeoutForFundamentalTimeouts(t *testing.T) { - // Generate a networking timeout - dialer := DialTCPFn(testFreeTCPAddr(t), time.Millisecond, ed25519.GenPrivKey()) - _, err := dialer() - assert.Error(t, err) - assert.True(t, IsConnTimeout(err)) -} - -func TestIsConnTimeoutForWrappedConnTimeouts(t *testing.T) { - dialer := DialTCPFn(testFreeTCPAddr(t), time.Millisecond, ed25519.GenPrivKey()) - _, err := dialer() - assert.Error(t, err) - err = cmn.ErrorWrap(ErrConnTimeout, err.Error()) - assert.True(t, IsConnTimeout(err)) -} - -func TestIsConnTimeoutForNonTimeoutErrors(t *testing.T) { - assert.False(t, IsConnTimeout(cmn.ErrorWrap(ErrDialRetryMax, "max retries exceeded"))) - assert.False(t, IsConnTimeout(errors.New("completely irrelevant error"))) -} diff --git a/privval/server.go b/privval/server.go deleted file mode 100644 index cce659525..000000000 --- a/privval/server.go +++ /dev/null @@ -1,168 +0,0 @@ -package privval - -import ( - "io" - "net" - "time" - - "github.com/pkg/errors" - "github.com/tendermint/tendermint/crypto/ed25519" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - p2pconn "github.com/tendermint/tendermint/p2p/conn" - "github.com/tendermint/tendermint/types" -) - -// Socket errors. -var ( - ErrDialRetryMax = errors.New("dialed maximum retries") -) - -// RemoteSignerOption sets an optional parameter on the RemoteSigner. -type RemoteSignerOption func(*RemoteSigner) - -// RemoteSignerConnDeadline sets the read and write deadline for connections -// from external signing processes. -func RemoteSignerConnDeadline(deadline time.Duration) RemoteSignerOption { - return func(ss *RemoteSigner) { ss.connDeadline = deadline } -} - -// RemoteSignerConnRetries sets the amount of attempted retries to connect. -func RemoteSignerConnRetries(retries int) RemoteSignerOption { - return func(ss *RemoteSigner) { ss.connRetries = retries } -} - -// RemoteSigner dials using its dialer and responds to any -// signature requests using its privVal. -type RemoteSigner struct { - cmn.BaseService - - chainID string - connDeadline time.Duration - connRetries int - privVal types.PrivValidator - - dialer Dialer - conn net.Conn -} - -// Dialer dials a remote address and returns a net.Conn or an error. -type Dialer func() (net.Conn, error) - -// DialTCPFn dials the given tcp addr, using the given connTimeout and privKey for the -// authenticated encryption handshake. -func DialTCPFn(addr string, connTimeout time.Duration, privKey ed25519.PrivKeyEd25519) Dialer { - return func() (net.Conn, error) { - conn, err := cmn.Connect(addr) - if err == nil { - err = conn.SetDeadline(time.Now().Add(connTimeout)) - } - if err == nil { - conn, err = p2pconn.MakeSecretConnection(conn, privKey) - } - return conn, err - } -} - -// DialUnixFn dials the given unix socket. -func DialUnixFn(addr string) Dialer { - return func() (net.Conn, error) { - unixAddr := &net.UnixAddr{Name: addr, Net: "unix"} - return net.DialUnix("unix", nil, unixAddr) - } -} - -// NewRemoteSigner return a RemoteSigner that will dial using the given -// dialer and respond to any signature requests over the connection -// using the given privVal. -func NewRemoteSigner( - logger log.Logger, - chainID string, - privVal types.PrivValidator, - dialer Dialer, -) *RemoteSigner { - rs := &RemoteSigner{ - chainID: chainID, - connDeadline: time.Second * defaultConnDeadlineSeconds, - connRetries: defaultDialRetries, - privVal: privVal, - dialer: dialer, - } - - rs.BaseService = *cmn.NewBaseService(logger, "RemoteSigner", rs) - return rs -} - -// OnStart implements cmn.Service. -func (rs *RemoteSigner) OnStart() error { - conn, err := rs.connect() - if err != nil { - rs.Logger.Error("OnStart", "err", err) - return err - } - rs.conn = conn - - go rs.handleConnection(conn) - - return nil -} - -// OnStop implements cmn.Service. -func (rs *RemoteSigner) OnStop() { - if rs.conn == nil { - return - } - - if err := rs.conn.Close(); err != nil { - rs.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed")) - } -} - -func (rs *RemoteSigner) connect() (net.Conn, error) { - for retries := rs.connRetries; retries > 0; retries-- { - // Don't sleep if it is the first retry. - if retries != rs.connRetries { - time.Sleep(rs.connDeadline) - } - conn, err := rs.dialer() - if err != nil { - rs.Logger.Error("dialing", "err", err) - continue - } - return conn, nil - } - - return nil, ErrDialRetryMax -} - -func (rs *RemoteSigner) handleConnection(conn net.Conn) { - for { - if !rs.IsRunning() { - return // Ignore error from listener closing. - } - - // Reset the connection deadline - conn.SetDeadline(time.Now().Add(rs.connDeadline)) - - req, err := readMsg(conn) - if err != nil { - if err != io.EOF { - rs.Logger.Error("handleConnection readMsg", "err", err) - } - return - } - - res, err := handleRequest(req, rs.chainID, rs.privVal) - - if err != nil { - // only log the error; we'll reply with an error in res - rs.Logger.Error("handleConnection handleRequest", "err", err) - } - - err = writeMsg(conn, res) - if err != nil { - rs.Logger.Error("handleConnection writeMsg", "err", err) - return - } - } -} diff --git a/privval/remote_signer.go b/privval/signer_remote.go similarity index 50% rename from privval/remote_signer.go rename to privval/signer_remote.go index a5b8cac64..53b0cb773 100644 --- a/privval/remote_signer.go +++ b/privval/signer_remote.go @@ -7,51 +7,44 @@ import ( "github.com/pkg/errors" - amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/types" ) -// Socket errors. -var ( - ErrConnTimeout = errors.New("remote signer timed out") -) - -// RemoteSignerClient implements PrivValidator. -// It uses a net.Conn to request signatures -// from an external process. -type RemoteSignerClient struct { +// SignerRemote implements PrivValidator. +// It uses a net.Conn to request signatures from an external process. +type SignerRemote struct { conn net.Conn // memoized consensusPubKey crypto.PubKey } -// Check that RemoteSignerClient implements PrivValidator. -var _ types.PrivValidator = (*RemoteSignerClient)(nil) +// Check that SignerRemote implements PrivValidator. +var _ types.PrivValidator = (*SignerRemote)(nil) -// NewRemoteSignerClient returns an instance of RemoteSignerClient. -func NewRemoteSignerClient(conn net.Conn) (*RemoteSignerClient, error) { +// NewSignerRemote returns an instance of SignerRemote. +func NewSignerRemote(conn net.Conn) (*SignerRemote, error) { // retrieve and memoize the consensus public key once. pubKey, err := getPubKey(conn) if err != nil { return nil, cmn.ErrorWrap(err, "error while retrieving public key for remote signer") } - return &RemoteSignerClient{ + return &SignerRemote{ conn: conn, consensusPubKey: pubKey, }, nil } // Close calls Close on the underlying net.Conn. -func (sc *RemoteSignerClient) Close() error { +func (sc *SignerRemote) Close() error { return sc.conn.Close() } // GetPubKey implements PrivValidator. -func (sc *RemoteSignerClient) GetPubKey() crypto.PubKey { +func (sc *SignerRemote) GetPubKey() crypto.PubKey { return sc.consensusPubKey } @@ -66,6 +59,7 @@ func getPubKey(conn net.Conn) (crypto.PubKey, error) { if err != nil { return nil, err } + pubKeyResp, ok := res.(*PubKeyResponse) if !ok { return nil, errors.Wrap(ErrUnexpectedResponse, "response is not PubKeyResponse") @@ -79,7 +73,7 @@ func getPubKey(conn net.Conn) (crypto.PubKey, error) { } // SignVote implements PrivValidator. -func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error { +func (sc *SignerRemote) SignVote(chainID string, vote *types.Vote) error { err := writeMsg(sc.conn, &SignVoteRequest{Vote: vote}) if err != nil { return err @@ -103,10 +97,7 @@ func (sc *RemoteSignerClient) SignVote(chainID string, vote *types.Vote) error { } // SignProposal implements PrivValidator. -func (sc *RemoteSignerClient) SignProposal( - chainID string, - proposal *types.Proposal, -) error { +func (sc *SignerRemote) SignProposal(chainID string, proposal *types.Proposal) error { err := writeMsg(sc.conn, &SignProposalRequest{Proposal: proposal}) if err != nil { return err @@ -129,7 +120,7 @@ func (sc *RemoteSignerClient) SignProposal( } // Ping is used to check connection health. -func (sc *RemoteSignerClient) Ping() error { +func (sc *SignerRemote) Ping() error { err := writeMsg(sc.conn, &PingRequest{}) if err != nil { return err @@ -147,69 +138,6 @@ func (sc *RemoteSignerClient) Ping() error { return nil } -// RemoteSignerMsg is sent between RemoteSigner and the RemoteSigner client. -type RemoteSignerMsg interface{} - -func RegisterRemoteSignerMsg(cdc *amino.Codec) { - cdc.RegisterInterface((*RemoteSignerMsg)(nil), nil) - cdc.RegisterConcrete(&PubKeyRequest{}, "tendermint/remotesigner/PubKeyRequest", nil) - cdc.RegisterConcrete(&PubKeyResponse{}, "tendermint/remotesigner/PubKeyResponse", nil) - cdc.RegisterConcrete(&SignVoteRequest{}, "tendermint/remotesigner/SignVoteRequest", nil) - cdc.RegisterConcrete(&SignedVoteResponse{}, "tendermint/remotesigner/SignedVoteResponse", nil) - cdc.RegisterConcrete(&SignProposalRequest{}, "tendermint/remotesigner/SignProposalRequest", nil) - cdc.RegisterConcrete(&SignedProposalResponse{}, "tendermint/remotesigner/SignedProposalResponse", nil) - cdc.RegisterConcrete(&PingRequest{}, "tendermint/remotesigner/PingRequest", nil) - cdc.RegisterConcrete(&PingResponse{}, "tendermint/remotesigner/PingResponse", nil) -} - -// PubKeyRequest requests the consensus public key from the remote signer. -type PubKeyRequest struct{} - -// PubKeyResponse is a PrivValidatorSocket message containing the public key. -type PubKeyResponse struct { - PubKey crypto.PubKey - Error *RemoteSignerError -} - -// SignVoteRequest is a PrivValidatorSocket message containing a vote. -type SignVoteRequest struct { - Vote *types.Vote -} - -// SignedVoteResponse is a PrivValidatorSocket message containing a signed vote along with a potenial error message. -type SignedVoteResponse struct { - Vote *types.Vote - Error *RemoteSignerError -} - -// SignProposalRequest is a PrivValidatorSocket message containing a Proposal. -type SignProposalRequest struct { - Proposal *types.Proposal -} - -type SignedProposalResponse struct { - Proposal *types.Proposal - Error *RemoteSignerError -} - -// PingRequest is a PrivValidatorSocket message to keep the connection alive. -type PingRequest struct { -} - -type PingResponse struct { -} - -// RemoteSignerError allows (remote) validators to include meaningful error descriptions in their reply. -type RemoteSignerError struct { - // TODO(ismail): create an enum of known errors - Code int - Description string -} - -func (e *RemoteSignerError) Error() string { - return fmt.Sprintf("RemoteSigner returned error #%d: %s", e.Code, e.Description) -} - func readMsg(r io.Reader) (msg RemoteSignerMsg, err error) { const maxRemoteSignerMsgSize = 1024 * 10 _, err = cdc.UnmarshalBinaryLengthPrefixedReader(r, &msg, maxRemoteSignerMsgSize) @@ -236,6 +164,7 @@ func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValida var p crypto.PubKey p = privVal.GetPubKey() res = &PubKeyResponse{p, nil} + case *SignVoteRequest: err = privVal.SignVote(chainID, r.Vote) if err != nil { @@ -243,6 +172,7 @@ func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValida } else { res = &SignedVoteResponse{r.Vote, nil} } + case *SignProposalRequest: err = privVal.SignProposal(chainID, r.Proposal) if err != nil { @@ -250,26 +180,13 @@ func handleRequest(req RemoteSignerMsg, chainID string, privVal types.PrivValida } else { res = &SignedProposalResponse{r.Proposal, nil} } + case *PingRequest: res = &PingResponse{} + default: err = fmt.Errorf("unknown msg: %v", r) } return res, err } - -// IsConnTimeout returns a boolean indicating whether the error is known to -// report that a connection timeout occurred. This detects both fundamental -// network timeouts, as well as ErrConnTimeout errors. -func IsConnTimeout(err error) bool { - if cmnErr, ok := err.(cmn.Error); ok { - if cmnErr.Data() == ErrConnTimeout { - return true - } - } - if _, ok := err.(timeoutError); ok { - return true - } - return false -} diff --git a/privval/signer_remote_test.go b/privval/signer_remote_test.go new file mode 100644 index 000000000..28230b803 --- /dev/null +++ b/privval/signer_remote_test.go @@ -0,0 +1,68 @@ +package privval + +import ( + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" +) + +// TestSignerRemoteRetryTCPOnly will test connection retry attempts over TCP. We +// don't need this for Unix sockets because the OS instantly knows the state of +// both ends of the socket connection. This basically causes the +// SignerServiceEndpoint.dialer() call inside SignerServiceEndpoint.connect() to return +// successfully immediately, putting an instant stop to any retry attempts. +func TestSignerRemoteRetryTCPOnly(t *testing.T) { + var ( + attemptCh = make(chan int) + retries = 2 + ) + + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + + go func(ln net.Listener, attemptCh chan<- int) { + attempts := 0 + + for { + conn, err := ln.Accept() + require.NoError(t, err) + + err = conn.Close() + require.NoError(t, err) + + attempts++ + + if attempts == retries { + attemptCh <- attempts + break + } + } + }(ln, attemptCh) + + serviceEndpoint := NewSignerServiceEndpoint( + log.TestingLogger(), + cmn.RandStr(12), + types.NewMockPV(), + DialTCPFn(ln.Addr().String(), testTimeoutReadWrite, ed25519.GenPrivKey()), + ) + defer serviceEndpoint.Stop() + + SignerServiceEndpointTimeoutReadWrite(time.Millisecond)(serviceEndpoint) + SignerServiceEndpointConnRetries(retries)(serviceEndpoint) + + assert.Equal(t, serviceEndpoint.Start(), ErrDialRetryMax) + + select { + case attempts := <-attemptCh: + assert.Equal(t, retries, attempts) + case <-time.After(100 * time.Millisecond): + t.Error("expected remote to observe connection attempts") + } +} diff --git a/privval/signer_service_endpoint.go b/privval/signer_service_endpoint.go new file mode 100644 index 000000000..1b37d5fc6 --- /dev/null +++ b/privval/signer_service_endpoint.go @@ -0,0 +1,139 @@ +package privval + +import ( + "io" + "net" + "time" + + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" +) + +// SignerServiceEndpointOption sets an optional parameter on the SignerServiceEndpoint. +type SignerServiceEndpointOption func(*SignerServiceEndpoint) + +// SignerServiceEndpointTimeoutReadWrite sets the read and write timeout for connections +// from external signing processes. +func SignerServiceEndpointTimeoutReadWrite(timeout time.Duration) SignerServiceEndpointOption { + return func(ss *SignerServiceEndpoint) { ss.timeoutReadWrite = timeout } +} + +// SignerServiceEndpointConnRetries sets the amount of attempted retries to connect. +func SignerServiceEndpointConnRetries(retries int) SignerServiceEndpointOption { + return func(ss *SignerServiceEndpoint) { ss.connRetries = retries } +} + +// SignerServiceEndpoint dials using its dialer and responds to any +// signature requests using its privVal. +type SignerServiceEndpoint struct { + cmn.BaseService + + chainID string + timeoutReadWrite time.Duration + connRetries int + privVal types.PrivValidator + + dialer SocketDialer + conn net.Conn +} + +// NewSignerServiceEndpoint returns a SignerServiceEndpoint that will dial using the given +// dialer and respond to any signature requests over the connection +// using the given privVal. +func NewSignerServiceEndpoint( + logger log.Logger, + chainID string, + privVal types.PrivValidator, + dialer SocketDialer, +) *SignerServiceEndpoint { + se := &SignerServiceEndpoint{ + chainID: chainID, + timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds, + connRetries: defaultMaxDialRetries, + privVal: privVal, + dialer: dialer, + } + + se.BaseService = *cmn.NewBaseService(logger, "SignerServiceEndpoint", se) + return se +} + +// OnStart implements cmn.Service. +func (se *SignerServiceEndpoint) OnStart() error { + conn, err := se.connect() + if err != nil { + se.Logger.Error("OnStart", "err", err) + return err + } + + se.conn = conn + go se.handleConnection(conn) + + return nil +} + +// OnStop implements cmn.Service. +func (se *SignerServiceEndpoint) OnStop() { + if se.conn == nil { + return + } + + if err := se.conn.Close(); err != nil { + se.Logger.Error("OnStop", "err", cmn.ErrorWrap(err, "closing listener failed")) + } +} + +func (se *SignerServiceEndpoint) connect() (net.Conn, error) { + for retries := 0; retries < se.connRetries; retries++ { + // Don't sleep if it is the first retry. + if retries > 0 { + time.Sleep(se.timeoutReadWrite) + } + + conn, err := se.dialer() + if err == nil { + return conn, nil + } + + se.Logger.Error("dialing", "err", err) + } + + return nil, ErrDialRetryMax +} + +func (se *SignerServiceEndpoint) handleConnection(conn net.Conn) { + for { + if !se.IsRunning() { + return // Ignore error from listener closing. + } + + // Reset the connection deadline + deadline := time.Now().Add(se.timeoutReadWrite) + err := conn.SetDeadline(deadline) + if err != nil { + return + } + + req, err := readMsg(conn) + if err != nil { + if err != io.EOF { + se.Logger.Error("handleConnection readMsg", "err", err) + } + return + } + + res, err := handleRequest(req, se.chainID, se.privVal) + + if err != nil { + // only log the error; we'll reply with an error in res + se.Logger.Error("handleConnection handleRequest", "err", err) + } + + err = writeMsg(conn, res) + if err != nil { + se.Logger.Error("handleConnection writeMsg", "err", err) + return + } + } +} diff --git a/privval/signer_validator_endpoint.go b/privval/signer_validator_endpoint.go new file mode 100644 index 000000000..6dc7f99d5 --- /dev/null +++ b/privval/signer_validator_endpoint.go @@ -0,0 +1,230 @@ +package privval + +import ( + "fmt" + "net" + "sync" + "time" + + "github.com/tendermint/tendermint/crypto" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/types" +) + +const ( + defaultHeartbeatSeconds = 2 + defaultMaxDialRetries = 10 +) + +var ( + heartbeatPeriod = time.Second * defaultHeartbeatSeconds +) + +// SignerValidatorEndpointOption sets an optional parameter on the SocketVal. +type SignerValidatorEndpointOption func(*SignerValidatorEndpoint) + +// SignerValidatorEndpointSetHeartbeat sets the period on which to check the liveness of the +// connected Signer connections. +func SignerValidatorEndpointSetHeartbeat(period time.Duration) SignerValidatorEndpointOption { + return func(sc *SignerValidatorEndpoint) { sc.heartbeatPeriod = period } +} + +// SocketVal implements PrivValidator. +// It listens for an external process to dial in and uses +// the socket to request signatures. +type SignerValidatorEndpoint struct { + cmn.BaseService + + listener net.Listener + + // ping + cancelPingCh chan struct{} + pingTicker *time.Ticker + heartbeatPeriod time.Duration + + // signer is mutable since it can be reset if the connection fails. + // failures are detected by a background ping routine. + // All messages are request/response, so we hold the mutex + // so only one request/response pair can happen at a time. + // Methods on the underlying net.Conn itself are already goroutine safe. + mtx sync.Mutex + + // TODO: Signer should encapsulate and hide the endpoint completely. Invert the relation + signer *SignerRemote +} + +// Check that SignerValidatorEndpoint implements PrivValidator. +var _ types.PrivValidator = (*SignerValidatorEndpoint)(nil) + +// NewSignerValidatorEndpoint returns an instance of SignerValidatorEndpoint. +func NewSignerValidatorEndpoint(logger log.Logger, listener net.Listener) *SignerValidatorEndpoint { + sc := &SignerValidatorEndpoint{ + listener: listener, + heartbeatPeriod: heartbeatPeriod, + } + + sc.BaseService = *cmn.NewBaseService(logger, "SignerValidatorEndpoint", sc) + + return sc +} + +//-------------------------------------------------------- +// Implement PrivValidator + +// GetPubKey implements PrivValidator. +func (ve *SignerValidatorEndpoint) GetPubKey() crypto.PubKey { + ve.mtx.Lock() + defer ve.mtx.Unlock() + return ve.signer.GetPubKey() +} + +// SignVote implements PrivValidator. +func (ve *SignerValidatorEndpoint) SignVote(chainID string, vote *types.Vote) error { + ve.mtx.Lock() + defer ve.mtx.Unlock() + return ve.signer.SignVote(chainID, vote) +} + +// SignProposal implements PrivValidator. +func (ve *SignerValidatorEndpoint) SignProposal(chainID string, proposal *types.Proposal) error { + ve.mtx.Lock() + defer ve.mtx.Unlock() + return ve.signer.SignProposal(chainID, proposal) +} + +//-------------------------------------------------------- +// More thread safe methods proxied to the signer + +// Ping is used to check connection health. +func (ve *SignerValidatorEndpoint) Ping() error { + ve.mtx.Lock() + defer ve.mtx.Unlock() + return ve.signer.Ping() +} + +// Close closes the underlying net.Conn. +func (ve *SignerValidatorEndpoint) Close() { + ve.mtx.Lock() + defer ve.mtx.Unlock() + if ve.signer != nil { + if err := ve.signer.Close(); err != nil { + ve.Logger.Error("OnStop", "err", err) + } + } + + if ve.listener != nil { + if err := ve.listener.Close(); err != nil { + ve.Logger.Error("OnStop", "err", err) + } + } +} + +//-------------------------------------------------------- +// Service start and stop + +// OnStart implements cmn.Service. +func (ve *SignerValidatorEndpoint) OnStart() error { + if closed, err := ve.reset(); err != nil { + ve.Logger.Error("OnStart", "err", err) + return err + } else if closed { + return fmt.Errorf("listener is closed") + } + + // Start a routine to keep the connection alive + ve.cancelPingCh = make(chan struct{}, 1) + ve.pingTicker = time.NewTicker(ve.heartbeatPeriod) + go func() { + for { + select { + case <-ve.pingTicker.C: + err := ve.Ping() + if err != nil { + ve.Logger.Error("Ping", "err", err) + if err == ErrUnexpectedResponse { + return + } + + closed, err := ve.reset() + if err != nil { + ve.Logger.Error("Reconnecting to remote signer failed", "err", err) + continue + } + if closed { + ve.Logger.Info("listener is closing") + return + } + + ve.Logger.Info("Re-created connection to remote signer", "impl", ve) + } + case <-ve.cancelPingCh: + ve.pingTicker.Stop() + return + } + } + }() + + return nil +} + +// OnStop implements cmn.Service. +func (ve *SignerValidatorEndpoint) OnStop() { + if ve.cancelPingCh != nil { + close(ve.cancelPingCh) + } + ve.Close() +} + +//-------------------------------------------------------- +// Connection and signer management + +// waits to accept and sets a new connection. +// connection is closed in OnStop. +// returns true if the listener is closed +// (ie. it returns a nil conn). +func (ve *SignerValidatorEndpoint) reset() (closed bool, err error) { + ve.mtx.Lock() + defer ve.mtx.Unlock() + + // first check if the conn already exists and close it. + if ve.signer != nil { + if tmpErr := ve.signer.Close(); tmpErr != nil { + ve.Logger.Error("error closing socket val connection during reset", "err", tmpErr) + } + } + + // wait for a new conn + conn, err := ve.acceptConnection() + if err != nil { + return false, err + } + + // listener is closed + if conn == nil { + return true, nil + } + + ve.signer, err = NewSignerRemote(conn) + if err != nil { + // failed to fetch the pubkey. close out the connection. + if tmpErr := conn.Close(); tmpErr != nil { + ve.Logger.Error("error closing connection", "err", tmpErr) + } + return false, err + } + return false, nil +} + +// Attempt to accept a connection. +// Times out after the listener's timeoutAccept +func (ve *SignerValidatorEndpoint) acceptConnection() (net.Conn, error) { + conn, err := ve.listener.Accept() + if err != nil { + if !ve.IsRunning() { + return nil, nil // Ignore error from listener closing. + } + return nil, err + } + return conn, nil +} diff --git a/privval/signer_validator_endpoint_test.go b/privval/signer_validator_endpoint_test.go new file mode 100644 index 000000000..bf4c29930 --- /dev/null +++ b/privval/signer_validator_endpoint_test.go @@ -0,0 +1,505 @@ +package privval + +import ( + "fmt" + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + + "github.com/tendermint/tendermint/types" +) + +var ( + testTimeoutAccept = defaultTimeoutAcceptSeconds * time.Second + + testTimeoutReadWrite = 100 * time.Millisecond + testTimeoutReadWrite2o3 = 66 * time.Millisecond // 2/3 of the other one + + testTimeoutHeartbeat = 10 * time.Millisecond + testTimeoutHeartbeat3o2 = 6 * time.Millisecond // 3/2 of the other one +) + +type socketTestCase struct { + addr string + dialer SocketDialer +} + +func socketTestCases(t *testing.T) []socketTestCase { + tcpAddr := fmt.Sprintf("tcp://%s", testFreeTCPAddr(t)) + unixFilePath, err := testUnixAddr() + require.NoError(t, err) + unixAddr := fmt.Sprintf("unix://%s", unixFilePath) + return []socketTestCase{ + { + addr: tcpAddr, + dialer: DialTCPFn(tcpAddr, testTimeoutReadWrite, ed25519.GenPrivKey()), + }, + { + addr: unixAddr, + dialer: DialUnixFn(unixFilePath), + }, + } +} + +func TestSocketPVAddress(t *testing.T) { + for _, tc := range socketTestCases(t) { + // Execute the test within a closure to ensure the deferred statements + // are called between each for loop iteration, for isolated test cases. + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair(t, chainID, types.NewMockPV(), tc.addr, tc.dialer) + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + serviceAddr := serviceEndpoint.privVal.GetPubKey().Address() + validatorAddr := validatorEndpoint.GetPubKey().Address() + + assert.Equal(t, serviceAddr, validatorAddr) + }() + } +} + +func TestSocketPVPubKey(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewMockPV(), + tc.addr, + tc.dialer) + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + clientKey := validatorEndpoint.GetPubKey() + privvalPubKey := serviceEndpoint.privVal.GetPubKey() + + assert.Equal(t, privvalPubKey, clientKey) + }() + } +} + +func TestSocketPVProposal(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewMockPV(), + tc.addr, + tc.dialer) + + ts = time.Now() + privProposal = &types.Proposal{Timestamp: ts} + clientProposal = &types.Proposal{Timestamp: ts} + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + require.NoError(t, serviceEndpoint.privVal.SignProposal(chainID, privProposal)) + require.NoError(t, validatorEndpoint.SignProposal(chainID, clientProposal)) + + assert.Equal(t, privProposal.Signature, clientProposal.Signature) + }() + } +} + +func TestSocketPVVote(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewMockPV(), + tc.addr, + tc.dialer) + + ts = time.Now() + vType = types.PrecommitType + want = &types.Vote{Timestamp: ts, Type: vType} + have = &types.Vote{Timestamp: ts, Type: vType} + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + require.NoError(t, serviceEndpoint.privVal.SignVote(chainID, want)) + require.NoError(t, validatorEndpoint.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + }() + } +} + +func TestSocketPVVoteResetDeadline(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewMockPV(), + tc.addr, + tc.dialer) + + ts = time.Now() + vType = types.PrecommitType + want = &types.Vote{Timestamp: ts, Type: vType} + have = &types.Vote{Timestamp: ts, Type: vType} + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + time.Sleep(testTimeoutReadWrite2o3) + + require.NoError(t, serviceEndpoint.privVal.SignVote(chainID, want)) + require.NoError(t, validatorEndpoint.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + + // This would exceed the deadline if it was not extended by the previous message + time.Sleep(testTimeoutReadWrite2o3) + + require.NoError(t, serviceEndpoint.privVal.SignVote(chainID, want)) + require.NoError(t, validatorEndpoint.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + }() + } +} + +func TestSocketPVVoteKeepalive(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewMockPV(), + tc.addr, + tc.dialer) + + ts = time.Now() + vType = types.PrecommitType + want = &types.Vote{Timestamp: ts, Type: vType} + have = &types.Vote{Timestamp: ts, Type: vType} + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + time.Sleep(testTimeoutReadWrite * 2) + + require.NoError(t, serviceEndpoint.privVal.SignVote(chainID, want)) + require.NoError(t, validatorEndpoint.SignVote(chainID, have)) + assert.Equal(t, want.Signature, have.Signature) + }() + } +} + +func TestSocketPVDeadline(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + listenc = make(chan struct{}) + thisConnTimeout = 100 * time.Millisecond + validatorEndpoint = newSignerValidatorEndpoint(log.TestingLogger(), tc.addr, thisConnTimeout) + ) + + go func(sc *SignerValidatorEndpoint) { + defer close(listenc) + + // Note: the TCP connection times out at the accept() phase, + // whereas the Unix domain sockets connection times out while + // attempting to fetch the remote signer's public key. + assert.True(t, IsConnTimeout(sc.Start())) + + assert.False(t, sc.IsRunning()) + }(validatorEndpoint) + + for { + _, err := cmn.Connect(tc.addr) + if err == nil { + break + } + } + + <-listenc + }() + } +} + +func TestRemoteSignVoteErrors(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewErroringMockPV(), + tc.addr, + tc.dialer) + + ts = time.Now() + vType = types.PrecommitType + vote = &types.Vote{Timestamp: ts, Type: vType} + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + err := validatorEndpoint.SignVote("", vote) + require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) + + err = serviceEndpoint.privVal.SignVote(chainID, vote) + require.Error(t, err) + err = validatorEndpoint.SignVote(chainID, vote) + require.Error(t, err) + }() + } +} + +func TestRemoteSignProposalErrors(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + chainID = cmn.RandStr(12) + validatorEndpoint, serviceEndpoint = testSetupSocketPair( + t, + chainID, + types.NewErroringMockPV(), + tc.addr, + tc.dialer) + + ts = time.Now() + proposal = &types.Proposal{Timestamp: ts} + ) + defer validatorEndpoint.Stop() + defer serviceEndpoint.Stop() + + err := validatorEndpoint.SignProposal("", proposal) + require.Equal(t, err.(*RemoteSignerError).Description, types.ErroringMockPVErr.Error()) + + err = serviceEndpoint.privVal.SignProposal(chainID, proposal) + require.Error(t, err) + + err = validatorEndpoint.SignProposal(chainID, proposal) + require.Error(t, err) + }() + } +} + +func TestErrUnexpectedResponse(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + logger = log.TestingLogger() + chainID = cmn.RandStr(12) + readyCh = make(chan struct{}) + errCh = make(chan error, 1) + + serviceEndpoint = NewSignerServiceEndpoint( + logger, + chainID, + types.NewMockPV(), + tc.dialer, + ) + + validatorEndpoint = newSignerValidatorEndpoint( + logger, + tc.addr, + testTimeoutReadWrite) + ) + + testStartEndpoint(t, readyCh, validatorEndpoint) + defer validatorEndpoint.Stop() + SignerServiceEndpointTimeoutReadWrite(time.Millisecond)(serviceEndpoint) + SignerServiceEndpointConnRetries(100)(serviceEndpoint) + // we do not want to Start() the remote signer here and instead use the connection to + // reply with intentionally wrong replies below: + rsConn, err := serviceEndpoint.connect() + defer rsConn.Close() + require.NoError(t, err) + require.NotNil(t, rsConn) + // send over public key to get the remote signer running: + go testReadWriteResponse(t, &PubKeyResponse{}, rsConn) + <-readyCh + + // Proposal: + go func(errc chan error) { + errc <- validatorEndpoint.SignProposal(chainID, &types.Proposal{}) + }(errCh) + + // read request and write wrong response: + go testReadWriteResponse(t, &SignedVoteResponse{}, rsConn) + err = <-errCh + require.Error(t, err) + require.Equal(t, err, ErrUnexpectedResponse) + + // Vote: + go func(errc chan error) { + errc <- validatorEndpoint.SignVote(chainID, &types.Vote{}) + }(errCh) + // read request and write wrong response: + go testReadWriteResponse(t, &SignedProposalResponse{}, rsConn) + err = <-errCh + require.Error(t, err) + require.Equal(t, err, ErrUnexpectedResponse) + }() + } +} + +func TestRetryConnToRemoteSigner(t *testing.T) { + for _, tc := range socketTestCases(t) { + func() { + var ( + logger = log.TestingLogger() + chainID = cmn.RandStr(12) + readyCh = make(chan struct{}) + + serviceEndpoint = NewSignerServiceEndpoint( + logger, + chainID, + types.NewMockPV(), + tc.dialer, + ) + thisConnTimeout = testTimeoutReadWrite + validatorEndpoint = newSignerValidatorEndpoint(logger, tc.addr, thisConnTimeout) + ) + // Ping every: + SignerValidatorEndpointSetHeartbeat(testTimeoutHeartbeat)(validatorEndpoint) + + SignerServiceEndpointTimeoutReadWrite(testTimeoutReadWrite)(serviceEndpoint) + SignerServiceEndpointConnRetries(10)(serviceEndpoint) + + testStartEndpoint(t, readyCh, validatorEndpoint) + defer validatorEndpoint.Stop() + require.NoError(t, serviceEndpoint.Start()) + assert.True(t, serviceEndpoint.IsRunning()) + + <-readyCh + time.Sleep(testTimeoutHeartbeat * 2) + + serviceEndpoint.Stop() + rs2 := NewSignerServiceEndpoint( + logger, + chainID, + types.NewMockPV(), + tc.dialer, + ) + // let some pings pass + time.Sleep(testTimeoutHeartbeat3o2) + require.NoError(t, rs2.Start()) + assert.True(t, rs2.IsRunning()) + defer rs2.Stop() + + // give the client some time to re-establish the conn to the remote signer + // should see sth like this in the logs: + // + // E[10016-01-10|17:12:46.128] Ping err="remote signer timed out" + // I[10016-01-10|17:16:42.447] Re-created connection to remote signer impl=SocketVal + time.Sleep(testTimeoutReadWrite * 2) + }() + } +} + +func newSignerValidatorEndpoint(logger log.Logger, addr string, timeoutReadWrite time.Duration) *SignerValidatorEndpoint { + proto, address := cmn.ProtocolAndAddress(addr) + + ln, err := net.Listen(proto, address) + logger.Info("Listening at", "proto", proto, "address", address) + if err != nil { + panic(err) + } + + var listener net.Listener + + if proto == "unix" { + unixLn := NewUnixListener(ln) + UnixListenerTimeoutAccept(testTimeoutAccept)(unixLn) + UnixListenerTimeoutReadWrite(timeoutReadWrite)(unixLn) + listener = unixLn + } else { + tcpLn := NewTCPListener(ln, ed25519.GenPrivKey()) + TCPListenerTimeoutAccept(testTimeoutAccept)(tcpLn) + TCPListenerTimeoutReadWrite(timeoutReadWrite)(tcpLn) + listener = tcpLn + } + + return NewSignerValidatorEndpoint(logger, listener) +} + +func testSetupSocketPair( + t *testing.T, + chainID string, + privValidator types.PrivValidator, + addr string, + socketDialer SocketDialer, +) (*SignerValidatorEndpoint, *SignerServiceEndpoint) { + var ( + logger = log.TestingLogger() + privVal = privValidator + readyc = make(chan struct{}) + serviceEndpoint = NewSignerServiceEndpoint( + logger, + chainID, + privVal, + socketDialer, + ) + + thisConnTimeout = testTimeoutReadWrite + validatorEndpoint = newSignerValidatorEndpoint(logger, addr, thisConnTimeout) + ) + + SignerValidatorEndpointSetHeartbeat(testTimeoutHeartbeat)(validatorEndpoint) + SignerServiceEndpointTimeoutReadWrite(testTimeoutReadWrite)(serviceEndpoint) + SignerServiceEndpointConnRetries(1e6)(serviceEndpoint) + + testStartEndpoint(t, readyc, validatorEndpoint) + + require.NoError(t, serviceEndpoint.Start()) + assert.True(t, serviceEndpoint.IsRunning()) + + <-readyc + + return validatorEndpoint, serviceEndpoint +} + +func testReadWriteResponse(t *testing.T, resp RemoteSignerMsg, rsConn net.Conn) { + _, err := readMsg(rsConn) + require.NoError(t, err) + + err = writeMsg(rsConn, resp) + require.NoError(t, err) +} + +func testStartEndpoint(t *testing.T, readyCh chan struct{}, sc *SignerValidatorEndpoint) { + go func(sc *SignerValidatorEndpoint) { + require.NoError(t, sc.Start()) + assert.True(t, sc.IsRunning()) + + readyCh <- struct{}{} + }(sc) +} + +// testFreeTCPAddr claims a free port so we don't block on listener being ready. +func testFreeTCPAddr(t *testing.T) string { + ln, err := net.Listen("tcp", "127.0.0.1:0") + require.NoError(t, err) + defer ln.Close() + + return fmt.Sprintf("127.0.0.1:%d", ln.Addr().(*net.TCPAddr).Port) +} diff --git a/privval/socket_dialers.go b/privval/socket_dialers.go new file mode 100644 index 000000000..c92a1c8cc --- /dev/null +++ b/privval/socket_dialers.go @@ -0,0 +1,43 @@ +package privval + +import ( + "net" + "time" + + "github.com/pkg/errors" + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + p2pconn "github.com/tendermint/tendermint/p2p/conn" +) + +// Socket errors. +var ( + ErrDialRetryMax = errors.New("dialed maximum retries") +) + +// SocketDialer dials a remote address and returns a net.Conn or an error. +type SocketDialer func() (net.Conn, error) + +// DialTCPFn dials the given tcp addr, using the given timeoutReadWrite and +// privKey for the authenticated encryption handshake. +func DialTCPFn(addr string, timeoutReadWrite time.Duration, privKey ed25519.PrivKeyEd25519) SocketDialer { + return func() (net.Conn, error) { + conn, err := cmn.Connect(addr) + if err == nil { + deadline := time.Now().Add(timeoutReadWrite) + err = conn.SetDeadline(deadline) + } + if err == nil { + conn, err = p2pconn.MakeSecretConnection(conn, privKey) + } + return conn, err + } +} + +// DialUnixFn dials the given unix socket. +func DialUnixFn(addr string) SocketDialer { + return func() (net.Conn, error) { + unixAddr := &net.UnixAddr{Name: addr, Net: "unix"} + return net.DialUnix("unix", nil, unixAddr) + } +} diff --git a/privval/socket_dialers_test.go b/privval/socket_dialers_test.go new file mode 100644 index 000000000..9d5d5cc2b --- /dev/null +++ b/privval/socket_dialers_test.go @@ -0,0 +1,26 @@ +package privval + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" +) + +func TestIsConnTimeoutForFundamentalTimeouts(t *testing.T) { + // Generate a networking timeout + dialer := DialTCPFn(testFreeTCPAddr(t), time.Millisecond, ed25519.GenPrivKey()) + _, err := dialer() + assert.Error(t, err) + assert.True(t, IsConnTimeout(err)) +} + +func TestIsConnTimeoutForWrappedConnTimeouts(t *testing.T) { + dialer := DialTCPFn(testFreeTCPAddr(t), time.Millisecond, ed25519.GenPrivKey()) + _, err := dialer() + assert.Error(t, err) + err = cmn.ErrorWrap(ErrConnTimeout, err.Error()) + assert.True(t, IsConnTimeout(err)) +} diff --git a/privval/socket.go b/privval/socket_listeners.go similarity index 60% rename from privval/socket.go rename to privval/socket_listeners.go index bd9cd9209..7c8835791 100644 --- a/privval/socket.go +++ b/privval/socket_listeners.go @@ -9,8 +9,8 @@ import ( ) const ( - defaultAcceptDeadlineSeconds = 3 - defaultConnDeadlineSeconds = 3 + defaultTimeoutAcceptSeconds = 3 + defaultTimeoutReadWriteSeconds = 3 ) // timeoutError can be used to check if an error returned from the netp package @@ -25,16 +25,16 @@ type timeoutError interface { // TCPListenerOption sets an optional parameter on the tcpListener. type TCPListenerOption func(*tcpListener) -// TCPListenerAcceptDeadline sets the deadline for the listener. -// A zero time value disables the deadline. -func TCPListenerAcceptDeadline(deadline time.Duration) TCPListenerOption { - return func(tl *tcpListener) { tl.acceptDeadline = deadline } +// TCPListenerTimeoutAccept sets the timeout for the listener. +// A zero time value disables the timeout. +func TCPListenerTimeoutAccept(timeout time.Duration) TCPListenerOption { + return func(tl *tcpListener) { tl.timeoutAccept = timeout } } -// TCPListenerConnDeadline sets the read and write deadline for connections +// TCPListenerTimeoutReadWrite sets the read and write timeout for connections // from external signing processes. -func TCPListenerConnDeadline(deadline time.Duration) TCPListenerOption { - return func(tl *tcpListener) { tl.connDeadline = deadline } +func TCPListenerTimeoutReadWrite(timeout time.Duration) TCPListenerOption { + return func(tl *tcpListener) { tl.timeoutReadWrite = timeout } } // tcpListener implements net.Listener. @@ -47,24 +47,25 @@ type tcpListener struct { secretConnKey ed25519.PrivKeyEd25519 - acceptDeadline time.Duration - connDeadline time.Duration + timeoutAccept time.Duration + timeoutReadWrite time.Duration } // NewTCPListener returns a listener that accepts authenticated encrypted connections // using the given secretConnKey and the default timeout values. func NewTCPListener(ln net.Listener, secretConnKey ed25519.PrivKeyEd25519) *tcpListener { return &tcpListener{ - TCPListener: ln.(*net.TCPListener), - secretConnKey: secretConnKey, - acceptDeadline: time.Second * defaultAcceptDeadlineSeconds, - connDeadline: time.Second * defaultConnDeadlineSeconds, + TCPListener: ln.(*net.TCPListener), + secretConnKey: secretConnKey, + timeoutAccept: time.Second * defaultTimeoutAcceptSeconds, + timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds, } } // Accept implements net.Listener. func (ln *tcpListener) Accept() (net.Conn, error) { - err := ln.SetDeadline(time.Now().Add(ln.acceptDeadline)) + deadline := time.Now().Add(ln.timeoutAccept) + err := ln.SetDeadline(deadline) if err != nil { return nil, err } @@ -75,7 +76,7 @@ func (ln *tcpListener) Accept() (net.Conn, error) { } // Wrap the conn in our timeout and encryption wrappers - timeoutConn := newTimeoutConn(tc, ln.connDeadline) + timeoutConn := newTimeoutConn(tc, ln.timeoutReadWrite) secretConn, err := p2pconn.MakeSecretConnection(timeoutConn, ln.secretConnKey) if err != nil { return nil, err @@ -92,16 +93,16 @@ var _ net.Listener = (*unixListener)(nil) type UnixListenerOption func(*unixListener) -// UnixListenerAcceptDeadline sets the deadline for the listener. -// A zero time value disables the deadline. -func UnixListenerAcceptDeadline(deadline time.Duration) UnixListenerOption { - return func(ul *unixListener) { ul.acceptDeadline = deadline } +// UnixListenerTimeoutAccept sets the timeout for the listener. +// A zero time value disables the timeout. +func UnixListenerTimeoutAccept(timeout time.Duration) UnixListenerOption { + return func(ul *unixListener) { ul.timeoutAccept = timeout } } -// UnixListenerConnDeadline sets the read and write deadline for connections +// UnixListenerTimeoutReadWrite sets the read and write timeout for connections // from external signing processes. -func UnixListenerConnDeadline(deadline time.Duration) UnixListenerOption { - return func(ul *unixListener) { ul.connDeadline = deadline } +func UnixListenerTimeoutReadWrite(timeout time.Duration) UnixListenerOption { + return func(ul *unixListener) { ul.timeoutReadWrite = timeout } } // unixListener wraps a *net.UnixListener to standardise protocol timeouts @@ -109,23 +110,24 @@ func UnixListenerConnDeadline(deadline time.Duration) UnixListenerOption { type unixListener struct { *net.UnixListener - acceptDeadline time.Duration - connDeadline time.Duration + timeoutAccept time.Duration + timeoutReadWrite time.Duration } // NewUnixListener returns a listener that accepts unencrypted connections // using the default timeout values. func NewUnixListener(ln net.Listener) *unixListener { return &unixListener{ - UnixListener: ln.(*net.UnixListener), - acceptDeadline: time.Second * defaultAcceptDeadlineSeconds, - connDeadline: time.Second * defaultConnDeadlineSeconds, + UnixListener: ln.(*net.UnixListener), + timeoutAccept: time.Second * defaultTimeoutAcceptSeconds, + timeoutReadWrite: time.Second * defaultTimeoutReadWriteSeconds, } } // Accept implements net.Listener. func (ln *unixListener) Accept() (net.Conn, error) { - err := ln.SetDeadline(time.Now().Add(ln.acceptDeadline)) + deadline := time.Now().Add(ln.timeoutAccept) + err := ln.SetDeadline(deadline) if err != nil { return nil, err } @@ -136,7 +138,7 @@ func (ln *unixListener) Accept() (net.Conn, error) { } // Wrap the conn in our timeout wrapper - conn := newTimeoutConn(tc, ln.connDeadline) + conn := newTimeoutConn(tc, ln.timeoutReadWrite) // TODO: wrap in something that authenticates // with a MAC - https://github.com/tendermint/tendermint/issues/3099 @@ -153,24 +155,25 @@ var _ net.Conn = (*timeoutConn)(nil) // timeoutConn wraps a net.Conn to standardise protocol timeouts / deadline resets. type timeoutConn struct { net.Conn - - connDeadline time.Duration + timeout time.Duration } // newTimeoutConn returns an instance of timeoutConn. -func newTimeoutConn( - conn net.Conn, - connDeadline time.Duration) *timeoutConn { +func newTimeoutConn(conn net.Conn, timeout time.Duration) *timeoutConn { return &timeoutConn{ conn, - connDeadline, + timeout, } } // Read implements net.Conn. func (c timeoutConn) Read(b []byte) (n int, err error) { // Reset deadline - c.Conn.SetReadDeadline(time.Now().Add(c.connDeadline)) + deadline := time.Now().Add(c.timeout) + err = c.Conn.SetReadDeadline(deadline) + if err != nil { + return + } return c.Conn.Read(b) } @@ -178,7 +181,11 @@ func (c timeoutConn) Read(b []byte) (n int, err error) { // Write implements net.Conn. func (c timeoutConn) Write(b []byte) (n int, err error) { // Reset deadline - c.Conn.SetWriteDeadline(time.Now().Add(c.connDeadline)) + deadline := time.Now().Add(c.timeout) + err = c.Conn.SetWriteDeadline(deadline) + if err != nil { + return + } return c.Conn.Write(b) } diff --git a/privval/socket_test.go b/privval/socket_listeners_test.go similarity index 71% rename from privval/socket_test.go rename to privval/socket_listeners_test.go index 7f7bbd892..498ef79c0 100644 --- a/privval/socket_test.go +++ b/privval/socket_listeners_test.go @@ -23,7 +23,7 @@ func newPrivKey() ed25519.PrivKeyEd25519 { type listenerTestCase struct { description string // For test reporting purposes. listener net.Listener - dialer Dialer + dialer SocketDialer } // testUnixAddr will attempt to obtain a platform-independent temporary file @@ -39,23 +39,23 @@ func testUnixAddr() (string, error) { return addr, nil } -func tcpListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { +func tcpListenerTestCase(t *testing.T, timeoutAccept, timeoutReadWrite time.Duration) listenerTestCase { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { t.Fatal(err) } tcpLn := NewTCPListener(ln, newPrivKey()) - TCPListenerAcceptDeadline(acceptDeadline)(tcpLn) - TCPListenerConnDeadline(connectDeadline)(tcpLn) + TCPListenerTimeoutAccept(timeoutAccept)(tcpLn) + TCPListenerTimeoutReadWrite(timeoutReadWrite)(tcpLn) return listenerTestCase{ description: "TCP", listener: tcpLn, - dialer: DialTCPFn(ln.Addr().String(), testConnDeadline, newPrivKey()), + dialer: DialTCPFn(ln.Addr().String(), testTimeoutReadWrite, newPrivKey()), } } -func unixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Duration) listenerTestCase { +func unixListenerTestCase(t *testing.T, timeoutAccept, timeoutReadWrite time.Duration) listenerTestCase { addr, err := testUnixAddr() if err != nil { t.Fatal(err) @@ -66,8 +66,8 @@ func unixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Dur } unixLn := NewUnixListener(ln) - UnixListenerAcceptDeadline(acceptDeadline)(unixLn) - UnixListenerConnDeadline(connectDeadline)(unixLn) + UnixListenerTimeoutAccept(timeoutAccept)(unixLn) + UnixListenerTimeoutReadWrite(timeoutReadWrite)(unixLn) return listenerTestCase{ description: "Unix", listener: unixLn, @@ -75,14 +75,14 @@ func unixListenerTestCase(t *testing.T, acceptDeadline, connectDeadline time.Dur } } -func listenerTestCases(t *testing.T, acceptDeadline, connectDeadline time.Duration) []listenerTestCase { +func listenerTestCases(t *testing.T, timeoutAccept, timeoutReadWrite time.Duration) []listenerTestCase { return []listenerTestCase{ - tcpListenerTestCase(t, acceptDeadline, connectDeadline), - unixListenerTestCase(t, acceptDeadline, connectDeadline), + tcpListenerTestCase(t, timeoutAccept, timeoutReadWrite), + unixListenerTestCase(t, timeoutAccept, timeoutReadWrite), } } -func TestListenerAcceptDeadlines(t *testing.T) { +func TestListenerTimeoutAccept(t *testing.T) { for _, tc := range listenerTestCases(t, time.Millisecond, time.Second) { _, err := tc.listener.Accept() opErr, ok := err.(*net.OpError) @@ -96,9 +96,9 @@ func TestListenerAcceptDeadlines(t *testing.T) { } } -func TestListenerConnectDeadlines(t *testing.T) { +func TestListenerTimeoutReadWrite(t *testing.T) { for _, tc := range listenerTestCases(t, time.Second, time.Millisecond) { - go func(dialer Dialer) { + go func(dialer SocketDialer) { _, err := dialer() if err != nil { panic(err) diff --git a/privval/utils.go b/privval/utils.go new file mode 100644 index 000000000..d8837bdf0 --- /dev/null +++ b/privval/utils.go @@ -0,0 +1,20 @@ +package privval + +import ( + cmn "github.com/tendermint/tendermint/libs/common" +) + +// IsConnTimeout returns a boolean indicating whether the error is known to +// report that a connection timeout occurred. This detects both fundamental +// network timeouts, as well as ErrConnTimeout errors. +func IsConnTimeout(err error) bool { + if cmnErr, ok := err.(cmn.Error); ok { + if cmnErr.Data() == ErrConnTimeout { + return true + } + } + if _, ok := err.(timeoutError); ok { + return true + } + return false +} diff --git a/privval/utils_test.go b/privval/utils_test.go new file mode 100644 index 000000000..23f6f6a3b --- /dev/null +++ b/privval/utils_test.go @@ -0,0 +1,14 @@ +package privval + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + cmn "github.com/tendermint/tendermint/libs/common" +) + +func TestIsConnTimeoutForNonTimeoutErrors(t *testing.T) { + assert.False(t, IsConnTimeout(cmn.ErrorWrap(ErrDialRetryMax, "max retries exceeded"))) + assert.False(t, IsConnTimeout(fmt.Errorf("completely irrelevant error"))) +} diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index b961f2384..005489133 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -49,7 +49,7 @@ var _ error = (*TestHarnessError)(nil) // with this version of Tendermint. type TestHarness struct { addr string - spv *privval.SocketVal + spv *privval.SignerValidatorEndpoint fpv *privval.FilePV chainID string acceptRetries int @@ -314,7 +314,7 @@ func (th *TestHarness) Shutdown(err error) { // newTestHarnessSocketVal creates our client instance which we will use for // testing. -func newTestHarnessSocketVal(logger log.Logger, cfg TestHarnessConfig) (*privval.SocketVal, error) { +func newTestHarnessSocketVal(logger log.Logger, cfg TestHarnessConfig) (*privval.SignerValidatorEndpoint, error) { proto, addr := cmn.ProtocolAndAddress(cfg.BindAddr) if proto == "unix" { // make sure the socket doesn't exist - if so, try to delete it @@ -334,20 +334,20 @@ func newTestHarnessSocketVal(logger log.Logger, cfg TestHarnessConfig) (*privval switch proto { case "unix": unixLn := privval.NewUnixListener(ln) - privval.UnixListenerAcceptDeadline(cfg.AcceptDeadline)(unixLn) - privval.UnixListenerConnDeadline(cfg.ConnDeadline)(unixLn) + privval.UnixListenerTimeoutAccept(cfg.AcceptDeadline)(unixLn) + privval.UnixListenerTimeoutReadWrite(cfg.ConnDeadline)(unixLn) svln = unixLn case "tcp": tcpLn := privval.NewTCPListener(ln, cfg.SecretConnKey) - privval.TCPListenerAcceptDeadline(cfg.AcceptDeadline)(tcpLn) - privval.TCPListenerConnDeadline(cfg.ConnDeadline)(tcpLn) + privval.TCPListenerTimeoutAccept(cfg.AcceptDeadline)(tcpLn) + privval.TCPListenerTimeoutReadWrite(cfg.ConnDeadline)(tcpLn) logger.Info("Resolved TCP address for listener", "addr", tcpLn.Addr()) svln = tcpLn default: logger.Error("Unsupported protocol (must be unix:// or tcp://)", "proto", proto) return nil, newTestHarnessError(ErrInvalidParameters, nil, fmt.Sprintf("Unsupported protocol: %s", proto)) } - return privval.NewSocketVal(logger, svln), nil + return privval.NewSignerValidatorEndpoint(logger, svln), nil } func newTestHarnessError(code int, err error, info string) *TestHarnessError { diff --git a/tools/tm-signer-harness/internal/test_harness_test.go b/tools/tm-signer-harness/internal/test_harness_test.go index 804aca45e..adb818b0b 100644 --- a/tools/tm-signer-harness/internal/test_harness_test.go +++ b/tools/tm-signer-harness/internal/test_harness_test.go @@ -84,7 +84,7 @@ func TestRemoteSignerTestHarnessMaxAcceptRetriesReached(t *testing.T) { func TestRemoteSignerTestHarnessSuccessfulRun(t *testing.T) { harnessTest( t, - func(th *TestHarness) *privval.RemoteSigner { + func(th *TestHarness) *privval.SignerServiceEndpoint { return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, false, false) }, NoError, @@ -94,7 +94,7 @@ func TestRemoteSignerTestHarnessSuccessfulRun(t *testing.T) { func TestRemoteSignerPublicKeyCheckFailed(t *testing.T) { harnessTest( t, - func(th *TestHarness) *privval.RemoteSigner { + func(th *TestHarness) *privval.SignerServiceEndpoint { return newMockRemoteSigner(t, th, ed25519.GenPrivKey(), false, false) }, ErrTestPublicKeyFailed, @@ -104,7 +104,7 @@ func TestRemoteSignerPublicKeyCheckFailed(t *testing.T) { func TestRemoteSignerProposalSigningFailed(t *testing.T) { harnessTest( t, - func(th *TestHarness) *privval.RemoteSigner { + func(th *TestHarness) *privval.SignerServiceEndpoint { return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, true, false) }, ErrTestSignProposalFailed, @@ -114,15 +114,15 @@ func TestRemoteSignerProposalSigningFailed(t *testing.T) { func TestRemoteSignerVoteSigningFailed(t *testing.T) { harnessTest( t, - func(th *TestHarness) *privval.RemoteSigner { + func(th *TestHarness) *privval.SignerServiceEndpoint { return newMockRemoteSigner(t, th, th.fpv.Key.PrivKey, false, true) }, ErrTestSignVoteFailed, ) } -func newMockRemoteSigner(t *testing.T, th *TestHarness, privKey crypto.PrivKey, breakProposalSigning bool, breakVoteSigning bool) *privval.RemoteSigner { - return privval.NewRemoteSigner( +func newMockRemoteSigner(t *testing.T, th *TestHarness, privKey crypto.PrivKey, breakProposalSigning bool, breakVoteSigning bool) *privval.SignerServiceEndpoint { + return privval.NewSignerServiceEndpoint( th.logger, th.chainID, types.NewMockPVWithParams(privKey, breakProposalSigning, breakVoteSigning), @@ -135,7 +135,7 @@ func newMockRemoteSigner(t *testing.T, th *TestHarness, privKey crypto.PrivKey, } // For running relatively standard tests. -func harnessTest(t *testing.T, rsMaker func(th *TestHarness) *privval.RemoteSigner, expectedExitCode int) { +func harnessTest(t *testing.T, rsMaker func(th *TestHarness) *privval.SignerServiceEndpoint, expectedExitCode int) { cfg := makeConfig(t, 100, 3) defer cleanup(cfg) From 37a548414bbb5dc8dce9fe452ba5f933fad29684 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Sat, 2 Mar 2019 07:06:57 +0100 Subject: [PATCH 075/259] docs: fix typo (#3373) --- docs/tendermint-core/running-in-production.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index fb98626ad..1ec792831 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -2,7 +2,7 @@ ## Database -By default, Tendermint uses the `syndtr/goleveldb` package for it's in-process +By default, Tendermint uses the `syndtr/goleveldb` package for its in-process key-value database. Unfortunately, this implementation of LevelDB seems to suffer under heavy load (see [#226](https://github.com/syndtr/goleveldb/issues/226)). It may be best to install the real C-implementation of LevelDB and compile Tendermint to use From d95894152beb3b25423cdb109af006340f0ac80b Mon Sep 17 00:00:00 2001 From: zjubfd Date: Sun, 3 Mar 2019 04:17:37 +0800 Subject: [PATCH 076/259] fix dirty data in peerset,resolve #3304 (#3359) * fix dirty data in peerset startInitPeer before PeerSet add the peer, once mconnection start and Receive of one Reactor faild, will try to remove it from PeerSet while PeerSet still not contain the peer. Fix this by change the order. * fix test FilterDuplicate * fix start/stop race * fix err --- libs/pubsub/pubsub.go | 2 ++ p2p/switch.go | 45 ++++++++++++++++++------------------------- p2p/switch_test.go | 10 ++++++---- 3 files changed, 27 insertions(+), 30 deletions(-) diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index 6e86a63c9..8d4d1fb05 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -88,6 +88,8 @@ type Server struct { cmds chan cmd cmdsCap int + // check if we have subscription before + // subscribing or unsubscribing mtx sync.RWMutex subscriptions map[string]map[string]struct{} // subscriber -> query (string) -> empty struct } diff --git a/p2p/switch.go b/p2p/switch.go index 7d2e6c3f3..ccd6d40f2 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -645,43 +645,36 @@ func (sw *Switch) addPeer(p Peer) error { // Handle the shut down case where the switch has stopped but we're // concurrently trying to add a peer. - if sw.IsRunning() { - // All good. Start peer - if err := sw.startInitPeer(p); err != nil { - return err - } - } else { + if !sw.IsRunning() { + // XXX should this return an error or just log and terminate? sw.Logger.Error("Won't start a peer - switch is not running", "peer", p) + return nil } - // Add the peer to .peers. - // We start it first so that a peer in the list is safe to Stop. - // It should not err since we already checked peers.Has(). + // Start the peer's send/recv routines. + // Must start it before adding it to the peer set + // to prevent Start and Stop from being called concurrently. + err := p.Start() + if err != nil { + // Should never happen + sw.Logger.Error("Error starting peer", "err", err, "peer", p) + return err + } + + // Add the peer to PeerSet. Do this before starting the reactors + // so that if Receive errors, we will find the peer and remove it. + // Add should not err since we already checked peers.Has(). if err := sw.peers.Add(p); err != nil { return err } - - sw.Logger.Info("Added peer", "peer", p) sw.metrics.Peers.Add(float64(1)) - return nil -} - -func (sw *Switch) startInitPeer(p Peer) error { - err := p.Start() // spawn send/recv routines - if err != nil { - // Should never happen - sw.Logger.Error( - "Error starting peer", - "err", err, - "peer", p, - ) - return err - } - + // Start all the reactor protocols on the peer. for _, reactor := range sw.reactors { reactor.AddPeer(p) } + sw.Logger.Info("Added peer", "peer", p) + return nil } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index 358661616..47cfed55f 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -273,6 +273,8 @@ func TestSwitchPeerFilterTimeout(t *testing.T) { func TestSwitchPeerFilterDuplicate(t *testing.T) { sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) + sw.Start() + defer sw.Stop() // simulate remote peer rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} @@ -293,12 +295,12 @@ func TestSwitchPeerFilterDuplicate(t *testing.T) { } err = sw.addPeer(p) - if err, ok := err.(ErrRejected); ok { - if !err.IsDuplicate() { - t.Errorf("expected peer to be duplicate") + if errRej, ok := err.(ErrRejected); ok { + if !errRej.IsDuplicate() { + t.Errorf("expected peer to be duplicate. got %v", errRej) } } else { - t.Errorf("expected ErrRejected") + t.Errorf("expected ErrRejected, got %v", err) } } From 976b1c2ef73e557a702ef17600e1eeaf4865f292 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Sun, 3 Mar 2019 04:21:21 +0800 Subject: [PATCH 077/259] fix pool timer leak bug, resolve#3353 (#3358) When remove peer, block pool simple remove bpPeer, but do not stop timer, that cause stopError for recorrected peers. Stop timer when remove from pool. --- blockchain/pool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/blockchain/pool.go b/blockchain/pool.go index 804a43250..2cb7dda96 100644 --- a/blockchain/pool.go +++ b/blockchain/pool.go @@ -299,6 +299,9 @@ func (pool *BlockPool) removePeer(peerID p2p.ID) { requester.redo(peerID) } } + if p, exist := pool.peers[peerID]; exist && p.timeout != nil { + p.timeout.Stop() + } delete(pool.peers, peerID) } From 3421e4dcd7c6937e9ae8f72af17b38840840a781 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Sun, 3 Mar 2019 05:36:52 +0800 Subject: [PATCH 078/259] make lightd availbe (#3364) 1."abci_query": rpcserver.NewRPCFunc(c.ABCIQuery, "path,data,prove") "validators": rpcserver.NewRPCFunc(c.Validators, "height"), the parameters and function do not match, cause index out of range error. 2. the prove of query is forced to be true, while default option is false. 3. fix the wrong key of merkle --- cmd/tendermint/commands/lite.go | 6 +++--- lite/proxy/proxy.go | 4 ++-- lite/proxy/query.go | 34 +++++++++++++++++++++++++++------ lite/proxy/wrapper.go | 4 ++++ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/cmd/tendermint/commands/lite.go b/cmd/tendermint/commands/lite.go index 94259cb0b..d3a4ac53e 100644 --- a/cmd/tendermint/commands/lite.go +++ b/cmd/tendermint/commands/lite.go @@ -43,7 +43,7 @@ func init() { LiteCmd.Flags().IntVar(&cacheSize, "cache-size", 10, "Specify the memory trust store cache size") } -func ensureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) { +func EnsureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) { u, err := url.Parse(addr) if err != nil { return "", err @@ -64,11 +64,11 @@ func runProxy(cmd *cobra.Command, args []string) error { // TODO: close up shop }) - nodeAddr, err := ensureAddrHasSchemeOrDefaultToTCP(nodeAddr) + nodeAddr, err := EnsureAddrHasSchemeOrDefaultToTCP(nodeAddr) if err != nil { return err } - listenAddr, err := ensureAddrHasSchemeOrDefaultToTCP(listenAddr) + listenAddr, err := EnsureAddrHasSchemeOrDefaultToTCP(listenAddr) if err != nil { return err } diff --git a/lite/proxy/proxy.go b/lite/proxy/proxy.go index d7ffb27d0..39baf5a48 100644 --- a/lite/proxy/proxy.go +++ b/lite/proxy/proxy.go @@ -66,7 +66,7 @@ func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc { "block": rpcserver.NewRPCFunc(c.Block, "height"), "commit": rpcserver.NewRPCFunc(c.Commit, "height"), "tx": rpcserver.NewRPCFunc(c.Tx, "hash,prove"), - "validators": rpcserver.NewRPCFunc(c.Validators, ""), + "validators": rpcserver.NewRPCFunc(c.Validators, "height"), // broadcast API "broadcast_tx_commit": rpcserver.NewRPCFunc(c.BroadcastTxCommit, "tx"), @@ -74,7 +74,7 @@ func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc { "broadcast_tx_async": rpcserver.NewRPCFunc(c.BroadcastTxAsync, "tx"), // abci API - "abci_query": rpcserver.NewRPCFunc(c.ABCIQuery, "path,data,prove"), + "abci_query": rpcserver.NewRPCFunc(c.ABCIQuery, "path,data"), "abci_info": rpcserver.NewRPCFunc(c.ABCIInfo, ""), } } diff --git a/lite/proxy/query.go b/lite/proxy/query.go index 3acf826b8..fd10e0bb6 100644 --- a/lite/proxy/query.go +++ b/lite/proxy/query.go @@ -2,6 +2,7 @@ package proxy import ( "fmt" + "strings" cmn "github.com/tendermint/tendermint/libs/common" @@ -43,11 +44,7 @@ func GetWithProof(prt *merkle.ProofRuntime, key []byte, reqHeight int64, node rp func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts rpcclient.ABCIQueryOptions, node rpcclient.Client, cert lite.Verifier) ( *ctypes.ResultABCIQuery, error) { - - if !opts.Prove { - return nil, cmn.NewError("require ABCIQueryOptions.Prove to be true") - } - + opts.Prove = true res, err := node.ABCIQueryWithOptions(path, key, opts) if err != nil { return nil, err @@ -77,7 +74,14 @@ func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts if resp.Value != nil { // Value exists // XXX How do we encode the key into a string... - err = prt.VerifyValue(resp.Proof, signedHeader.AppHash, string(resp.Key), resp.Value) + storeName, err := parseQueryStorePath(path) + if err != nil { + return nil, err + } + kp := merkle.KeyPath{} + kp = kp.AppendKey([]byte(storeName), merkle.KeyEncodingURL) + kp = kp.AppendKey(resp.Key, merkle.KeyEncodingURL) + err = prt.VerifyValue(resp.Proof, signedHeader.AppHash, kp.String(), resp.Value) if err != nil { return nil, cmn.ErrorWrap(err, "Couldn't verify value proof") } @@ -94,6 +98,24 @@ func GetWithProofOptions(prt *merkle.ProofRuntime, path string, key []byte, opts } } +func parseQueryStorePath(path string) (storeName string, err error) { + if !strings.HasPrefix(path, "/") { + return "", fmt.Errorf("expected path to start with /") + } + + paths := strings.SplitN(path[1:], "/", 3) + switch { + case len(paths) != 3: + return "", fmt.Errorf("expected format like /store//key") + case paths[0] != "store": + return "", fmt.Errorf("expected format like /store//key") + case paths[2] != "key": + return "", fmt.Errorf("expected format like /store//key") + } + + return paths[1], nil +} + // GetCertifiedCommit gets the signed header for a given height and certifies // it. Returns error if unable to get a proven header. func GetCertifiedCommit(h int64, client rpcclient.Client, cert lite.Verifier) (types.SignedHeader, error) { diff --git a/lite/proxy/wrapper.go b/lite/proxy/wrapper.go index 7ddb3b8ad..c90cdb275 100644 --- a/lite/proxy/wrapper.go +++ b/lite/proxy/wrapper.go @@ -145,6 +145,10 @@ func (w Wrapper) Commit(height *int64) (*ctypes.ResultCommit, error) { return res, err } +func (w Wrapper) RegisterOpDecoder(typ string, dec merkle.OpDecoder) { + w.prt.RegisterOpDecoder(typ, dec) +} + // // WrappedSwitch creates a websocket connection that auto-verifies any info // // coming through before passing it along. // // From 8a962ffc4639eed95501114e0436ccffc52f8ef1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 4 Mar 2019 12:17:38 +0400 Subject: [PATCH 079/259] deps: update gogo/protobuf from 1.1.1 to 1.2.1 and golang/protobuf from 1.1.0 to 1.3.0 (#3357) * deps: update gogo/proto from 1.1.1 to 1.2.1 - verified changes manually git diff 636bf030~ ba06b47c --stat -- ':!*.pb.go' ':!test' * deps: update golang/protobuf from 1.1.0 to 1.3.0 - verified changes manually git diff b4deda0~ c823c79 -- ':!*.pb.go' ':!test' --- Gopkg.lock | 12 +- Gopkg.toml | 4 +- abci/types/types.pb.go | 270 +++++++++++++++++++++++++++++++------ crypto/merkle/merkle.pb.go | 14 +- libs/common/types.pb.go | 21 +-- rpc/grpc/types.pb.go | 30 +++-- types/proto3/block.pb.go | 14 +- 7 files changed, 286 insertions(+), 79 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 9e3d5d8a4..f385e2559 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -91,7 +91,7 @@ version = "v1.8.0" [[projects]] - digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e" + digest = "1:95e1006e41c641abd2f365dfa0f1213c04da294e7cd5f0bf983af234b775db64" name = "github.com/gogo/protobuf" packages = [ "gogoproto", @@ -102,11 +102,11 @@ "types", ] pruneopts = "UT" - revision = "636bf0302bc95575d69441b25a2603156ffdddf1" - version = "v1.1.1" + revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c" + version = "v1.2.1" [[projects]] - digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260" + digest = "1:239c4c7fd2159585454003d9be7207167970194216193a8a210b8d29576f19c9" name = "github.com/golang/protobuf" packages = [ "proto", @@ -116,8 +116,8 @@ "ptypes/timestamp", ] pruneopts = "UT" - revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" - version = "v1.1.0" + revision = "c823c79ea1570fb5ff454033735a8e68575d1d0f" + version = "v1.3.0" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index e5d6b8da2..cf905ee75 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -28,11 +28,11 @@ [[constraint]] name = "github.com/gogo/protobuf" - version = "~1.1.1" + version = "~1.2.1" [[constraint]] name = "github.com/golang/protobuf" - version = "~1.1.0" + version = "~1.3.0" # Allow only minor releases for other libraries [[constraint]] diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index c867dffc8..8e2f77f23 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -61,7 +61,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{0} + return fileDescriptor_types_dfa4953f824ab2aa, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -483,7 +483,7 @@ func (m *RequestEcho) Reset() { *m = RequestEcho{} } func (m *RequestEcho) String() string { return proto.CompactTextString(m) } func (*RequestEcho) ProtoMessage() {} func (*RequestEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{1} + return fileDescriptor_types_dfa4953f824ab2aa, []int{1} } func (m *RequestEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -529,7 +529,7 @@ func (m *RequestFlush) Reset() { *m = RequestFlush{} } func (m *RequestFlush) String() string { return proto.CompactTextString(m) } func (*RequestFlush) ProtoMessage() {} func (*RequestFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{2} + return fileDescriptor_types_dfa4953f824ab2aa, []int{2} } func (m *RequestFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -571,7 +571,7 @@ func (m *RequestInfo) Reset() { *m = RequestInfo{} } func (m *RequestInfo) String() string { return proto.CompactTextString(m) } func (*RequestInfo) ProtoMessage() {} func (*RequestInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{3} + return fileDescriptor_types_dfa4953f824ab2aa, []int{3} } func (m *RequestInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,7 +634,7 @@ func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } func (*RequestSetOption) ProtoMessage() {} func (*RequestSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{4} + return fileDescriptor_types_dfa4953f824ab2aa, []int{4} } func (m *RequestSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -692,7 +692,7 @@ func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } func (*RequestInitChain) ProtoMessage() {} func (*RequestInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{5} + return fileDescriptor_types_dfa4953f824ab2aa, []int{5} } func (m *RequestInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -770,7 +770,7 @@ func (m *RequestQuery) Reset() { *m = RequestQuery{} } func (m *RequestQuery) String() string { return proto.CompactTextString(m) } func (*RequestQuery) ProtoMessage() {} func (*RequestQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{6} + return fileDescriptor_types_dfa4953f824ab2aa, []int{6} } func (m *RequestQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -841,7 +841,7 @@ func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } func (*RequestBeginBlock) ProtoMessage() {} func (*RequestBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{7} + return fileDescriptor_types_dfa4953f824ab2aa, []int{7} } func (m *RequestBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -909,7 +909,7 @@ func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } func (*RequestCheckTx) ProtoMessage() {} func (*RequestCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{8} + return fileDescriptor_types_dfa4953f824ab2aa, []int{8} } func (m *RequestCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -956,7 +956,7 @@ func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } func (*RequestDeliverTx) ProtoMessage() {} func (*RequestDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{9} + return fileDescriptor_types_dfa4953f824ab2aa, []int{9} } func (m *RequestDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1003,7 @@ func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } func (*RequestEndBlock) ProtoMessage() {} func (*RequestEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{10} + return fileDescriptor_types_dfa4953f824ab2aa, []int{10} } func (m *RequestEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1049,7 @@ func (m *RequestCommit) Reset() { *m = RequestCommit{} } func (m *RequestCommit) String() string { return proto.CompactTextString(m) } func (*RequestCommit) ProtoMessage() {} func (*RequestCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{11} + return fileDescriptor_types_dfa4953f824ab2aa, []int{11} } func (m *RequestCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1102,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{12} + return fileDescriptor_types_dfa4953f824ab2aa, []int{12} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1555,7 +1555,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{13} + return fileDescriptor_types_dfa4953f824ab2aa, []int{13} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1602,7 +1602,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{14} + return fileDescriptor_types_dfa4953f824ab2aa, []int{14} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1648,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{15} + return fileDescriptor_types_dfa4953f824ab2aa, []int{15} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1692,7 +1692,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{16} + return fileDescriptor_types_dfa4953f824ab2aa, []int{16} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1771,7 +1771,7 @@ func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } func (*ResponseSetOption) ProtoMessage() {} func (*ResponseSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{17} + return fileDescriptor_types_dfa4953f824ab2aa, []int{17} } func (m *ResponseSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1833,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{18} + return fileDescriptor_types_dfa4953f824ab2aa, []int{18} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1896,7 +1896,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{19} + return fileDescriptor_types_dfa4953f824ab2aa, []int{19} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1999,7 +1999,7 @@ func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } func (*ResponseBeginBlock) ProtoMessage() {} func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{20} + return fileDescriptor_types_dfa4953f824ab2aa, []int{20} } func (m *ResponseBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2053,7 +2053,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{21} + return fileDescriptor_types_dfa4953f824ab2aa, []int{21} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2156,7 +2156,7 @@ func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } func (*ResponseDeliverTx) ProtoMessage() {} func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{22} + return fileDescriptor_types_dfa4953f824ab2aa, []int{22} } func (m *ResponseDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2254,7 +2254,7 @@ func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } func (*ResponseEndBlock) ProtoMessage() {} func (*ResponseEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{23} + return fileDescriptor_types_dfa4953f824ab2aa, []int{23} } func (m *ResponseEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2316,7 +2316,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{24} + return fileDescriptor_types_dfa4953f824ab2aa, []int{24} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2367,7 +2367,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } func (*ConsensusParams) ProtoMessage() {} func (*ConsensusParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{25} + return fileDescriptor_types_dfa4953f824ab2aa, []int{25} } func (m *ConsensusParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2432,7 +2432,7 @@ func (m *BlockSizeParams) Reset() { *m = BlockSizeParams{} } func (m *BlockSizeParams) String() string { return proto.CompactTextString(m) } func (*BlockSizeParams) ProtoMessage() {} func (*BlockSizeParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{26} + return fileDescriptor_types_dfa4953f824ab2aa, []int{26} } func (m *BlockSizeParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2488,7 +2488,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } func (m *EvidenceParams) String() string { return proto.CompactTextString(m) } func (*EvidenceParams) ProtoMessage() {} func (*EvidenceParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{27} + return fileDescriptor_types_dfa4953f824ab2aa, []int{27} } func (m *EvidenceParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2536,7 +2536,7 @@ func (m *ValidatorParams) Reset() { *m = ValidatorParams{} } func (m *ValidatorParams) String() string { return proto.CompactTextString(m) } func (*ValidatorParams) ProtoMessage() {} func (*ValidatorParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{28} + return fileDescriptor_types_dfa4953f824ab2aa, []int{28} } func (m *ValidatorParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2584,7 +2584,7 @@ func (m *LastCommitInfo) Reset() { *m = LastCommitInfo{} } func (m *LastCommitInfo) String() string { return proto.CompactTextString(m) } func (*LastCommitInfo) ProtoMessage() {} func (*LastCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{29} + return fileDescriptor_types_dfa4953f824ab2aa, []int{29} } func (m *LastCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2658,7 +2658,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{30} + return fileDescriptor_types_dfa4953f824ab2aa, []int{30} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2811,7 +2811,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{31} + return fileDescriptor_types_dfa4953f824ab2aa, []int{31} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2866,7 +2866,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{32} + return fileDescriptor_types_dfa4953f824ab2aa, []int{32} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2921,7 +2921,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{33} + return fileDescriptor_types_dfa4953f824ab2aa, []int{33} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2978,7 +2978,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{34} + return fileDescriptor_types_dfa4953f824ab2aa, []int{34} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3034,7 +3034,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{35} + return fileDescriptor_types_dfa4953f824ab2aa, []int{35} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3090,7 +3090,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{36} + return fileDescriptor_types_dfa4953f824ab2aa, []int{36} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3145,7 +3145,7 @@ func (m *PubKey) Reset() { *m = PubKey{} } func (m *PubKey) String() string { return proto.CompactTextString(m) } func (*PubKey) ProtoMessage() {} func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{37} + return fileDescriptor_types_dfa4953f824ab2aa, []int{37} } func (m *PubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3203,7 +3203,7 @@ func (m *Evidence) Reset() { *m = Evidence{} } func (m *Evidence) String() string { return proto.CompactTextString(m) } func (*Evidence) ProtoMessage() {} func (*Evidence) Descriptor() ([]byte, []int) { - return fileDescriptor_types_5b877df1938afe10, []int{38} + return fileDescriptor_types_dfa4953f824ab2aa, []int{38} } func (m *Evidence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -8448,6 +8448,9 @@ func encodeVarintPopulateTypes(dAtA []byte, v uint64) []byte { return dAtA } func (m *Request) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != nil { @@ -8460,6 +8463,9 @@ func (m *Request) Size() (n int) { } func (m *Request_Echo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Echo != nil { @@ -8469,6 +8475,9 @@ func (m *Request_Echo) Size() (n int) { return n } func (m *Request_Flush) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Flush != nil { @@ -8478,6 +8487,9 @@ func (m *Request_Flush) Size() (n int) { return n } func (m *Request_Info) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Info != nil { @@ -8487,6 +8499,9 @@ func (m *Request_Info) Size() (n int) { return n } func (m *Request_SetOption) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.SetOption != nil { @@ -8496,6 +8511,9 @@ func (m *Request_SetOption) Size() (n int) { return n } func (m *Request_InitChain) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.InitChain != nil { @@ -8505,6 +8523,9 @@ func (m *Request_InitChain) Size() (n int) { return n } func (m *Request_Query) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Query != nil { @@ -8514,6 +8535,9 @@ func (m *Request_Query) Size() (n int) { return n } func (m *Request_BeginBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.BeginBlock != nil { @@ -8523,6 +8547,9 @@ func (m *Request_BeginBlock) Size() (n int) { return n } func (m *Request_CheckTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.CheckTx != nil { @@ -8532,6 +8559,9 @@ func (m *Request_CheckTx) Size() (n int) { return n } func (m *Request_EndBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.EndBlock != nil { @@ -8541,6 +8571,9 @@ func (m *Request_EndBlock) Size() (n int) { return n } func (m *Request_Commit) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Commit != nil { @@ -8550,6 +8583,9 @@ func (m *Request_Commit) Size() (n int) { return n } func (m *Request_DeliverTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.DeliverTx != nil { @@ -8559,6 +8595,9 @@ func (m *Request_DeliverTx) Size() (n int) { return n } func (m *RequestEcho) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Message) @@ -8572,6 +8611,9 @@ func (m *RequestEcho) Size() (n int) { } func (m *RequestFlush) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.XXX_unrecognized != nil { @@ -8581,6 +8623,9 @@ func (m *RequestFlush) Size() (n int) { } func (m *RequestInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Version) @@ -8600,6 +8645,9 @@ func (m *RequestInfo) Size() (n int) { } func (m *RequestSetOption) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -8617,6 +8665,9 @@ func (m *RequestSetOption) Size() (n int) { } func (m *RequestInitChain) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = github_com_gogo_protobuf_types.SizeOfStdTime(m.Time) @@ -8646,6 +8697,9 @@ func (m *RequestInitChain) Size() (n int) { } func (m *RequestQuery) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Data) @@ -8669,6 +8723,9 @@ func (m *RequestQuery) Size() (n int) { } func (m *RequestBeginBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Hash) @@ -8692,6 +8749,9 @@ func (m *RequestBeginBlock) Size() (n int) { } func (m *RequestCheckTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Tx) @@ -8705,6 +8765,9 @@ func (m *RequestCheckTx) Size() (n int) { } func (m *RequestDeliverTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Tx) @@ -8718,6 +8781,9 @@ func (m *RequestDeliverTx) Size() (n int) { } func (m *RequestEndBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Height != 0 { @@ -8730,6 +8796,9 @@ func (m *RequestEndBlock) Size() (n int) { } func (m *RequestCommit) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.XXX_unrecognized != nil { @@ -8739,6 +8808,9 @@ func (m *RequestCommit) Size() (n int) { } func (m *Response) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Value != nil { @@ -8751,6 +8823,9 @@ func (m *Response) Size() (n int) { } func (m *Response_Exception) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Exception != nil { @@ -8760,6 +8835,9 @@ func (m *Response_Exception) Size() (n int) { return n } func (m *Response_Echo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Echo != nil { @@ -8769,6 +8847,9 @@ func (m *Response_Echo) Size() (n int) { return n } func (m *Response_Flush) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Flush != nil { @@ -8778,6 +8859,9 @@ func (m *Response_Flush) Size() (n int) { return n } func (m *Response_Info) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Info != nil { @@ -8787,6 +8871,9 @@ func (m *Response_Info) Size() (n int) { return n } func (m *Response_SetOption) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.SetOption != nil { @@ -8796,6 +8883,9 @@ func (m *Response_SetOption) Size() (n int) { return n } func (m *Response_InitChain) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.InitChain != nil { @@ -8805,6 +8895,9 @@ func (m *Response_InitChain) Size() (n int) { return n } func (m *Response_Query) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Query != nil { @@ -8814,6 +8907,9 @@ func (m *Response_Query) Size() (n int) { return n } func (m *Response_BeginBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.BeginBlock != nil { @@ -8823,6 +8919,9 @@ func (m *Response_BeginBlock) Size() (n int) { return n } func (m *Response_CheckTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.CheckTx != nil { @@ -8832,6 +8931,9 @@ func (m *Response_CheckTx) Size() (n int) { return n } func (m *Response_DeliverTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.DeliverTx != nil { @@ -8841,6 +8943,9 @@ func (m *Response_DeliverTx) Size() (n int) { return n } func (m *Response_EndBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.EndBlock != nil { @@ -8850,6 +8955,9 @@ func (m *Response_EndBlock) Size() (n int) { return n } func (m *Response_Commit) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Commit != nil { @@ -8859,6 +8967,9 @@ func (m *Response_Commit) Size() (n int) { return n } func (m *ResponseException) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Error) @@ -8872,6 +8983,9 @@ func (m *ResponseException) Size() (n int) { } func (m *ResponseEcho) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Message) @@ -8885,6 +8999,9 @@ func (m *ResponseEcho) Size() (n int) { } func (m *ResponseFlush) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.XXX_unrecognized != nil { @@ -8894,6 +9011,9 @@ func (m *ResponseFlush) Size() (n int) { } func (m *ResponseInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Data) @@ -8921,6 +9041,9 @@ func (m *ResponseInfo) Size() (n int) { } func (m *ResponseSetOption) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Code != 0 { @@ -8941,6 +9064,9 @@ func (m *ResponseSetOption) Size() (n int) { } func (m *ResponseInitChain) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.ConsensusParams != nil { @@ -8960,6 +9086,9 @@ func (m *ResponseInitChain) Size() (n int) { } func (m *ResponseQuery) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Code != 0 { @@ -9002,6 +9131,9 @@ func (m *ResponseQuery) Size() (n int) { } func (m *ResponseBeginBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Tags) > 0 { @@ -9017,6 +9149,9 @@ func (m *ResponseBeginBlock) Size() (n int) { } func (m *ResponseCheckTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Code != 0 { @@ -9057,6 +9192,9 @@ func (m *ResponseCheckTx) Size() (n int) { } func (m *ResponseDeliverTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Code != 0 { @@ -9097,6 +9235,9 @@ func (m *ResponseDeliverTx) Size() (n int) { } func (m *ResponseEndBlock) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.ValidatorUpdates) > 0 { @@ -9122,6 +9263,9 @@ func (m *ResponseEndBlock) Size() (n int) { } func (m *ResponseCommit) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Data) @@ -9135,6 +9279,9 @@ func (m *ResponseCommit) Size() (n int) { } func (m *ConsensusParams) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.BlockSize != nil { @@ -9156,6 +9303,9 @@ func (m *ConsensusParams) Size() (n int) { } func (m *BlockSizeParams) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.MaxBytes != 0 { @@ -9171,6 +9321,9 @@ func (m *BlockSizeParams) Size() (n int) { } func (m *EvidenceParams) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.MaxAge != 0 { @@ -9183,6 +9336,9 @@ func (m *EvidenceParams) Size() (n int) { } func (m *ValidatorParams) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.PubKeyTypes) > 0 { @@ -9198,6 +9354,9 @@ func (m *ValidatorParams) Size() (n int) { } func (m *LastCommitInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Round != 0 { @@ -9216,6 +9375,9 @@ func (m *LastCommitInfo) Size() (n int) { } func (m *Header) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Version.Size() @@ -9280,6 +9442,9 @@ func (m *Header) Size() (n int) { } func (m *Version) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Block != 0 { @@ -9295,6 +9460,9 @@ func (m *Version) Size() (n int) { } func (m *BlockID) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Hash) @@ -9310,6 +9478,9 @@ func (m *BlockID) Size() (n int) { } func (m *PartSetHeader) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.Total != 0 { @@ -9326,6 +9497,9 @@ func (m *PartSetHeader) Size() (n int) { } func (m *Validator) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Address) @@ -9342,6 +9516,9 @@ func (m *Validator) Size() (n int) { } func (m *ValidatorUpdate) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.PubKey.Size() @@ -9356,6 +9533,9 @@ func (m *ValidatorUpdate) Size() (n int) { } func (m *VoteInfo) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = m.Validator.Size() @@ -9370,6 +9550,9 @@ func (m *VoteInfo) Size() (n int) { } func (m *PubKey) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -9387,6 +9570,9 @@ func (m *PubKey) Size() (n int) { } func (m *Evidence) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -15171,12 +15357,12 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_5b877df1938afe10) } +func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_dfa4953f824ab2aa) } func init() { - golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_5b877df1938afe10) + golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_dfa4953f824ab2aa) } -var fileDescriptor_types_5b877df1938afe10 = []byte{ +var fileDescriptor_types_dfa4953f824ab2aa = []byte{ // 2214 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcb, 0x73, 0x1b, 0xc7, 0xd1, 0xe7, 0x82, 0x20, 0x81, 0x6d, 0x10, 0x0f, 0x8d, 0x28, 0x09, 0xc2, 0xe7, 0x8f, 0x54, 0xad, diff --git a/crypto/merkle/merkle.pb.go b/crypto/merkle/merkle.pb.go index 75e1b08c3..5b7e15c5a 100644 --- a/crypto/merkle/merkle.pb.go +++ b/crypto/merkle/merkle.pb.go @@ -39,7 +39,7 @@ func (m *ProofOp) Reset() { *m = ProofOp{} } func (m *ProofOp) String() string { return proto.CompactTextString(m) } func (*ProofOp) ProtoMessage() {} func (*ProofOp) Descriptor() ([]byte, []int) { - return fileDescriptor_merkle_5d3f6051907285da, []int{0} + return fileDescriptor_merkle_24be8bc4e405ac66, []int{0} } func (m *ProofOp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -101,7 +101,7 @@ func (m *Proof) Reset() { *m = Proof{} } func (m *Proof) String() string { return proto.CompactTextString(m) } func (*Proof) ProtoMessage() {} func (*Proof) Descriptor() ([]byte, []int) { - return fileDescriptor_merkle_5d3f6051907285da, []int{1} + return fileDescriptor_merkle_24be8bc4e405ac66, []int{1} } func (m *Proof) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -395,6 +395,9 @@ func encodeVarintPopulateMerkle(dAtA []byte, v uint64) []byte { return dAtA } func (m *ProofOp) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Type) @@ -416,6 +419,9 @@ func (m *ProofOp) Size() (n int) { } func (m *Proof) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if len(m.Ops) > 0 { @@ -772,9 +778,9 @@ var ( ErrIntOverflowMerkle = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("crypto/merkle/merkle.proto", fileDescriptor_merkle_5d3f6051907285da) } +func init() { proto.RegisterFile("crypto/merkle/merkle.proto", fileDescriptor_merkle_24be8bc4e405ac66) } -var fileDescriptor_merkle_5d3f6051907285da = []byte{ +var fileDescriptor_merkle_24be8bc4e405ac66 = []byte{ // 200 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4a, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0xcf, 0x4d, 0x2d, 0xca, 0xce, 0x49, 0x85, 0x52, 0x7a, 0x05, 0x45, 0xf9, 0x25, diff --git a/libs/common/types.pb.go b/libs/common/types.pb.go index 716d28a06..46a714d23 100644 --- a/libs/common/types.pb.go +++ b/libs/common/types.pb.go @@ -1,7 +1,6 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: libs/common/types.proto -//nolint package common import proto "github.com/gogo/protobuf/proto" @@ -26,7 +25,7 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package -// Define these here for compatibility but use libs/common.KVPair. +// Define these here for compatibility but use tmlibs/common.KVPair. type KVPair struct { Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` @@ -39,7 +38,7 @@ func (m *KVPair) Reset() { *m = KVPair{} } func (m *KVPair) String() string { return proto.CompactTextString(m) } func (*KVPair) ProtoMessage() {} func (*KVPair) Descriptor() ([]byte, []int) { - return fileDescriptor_types_611b4364a8604338, []int{0} + return fileDescriptor_types_a863d437ea36eb85, []int{0} } func (m *KVPair) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -82,7 +81,7 @@ func (m *KVPair) GetValue() []byte { return nil } -// Define these here for compatibility but use libs/common.KI64Pair. +// Define these here for compatibility but use tmlibs/common.KI64Pair. type KI64Pair struct { Key []byte `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` Value int64 `protobuf:"varint,2,opt,name=value,proto3" json:"value,omitempty"` @@ -95,7 +94,7 @@ func (m *KI64Pair) Reset() { *m = KI64Pair{} } func (m *KI64Pair) String() string { return proto.CompactTextString(m) } func (*KI64Pair) ProtoMessage() {} func (*KI64Pair) Descriptor() ([]byte, []int) { - return fileDescriptor_types_611b4364a8604338, []int{1} + return fileDescriptor_types_a863d437ea36eb85, []int{1} } func (m *KI64Pair) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -386,6 +385,9 @@ func encodeVarintPopulateTypes(dAtA []byte, v uint64) []byte { return dAtA } func (m *KVPair) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -403,6 +405,9 @@ func (m *KVPair) Size() (n int) { } func (m *KI64Pair) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Key) @@ -750,12 +755,12 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("libs/common/types.proto", fileDescriptor_types_611b4364a8604338) } +func init() { proto.RegisterFile("libs/common/types.proto", fileDescriptor_types_a863d437ea36eb85) } func init() { - golang_proto.RegisterFile("libs/common/types.proto", fileDescriptor_types_611b4364a8604338) + golang_proto.RegisterFile("libs/common/types.proto", fileDescriptor_types_a863d437ea36eb85) } -var fileDescriptor_types_611b4364a8604338 = []byte{ +var fileDescriptor_types_a863d437ea36eb85 = []byte{ // 174 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcf, 0xc9, 0x4c, 0x2a, 0xd6, 0x4f, 0xce, 0xcf, 0xcd, 0xcd, 0xcf, 0xd3, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x2b, 0x28, diff --git a/rpc/grpc/types.pb.go b/rpc/grpc/types.pb.go index b33397dab..2fd469159 100644 --- a/rpc/grpc/types.pb.go +++ b/rpc/grpc/types.pb.go @@ -1,7 +1,6 @@ // Code generated by protoc-gen-gogo. DO NOT EDIT. // source: rpc/grpc/types.proto -//nolint package core_grpc import proto "github.com/gogo/protobuf/proto" @@ -42,7 +41,7 @@ func (m *RequestPing) Reset() { *m = RequestPing{} } func (m *RequestPing) String() string { return proto.CompactTextString(m) } func (*RequestPing) ProtoMessage() {} func (*RequestPing) Descriptor() ([]byte, []int) { - return fileDescriptor_types_48bb8d9591d37e66, []int{0} + return fileDescriptor_types_8721e2f2d306fca2, []int{0} } func (m *RequestPing) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -82,7 +81,7 @@ func (m *RequestBroadcastTx) Reset() { *m = RequestBroadcastTx{} } func (m *RequestBroadcastTx) String() string { return proto.CompactTextString(m) } func (*RequestBroadcastTx) ProtoMessage() {} func (*RequestBroadcastTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_48bb8d9591d37e66, []int{1} + return fileDescriptor_types_8721e2f2d306fca2, []int{1} } func (m *RequestBroadcastTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -128,7 +127,7 @@ func (m *ResponsePing) Reset() { *m = ResponsePing{} } func (m *ResponsePing) String() string { return proto.CompactTextString(m) } func (*ResponsePing) ProtoMessage() {} func (*ResponsePing) Descriptor() ([]byte, []int) { - return fileDescriptor_types_48bb8d9591d37e66, []int{2} + return fileDescriptor_types_8721e2f2d306fca2, []int{2} } func (m *ResponsePing) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -169,7 +168,7 @@ func (m *ResponseBroadcastTx) Reset() { *m = ResponseBroadcastTx{} } func (m *ResponseBroadcastTx) String() string { return proto.CompactTextString(m) } func (*ResponseBroadcastTx) ProtoMessage() {} func (*ResponseBroadcastTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_48bb8d9591d37e66, []int{3} + return fileDescriptor_types_8721e2f2d306fca2, []int{3} } func (m *ResponseBroadcastTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -370,8 +369,7 @@ func (c *broadcastAPIClient) BroadcastTx(ctx context.Context, in *RequestBroadca return out, nil } -// Server API for BroadcastAPI service - +// BroadcastAPIServer is the server API for BroadcastAPI service. type BroadcastAPIServer interface { Ping(context.Context, *RequestPing) (*ResponsePing, error) BroadcastTx(context.Context, *RequestBroadcastTx) (*ResponseBroadcastTx, error) @@ -669,6 +667,9 @@ func encodeVarintPopulateTypes(dAtA []byte, v uint64) []byte { return dAtA } func (m *RequestPing) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.XXX_unrecognized != nil { @@ -678,6 +679,9 @@ func (m *RequestPing) Size() (n int) { } func (m *RequestBroadcastTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l l = len(m.Tx) @@ -691,6 +695,9 @@ func (m *RequestBroadcastTx) Size() (n int) { } func (m *ResponsePing) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.XXX_unrecognized != nil { @@ -700,6 +707,9 @@ func (m *ResponsePing) Size() (n int) { } func (m *ResponseBroadcastTx) Size() (n int) { + if m == nil { + return 0 + } var l int _ = l if m.CheckTx != nil { @@ -1135,10 +1145,10 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("rpc/grpc/types.proto", fileDescriptor_types_48bb8d9591d37e66) } -func init() { golang_proto.RegisterFile("rpc/grpc/types.proto", fileDescriptor_types_48bb8d9591d37e66) } +func init() { proto.RegisterFile("rpc/grpc/types.proto", fileDescriptor_types_8721e2f2d306fca2) } +func init() { golang_proto.RegisterFile("rpc/grpc/types.proto", fileDescriptor_types_8721e2f2d306fca2) } -var fileDescriptor_types_48bb8d9591d37e66 = []byte{ +var fileDescriptor_types_8721e2f2d306fca2 = []byte{ // 321 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x29, 0x2a, 0x48, 0xd6, 0x4f, 0x07, 0x11, 0x25, 0x95, 0x05, 0xa9, 0xc5, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0x9c, diff --git a/types/proto3/block.pb.go b/types/proto3/block.pb.go index 99dadac16..0fee66c29 100644 --- a/types/proto3/block.pb.go +++ b/types/proto3/block.pb.go @@ -30,7 +30,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_block_57c41dfc0fc285b3, []int{0} + return fileDescriptor_block_1ca6cebf74619a45, []int{0} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PartSetHeader.Unmarshal(m, b) @@ -76,7 +76,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_block_57c41dfc0fc285b3, []int{1} + return fileDescriptor_block_1ca6cebf74619a45, []int{1} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_BlockID.Unmarshal(m, b) @@ -141,7 +141,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_block_57c41dfc0fc285b3, []int{2} + return fileDescriptor_block_1ca6cebf74619a45, []int{2} } func (m *Header) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Header.Unmarshal(m, b) @@ -285,7 +285,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_block_57c41dfc0fc285b3, []int{3} + return fileDescriptor_block_1ca6cebf74619a45, []int{3} } func (m *Version) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Version.Unmarshal(m, b) @@ -336,7 +336,7 @@ func (m *Timestamp) Reset() { *m = Timestamp{} } func (m *Timestamp) String() string { return proto.CompactTextString(m) } func (*Timestamp) ProtoMessage() {} func (*Timestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_block_57c41dfc0fc285b3, []int{4} + return fileDescriptor_block_1ca6cebf74619a45, []int{4} } func (m *Timestamp) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_Timestamp.Unmarshal(m, b) @@ -378,9 +378,9 @@ func init() { proto.RegisterType((*Timestamp)(nil), "proto3.Timestamp") } -func init() { proto.RegisterFile("types/proto3/block.proto", fileDescriptor_block_57c41dfc0fc285b3) } +func init() { proto.RegisterFile("types/proto3/block.proto", fileDescriptor_block_1ca6cebf74619a45) } -var fileDescriptor_block_57c41dfc0fc285b3 = []byte{ +var fileDescriptor_block_1ca6cebf74619a45 = []byte{ // 451 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0x5f, 0x6f, 0xd3, 0x30, 0x10, 0x57, 0x68, 0xda, 0xae, 0x97, 0x76, 0x1d, 0x27, 0x40, 0x16, 0x4f, 0x55, 0x04, 0xa8, 0xbc, From f39138aa2e543438548a150bdad45304ccc3296b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 4 Mar 2019 12:18:32 +0400 Subject: [PATCH 080/259] remove RoundState from EventDataRoundState (#3354) Before we're using it to get a round state in tests. Now it can be done by calling csX.GetRoundState. We will need to rewrite TestStateSlashingPrevotes and TestStateSlashingPrecommits, which are commented right now, to not rely on EventDataRoundState#RoundState field. Refs #1527 --- consensus/types/round_state.go | 10 +++------- types/events.go | 3 --- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/consensus/types/round_state.go b/consensus/types/round_state.go index eab13b6c7..c4372e201 100644 --- a/consensus/types/round_state.go +++ b/consensus/types/round_state.go @@ -148,14 +148,10 @@ func (rs *RoundState) CompleteProposalEvent() types.EventDataCompleteProposal { // RoundStateEvent returns the H/R/S of the RoundState as an event. func (rs *RoundState) RoundStateEvent() types.EventDataRoundState { - // copy the RoundState. - // TODO: if we want to avoid this, we may need synchronous events after all - rsCopy := *rs return types.EventDataRoundState{ - Height: rs.Height, - Round: rs.Round, - Step: rs.Step.String(), - RoundState: &rsCopy, + Height: rs.Height, + Round: rs.Round, + Step: rs.Step.String(), } } diff --git a/types/events.go b/types/events.go index b70bc9dc5..b65ea3832 100644 --- a/types/events.go +++ b/types/events.go @@ -87,9 +87,6 @@ type EventDataRoundState struct { Height int64 `json:"height"` Round int `json:"round"` Step string `json:"step"` - - // private, not exposed to websockets - RoundState interface{} `json:"-"` } type ValidatorInfo struct { From 52771e1287eb44eabae99e6d2781a57c577821bb Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 4 Mar 2019 13:24:44 +0400 Subject: [PATCH 081/259] =?UTF-8?q?make=20BlockTimeIota=20a=20consensus=20?= =?UTF-8?q?parameter,=20not=20a=20locally=20configurable=20=E2=80=A6=20(#3?= =?UTF-8?q?048)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * make BlockTimeIota a consensus parameter, not a locally configurable option Refs #2920 * make TimeIota int64 ms Refs #2920 * update Gopkg.toml * fixes after Ethan's review * fix TestRemoteSignerProposalSigningFailed * update changelog --- CHANGELOG_PENDING.md | 3 + abci/types/types.pb.go | 499 ++++++++++-------- abci/types/types.proto | 12 +- abci/types/typespb_test.go | 34 +- config/config.go | 14 - config/toml.go | 3 - consensus/state.go | 8 +- docs/spec/abci/abci.md | 6 +- docs/spec/abci/apps.md | 17 +- docs/spec/blockchain/state.md | 22 +- docs/spec/consensus/creating-proposal.md | 2 +- node/node_test.go | 2 +- state/execution.go | 4 +- state/state_test.go | 28 +- state/tx_filter.go | 4 +- state/tx_filter_test.go | 2 +- state/validation.go | 2 +- state/validation_test.go | 2 +- .../internal/test_harness_test.go | 15 +- types/block.go | 4 +- types/genesis_test.go | 2 +- types/params.go | 60 ++- types/params_test.go | 76 +-- types/protobuf.go | 41 +- 24 files changed, 471 insertions(+), 391 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f8614606d..fe9a87d8a 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -24,6 +24,9 @@ Special thanks to external contributors on this release: - [mempool] \#3079 bound mempool memory usage (`mempool.max_txs_bytes` is set to 1GB by default; see config.toml) mempool's current `txs_total_bytes` is exposed via `total_bytes` field in `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. +- [config] \#2920 Remove `consensus.blocktime_iota` parameter +- [genesis] \#2920 Add `time_iota_ms` to block's consensus parameters +- [genesis] \#2920 Rename `consensus_params.block_size` to `consensus_params.block` ### IMPROVEMENTS: - [libs/common] \#3238 exit with zero (0) code upon receiving SIGTERM/SIGINT diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 8e2f77f23..79af610c9 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -61,7 +61,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{0} + return fileDescriptor_types_e441973ce6650a0d, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -483,7 +483,7 @@ func (m *RequestEcho) Reset() { *m = RequestEcho{} } func (m *RequestEcho) String() string { return proto.CompactTextString(m) } func (*RequestEcho) ProtoMessage() {} func (*RequestEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{1} + return fileDescriptor_types_e441973ce6650a0d, []int{1} } func (m *RequestEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -529,7 +529,7 @@ func (m *RequestFlush) Reset() { *m = RequestFlush{} } func (m *RequestFlush) String() string { return proto.CompactTextString(m) } func (*RequestFlush) ProtoMessage() {} func (*RequestFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{2} + return fileDescriptor_types_e441973ce6650a0d, []int{2} } func (m *RequestFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -571,7 +571,7 @@ func (m *RequestInfo) Reset() { *m = RequestInfo{} } func (m *RequestInfo) String() string { return proto.CompactTextString(m) } func (*RequestInfo) ProtoMessage() {} func (*RequestInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{3} + return fileDescriptor_types_e441973ce6650a0d, []int{3} } func (m *RequestInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,7 +634,7 @@ func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } func (*RequestSetOption) ProtoMessage() {} func (*RequestSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{4} + return fileDescriptor_types_e441973ce6650a0d, []int{4} } func (m *RequestSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -692,7 +692,7 @@ func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } func (*RequestInitChain) ProtoMessage() {} func (*RequestInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{5} + return fileDescriptor_types_e441973ce6650a0d, []int{5} } func (m *RequestInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -770,7 +770,7 @@ func (m *RequestQuery) Reset() { *m = RequestQuery{} } func (m *RequestQuery) String() string { return proto.CompactTextString(m) } func (*RequestQuery) ProtoMessage() {} func (*RequestQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{6} + return fileDescriptor_types_e441973ce6650a0d, []int{6} } func (m *RequestQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -841,7 +841,7 @@ func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } func (*RequestBeginBlock) ProtoMessage() {} func (*RequestBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{7} + return fileDescriptor_types_e441973ce6650a0d, []int{7} } func (m *RequestBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -909,7 +909,7 @@ func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } func (*RequestCheckTx) ProtoMessage() {} func (*RequestCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{8} + return fileDescriptor_types_e441973ce6650a0d, []int{8} } func (m *RequestCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -956,7 +956,7 @@ func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } func (*RequestDeliverTx) ProtoMessage() {} func (*RequestDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{9} + return fileDescriptor_types_e441973ce6650a0d, []int{9} } func (m *RequestDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1003,7 @@ func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } func (*RequestEndBlock) ProtoMessage() {} func (*RequestEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{10} + return fileDescriptor_types_e441973ce6650a0d, []int{10} } func (m *RequestEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1049,7 @@ func (m *RequestCommit) Reset() { *m = RequestCommit{} } func (m *RequestCommit) String() string { return proto.CompactTextString(m) } func (*RequestCommit) ProtoMessage() {} func (*RequestCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{11} + return fileDescriptor_types_e441973ce6650a0d, []int{11} } func (m *RequestCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1102,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{12} + return fileDescriptor_types_e441973ce6650a0d, []int{12} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1555,7 +1555,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{13} + return fileDescriptor_types_e441973ce6650a0d, []int{13} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1602,7 +1602,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{14} + return fileDescriptor_types_e441973ce6650a0d, []int{14} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1648,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{15} + return fileDescriptor_types_e441973ce6650a0d, []int{15} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1692,7 +1692,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{16} + return fileDescriptor_types_e441973ce6650a0d, []int{16} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1771,7 +1771,7 @@ func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } func (*ResponseSetOption) ProtoMessage() {} func (*ResponseSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{17} + return fileDescriptor_types_e441973ce6650a0d, []int{17} } func (m *ResponseSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1833,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{18} + return fileDescriptor_types_e441973ce6650a0d, []int{18} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1896,7 +1896,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{19} + return fileDescriptor_types_e441973ce6650a0d, []int{19} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1999,7 +1999,7 @@ func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } func (*ResponseBeginBlock) ProtoMessage() {} func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{20} + return fileDescriptor_types_e441973ce6650a0d, []int{20} } func (m *ResponseBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2053,7 +2053,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{21} + return fileDescriptor_types_e441973ce6650a0d, []int{21} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2156,7 +2156,7 @@ func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } func (*ResponseDeliverTx) ProtoMessage() {} func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{22} + return fileDescriptor_types_e441973ce6650a0d, []int{22} } func (m *ResponseDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2254,7 +2254,7 @@ func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } func (*ResponseEndBlock) ProtoMessage() {} func (*ResponseEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{23} + return fileDescriptor_types_e441973ce6650a0d, []int{23} } func (m *ResponseEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2316,7 +2316,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{24} + return fileDescriptor_types_e441973ce6650a0d, []int{24} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2355,7 +2355,7 @@ func (m *ResponseCommit) GetData() []byte { // ConsensusParams contains all consensus-relevant parameters // that can be adjusted by the abci app type ConsensusParams struct { - BlockSize *BlockSizeParams `protobuf:"bytes,1,opt,name=block_size,json=blockSize" json:"block_size,omitempty"` + Block *BlockParams `protobuf:"bytes,1,opt,name=block" json:"block,omitempty"` Evidence *EvidenceParams `protobuf:"bytes,2,opt,name=evidence" json:"evidence,omitempty"` Validator *ValidatorParams `protobuf:"bytes,3,opt,name=validator" json:"validator,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -2367,7 +2367,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } func (*ConsensusParams) ProtoMessage() {} func (*ConsensusParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{25} + return fileDescriptor_types_e441973ce6650a0d, []int{25} } func (m *ConsensusParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2396,9 +2396,9 @@ func (m *ConsensusParams) XXX_DiscardUnknown() { var xxx_messageInfo_ConsensusParams proto.InternalMessageInfo -func (m *ConsensusParams) GetBlockSize() *BlockSizeParams { +func (m *ConsensusParams) GetBlock() *BlockParams { if m != nil { - return m.BlockSize + return m.Block } return nil } @@ -2417,29 +2417,31 @@ func (m *ConsensusParams) GetValidator() *ValidatorParams { return nil } -// BlockSize contains limits on the block size. -type BlockSizeParams struct { +// BlockParams contains limits on the block size and timestamp. +type BlockParams struct { // Note: must be greater than 0 MaxBytes int64 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` // Note: must be greater or equal to -1 - MaxGas int64 `protobuf:"varint,2,opt,name=max_gas,json=maxGas,proto3" json:"max_gas,omitempty"` + MaxGas int64 `protobuf:"varint,2,opt,name=max_gas,json=maxGas,proto3" json:"max_gas,omitempty"` + // Note: must be greater than 0 + TimeIotaMs int64 `protobuf:"varint,3,opt,name=time_iota_ms,json=timeIotaMs,proto3" json:"time_iota_ms,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` } -func (m *BlockSizeParams) Reset() { *m = BlockSizeParams{} } -func (m *BlockSizeParams) String() string { return proto.CompactTextString(m) } -func (*BlockSizeParams) ProtoMessage() {} -func (*BlockSizeParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{26} +func (m *BlockParams) Reset() { *m = BlockParams{} } +func (m *BlockParams) String() string { return proto.CompactTextString(m) } +func (*BlockParams) ProtoMessage() {} +func (*BlockParams) Descriptor() ([]byte, []int) { + return fileDescriptor_types_e441973ce6650a0d, []int{26} } -func (m *BlockSizeParams) XXX_Unmarshal(b []byte) error { +func (m *BlockParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) } -func (m *BlockSizeParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { +func (m *BlockParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { if deterministic { - return xxx_messageInfo_BlockSizeParams.Marshal(b, m, deterministic) + return xxx_messageInfo_BlockParams.Marshal(b, m, deterministic) } else { b = b[:cap(b)] n, err := m.MarshalTo(b) @@ -2449,32 +2451,39 @@ func (m *BlockSizeParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, err return b[:n], nil } } -func (dst *BlockSizeParams) XXX_Merge(src proto.Message) { - xxx_messageInfo_BlockSizeParams.Merge(dst, src) +func (dst *BlockParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_BlockParams.Merge(dst, src) } -func (m *BlockSizeParams) XXX_Size() int { +func (m *BlockParams) XXX_Size() int { return m.Size() } -func (m *BlockSizeParams) XXX_DiscardUnknown() { - xxx_messageInfo_BlockSizeParams.DiscardUnknown(m) +func (m *BlockParams) XXX_DiscardUnknown() { + xxx_messageInfo_BlockParams.DiscardUnknown(m) } -var xxx_messageInfo_BlockSizeParams proto.InternalMessageInfo +var xxx_messageInfo_BlockParams proto.InternalMessageInfo -func (m *BlockSizeParams) GetMaxBytes() int64 { +func (m *BlockParams) GetMaxBytes() int64 { if m != nil { return m.MaxBytes } return 0 } -func (m *BlockSizeParams) GetMaxGas() int64 { +func (m *BlockParams) GetMaxGas() int64 { if m != nil { return m.MaxGas } return 0 } +func (m *BlockParams) GetTimeIotaMs() int64 { + if m != nil { + return m.TimeIotaMs + } + return 0 +} + // EvidenceParams contains limits on the evidence. type EvidenceParams struct { // Note: must be greater than 0 @@ -2488,7 +2497,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } func (m *EvidenceParams) String() string { return proto.CompactTextString(m) } func (*EvidenceParams) ProtoMessage() {} func (*EvidenceParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{27} + return fileDescriptor_types_e441973ce6650a0d, []int{27} } func (m *EvidenceParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2536,7 +2545,7 @@ func (m *ValidatorParams) Reset() { *m = ValidatorParams{} } func (m *ValidatorParams) String() string { return proto.CompactTextString(m) } func (*ValidatorParams) ProtoMessage() {} func (*ValidatorParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{28} + return fileDescriptor_types_e441973ce6650a0d, []int{28} } func (m *ValidatorParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2584,7 +2593,7 @@ func (m *LastCommitInfo) Reset() { *m = LastCommitInfo{} } func (m *LastCommitInfo) String() string { return proto.CompactTextString(m) } func (*LastCommitInfo) ProtoMessage() {} func (*LastCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{29} + return fileDescriptor_types_e441973ce6650a0d, []int{29} } func (m *LastCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2658,7 +2667,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{30} + return fileDescriptor_types_e441973ce6650a0d, []int{30} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2811,7 +2820,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{31} + return fileDescriptor_types_e441973ce6650a0d, []int{31} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2866,7 +2875,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{32} + return fileDescriptor_types_e441973ce6650a0d, []int{32} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2921,7 +2930,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{33} + return fileDescriptor_types_e441973ce6650a0d, []int{33} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2978,7 +2987,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{34} + return fileDescriptor_types_e441973ce6650a0d, []int{34} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3034,7 +3043,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{35} + return fileDescriptor_types_e441973ce6650a0d, []int{35} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3090,7 +3099,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{36} + return fileDescriptor_types_e441973ce6650a0d, []int{36} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3145,7 +3154,7 @@ func (m *PubKey) Reset() { *m = PubKey{} } func (m *PubKey) String() string { return proto.CompactTextString(m) } func (*PubKey) ProtoMessage() {} func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{37} + return fileDescriptor_types_e441973ce6650a0d, []int{37} } func (m *PubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3203,7 +3212,7 @@ func (m *Evidence) Reset() { *m = Evidence{} } func (m *Evidence) String() string { return proto.CompactTextString(m) } func (*Evidence) ProtoMessage() {} func (*Evidence) Descriptor() ([]byte, []int) { - return fileDescriptor_types_dfa4953f824ab2aa, []int{38} + return fileDescriptor_types_e441973ce6650a0d, []int{38} } func (m *Evidence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3320,8 +3329,8 @@ func init() { golang_proto.RegisterType((*ResponseCommit)(nil), "types.ResponseCommit") proto.RegisterType((*ConsensusParams)(nil), "types.ConsensusParams") golang_proto.RegisterType((*ConsensusParams)(nil), "types.ConsensusParams") - proto.RegisterType((*BlockSizeParams)(nil), "types.BlockSizeParams") - golang_proto.RegisterType((*BlockSizeParams)(nil), "types.BlockSizeParams") + proto.RegisterType((*BlockParams)(nil), "types.BlockParams") + golang_proto.RegisterType((*BlockParams)(nil), "types.BlockParams") proto.RegisterType((*EvidenceParams)(nil), "types.EvidenceParams") golang_proto.RegisterType((*EvidenceParams)(nil), "types.EvidenceParams") proto.RegisterType((*ValidatorParams)(nil), "types.ValidatorParams") @@ -4768,7 +4777,7 @@ func (this *ConsensusParams) Equal(that interface{}) bool { } else if this == nil { return false } - if !this.BlockSize.Equal(that1.BlockSize) { + if !this.Block.Equal(that1.Block) { return false } if !this.Evidence.Equal(that1.Evidence) { @@ -4782,14 +4791,14 @@ func (this *ConsensusParams) Equal(that interface{}) bool { } return true } -func (this *BlockSizeParams) Equal(that interface{}) bool { +func (this *BlockParams) Equal(that interface{}) bool { if that == nil { return this == nil } - that1, ok := that.(*BlockSizeParams) + that1, ok := that.(*BlockParams) if !ok { - that2, ok := that.(BlockSizeParams) + that2, ok := that.(BlockParams) if ok { that1 = &that2 } else { @@ -4807,6 +4816,9 @@ func (this *BlockSizeParams) Equal(that interface{}) bool { if this.MaxGas != that1.MaxGas { return false } + if this.TimeIotaMs != that1.TimeIotaMs { + return false + } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { return false } @@ -6950,11 +6962,11 @@ func (m *ConsensusParams) MarshalTo(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.BlockSize != nil { + if m.Block != nil { dAtA[i] = 0xa i++ - i = encodeVarintTypes(dAtA, i, uint64(m.BlockSize.Size())) - n33, err := m.BlockSize.MarshalTo(dAtA[i:]) + i = encodeVarintTypes(dAtA, i, uint64(m.Block.Size())) + n33, err := m.Block.MarshalTo(dAtA[i:]) if err != nil { return 0, err } @@ -6986,7 +6998,7 @@ func (m *ConsensusParams) MarshalTo(dAtA []byte) (int, error) { return i, nil } -func (m *BlockSizeParams) Marshal() (dAtA []byte, err error) { +func (m *BlockParams) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) n, err := m.MarshalTo(dAtA) @@ -6996,7 +7008,7 @@ func (m *BlockSizeParams) Marshal() (dAtA []byte, err error) { return dAtA[:n], nil } -func (m *BlockSizeParams) MarshalTo(dAtA []byte) (int, error) { +func (m *BlockParams) MarshalTo(dAtA []byte) (int, error) { var i int _ = i var l int @@ -7011,6 +7023,11 @@ func (m *BlockSizeParams) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintTypes(dAtA, i, uint64(m.MaxGas)) } + if m.TimeIotaMs != 0 { + dAtA[i] = 0x18 + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.TimeIotaMs)) + } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) } @@ -8109,7 +8126,7 @@ func NewPopulatedResponseCommit(r randyTypes, easy bool) *ResponseCommit { func NewPopulatedConsensusParams(r randyTypes, easy bool) *ConsensusParams { this := &ConsensusParams{} if r.Intn(10) != 0 { - this.BlockSize = NewPopulatedBlockSizeParams(r, easy) + this.Block = NewPopulatedBlockParams(r, easy) } if r.Intn(10) != 0 { this.Evidence = NewPopulatedEvidenceParams(r, easy) @@ -8123,8 +8140,8 @@ func NewPopulatedConsensusParams(r randyTypes, easy bool) *ConsensusParams { return this } -func NewPopulatedBlockSizeParams(r randyTypes, easy bool) *BlockSizeParams { - this := &BlockSizeParams{} +func NewPopulatedBlockParams(r randyTypes, easy bool) *BlockParams { + this := &BlockParams{} this.MaxBytes = int64(r.Int63()) if r.Intn(2) == 0 { this.MaxBytes *= -1 @@ -8133,8 +8150,12 @@ func NewPopulatedBlockSizeParams(r randyTypes, easy bool) *BlockSizeParams { if r.Intn(2) == 0 { this.MaxGas *= -1 } + this.TimeIotaMs = int64(r.Int63()) + if r.Intn(2) == 0 { + this.TimeIotaMs *= -1 + } if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedTypes(r, 3) + this.XXX_unrecognized = randUnrecognizedTypes(r, 4) } return this } @@ -9284,8 +9305,8 @@ func (m *ConsensusParams) Size() (n int) { } var l int _ = l - if m.BlockSize != nil { - l = m.BlockSize.Size() + if m.Block != nil { + l = m.Block.Size() n += 1 + l + sovTypes(uint64(l)) } if m.Evidence != nil { @@ -9302,7 +9323,7 @@ func (m *ConsensusParams) Size() (n int) { return n } -func (m *BlockSizeParams) Size() (n int) { +func (m *BlockParams) Size() (n int) { if m == nil { return 0 } @@ -9314,6 +9335,9 @@ func (m *BlockSizeParams) Size() (n int) { if m.MaxGas != 0 { n += 1 + sovTypes(uint64(m.MaxGas)) } + if m.TimeIotaMs != 0 { + n += 1 + sovTypes(uint64(m.TimeIotaMs)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13394,7 +13418,7 @@ func (m *ConsensusParams) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field BlockSize", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Block", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13418,10 +13442,10 @@ func (m *ConsensusParams) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.BlockSize == nil { - m.BlockSize = &BlockSizeParams{} + if m.Block == nil { + m.Block = &BlockParams{} } - if err := m.BlockSize.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.Block.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13513,7 +13537,7 @@ func (m *ConsensusParams) Unmarshal(dAtA []byte) error { } return nil } -func (m *BlockSizeParams) Unmarshal(dAtA []byte) error { +func (m *BlockParams) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 for iNdEx < l { @@ -13536,10 +13560,10 @@ func (m *BlockSizeParams) Unmarshal(dAtA []byte) error { fieldNum := int32(wire >> 3) wireType := int(wire & 0x7) if wireType == 4 { - return fmt.Errorf("proto: BlockSizeParams: wiretype end group for non-group") + return fmt.Errorf("proto: BlockParams: wiretype end group for non-group") } if fieldNum <= 0 { - return fmt.Errorf("proto: BlockSizeParams: illegal tag %d (wire type %d)", fieldNum, wire) + return fmt.Errorf("proto: BlockParams: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { case 1: @@ -13580,6 +13604,25 @@ func (m *BlockSizeParams) Unmarshal(dAtA []byte) error { break } } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field TimeIotaMs", wireType) + } + m.TimeIotaMs = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.TimeIotaMs |= (int64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15357,150 +15400,150 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_dfa4953f824ab2aa) } +func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_e441973ce6650a0d) } func init() { - golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_dfa4953f824ab2aa) + golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_e441973ce6650a0d) } -var fileDescriptor_types_dfa4953f824ab2aa = []byte{ - // 2214 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcb, 0x73, 0x1b, 0xc7, - 0xd1, 0xe7, 0x82, 0x20, 0x81, 0x6d, 0x10, 0x0f, 0x8d, 0x28, 0x09, 0xc2, 0xe7, 0x8f, 0x54, 0xad, - 0x12, 0x5b, 0x8c, 0x65, 0xd0, 0xa6, 0xa3, 0x14, 0x65, 0x39, 0xa9, 0x22, 0x24, 0xc5, 0x64, 0xd9, - 0x49, 0x98, 0x95, 0xc4, 0x5c, 0x52, 0xb5, 0x35, 0xc0, 0x8e, 0x80, 0x2d, 0x02, 0xbb, 0xeb, 0xdd, - 0x01, 0x0d, 0xea, 0x98, 0xb3, 0x0f, 0x3e, 0xe4, 0x8f, 0xc8, 0x35, 0x37, 0x1f, 0x73, 0x4a, 0xf9, - 0x98, 0x43, 0xce, 0x4a, 0xc2, 0x54, 0x0e, 0xc9, 0x35, 0x95, 0xaa, 0x1c, 0x53, 0xd3, 0x33, 0xb3, - 0x2f, 0x2e, 0x14, 0xcb, 0xc9, 0x29, 0x17, 0x60, 0xa6, 0x1f, 0xf3, 0xe8, 0xed, 0xee, 0x5f, 0xf7, - 0xc0, 0x75, 0x3a, 0x1c, 0x79, 0xbb, 0xfc, 0x3c, 0x64, 0xb1, 0xfc, 0xed, 0x87, 0x51, 0xc0, 0x03, - 0xb2, 0x86, 0x93, 0xde, 0x3b, 0x63, 0x8f, 0x4f, 0xe6, 0xc3, 0xfe, 0x28, 0x98, 0xed, 0x8e, 0x83, - 0x71, 0xb0, 0x8b, 0xdc, 0xe1, 0xfc, 0x39, 0xce, 0x70, 0x82, 0x23, 0xa9, 0xd5, 0xdb, 0x1e, 0x07, - 0xc1, 0x78, 0xca, 0x52, 0x29, 0xee, 0xcd, 0x58, 0xcc, 0xe9, 0x2c, 0x54, 0x02, 0xfb, 0x99, 0xf5, - 0x38, 0xf3, 0x5d, 0x16, 0xcd, 0x3c, 0x9f, 0x67, 0x87, 0x53, 0x6f, 0x18, 0xef, 0x8e, 0x82, 0xd9, - 0x2c, 0xf0, 0xb3, 0x07, 0xea, 0x3d, 0xf8, 0xb7, 0x9a, 0xa3, 0xe8, 0x3c, 0xe4, 0xc1, 0xee, 0x8c, - 0x45, 0xa7, 0x53, 0xa6, 0xfe, 0xa4, 0xb2, 0xf5, 0xdb, 0x2a, 0xd4, 0x6c, 0xf6, 0xe9, 0x9c, 0xc5, - 0x9c, 0xdc, 0x81, 0x2a, 0x1b, 0x4d, 0x82, 0x6e, 0xe5, 0x96, 0x71, 0xa7, 0xb1, 0x47, 0xfa, 0x72, - 0x13, 0xc5, 0x7d, 0x3c, 0x9a, 0x04, 0x87, 0x2b, 0x36, 0x4a, 0x90, 0xb7, 0x61, 0xed, 0xf9, 0x74, - 0x1e, 0x4f, 0xba, 0xab, 0x28, 0x7a, 0x35, 0x2f, 0xfa, 0x43, 0xc1, 0x3a, 0x5c, 0xb1, 0xa5, 0x8c, - 0x58, 0xd6, 0xf3, 0x9f, 0x07, 0xdd, 0x6a, 0xd9, 0xb2, 0x47, 0xfe, 0x73, 0x5c, 0x56, 0x48, 0x90, - 0x7d, 0x80, 0x98, 0x71, 0x27, 0x08, 0xb9, 0x17, 0xf8, 0xdd, 0x35, 0x94, 0xbf, 0x91, 0x97, 0x7f, - 0xc2, 0xf8, 0x4f, 0x90, 0x7d, 0xb8, 0x62, 0x9b, 0xb1, 0x9e, 0x08, 0x4d, 0xcf, 0xf7, 0xb8, 0x33, - 0x9a, 0x50, 0xcf, 0xef, 0xae, 0x97, 0x69, 0x1e, 0xf9, 0x1e, 0x7f, 0x28, 0xd8, 0x42, 0xd3, 0xd3, - 0x13, 0x71, 0x95, 0x4f, 0xe7, 0x2c, 0x3a, 0xef, 0xd6, 0xca, 0xae, 0xf2, 0x53, 0xc1, 0x12, 0x57, - 0x41, 0x19, 0xf2, 0x00, 0x1a, 0x43, 0x36, 0xf6, 0x7c, 0x67, 0x38, 0x0d, 0x46, 0xa7, 0xdd, 0x3a, - 0xaa, 0x74, 0xf3, 0x2a, 0x03, 0x21, 0x30, 0x10, 0xfc, 0xc3, 0x15, 0x1b, 0x86, 0xc9, 0x8c, 0xec, - 0x41, 0x7d, 0x34, 0x61, 0xa3, 0x53, 0x87, 0x2f, 0xba, 0x26, 0x6a, 0x5e, 0xcb, 0x6b, 0x3e, 0x14, - 0xdc, 0xa7, 0x8b, 0xc3, 0x15, 0xbb, 0x36, 0x92, 0x43, 0x72, 0x0f, 0x4c, 0xe6, 0xbb, 0x6a, 0xbb, - 0x06, 0x2a, 0x5d, 0x2f, 0x7c, 0x17, 0xdf, 0xd5, 0x9b, 0xd5, 0x99, 0x1a, 0x93, 0x3e, 0xac, 0x0b, - 0x47, 0xf1, 0x78, 0x77, 0x03, 0x75, 0x36, 0x0b, 0x1b, 0x21, 0xef, 0x70, 0xc5, 0x56, 0x52, 0xc2, - 0x7c, 0x2e, 0x9b, 0x7a, 0x67, 0x2c, 0x12, 0x87, 0xbb, 0x5a, 0x66, 0xbe, 0x47, 0x92, 0x8f, 0xc7, - 0x33, 0x5d, 0x3d, 0x19, 0xd4, 0x60, 0xed, 0x8c, 0x4e, 0xe7, 0xcc, 0x7a, 0x0b, 0x1a, 0x19, 0x4f, - 0x21, 0x5d, 0xa8, 0xcd, 0x58, 0x1c, 0xd3, 0x31, 0xeb, 0x1a, 0xb7, 0x8c, 0x3b, 0xa6, 0xad, 0xa7, - 0x56, 0x0b, 0x36, 0xb2, 0x7e, 0x62, 0xcd, 0x12, 0x45, 0xe1, 0x0b, 0x42, 0xf1, 0x8c, 0x45, 0xb1, - 0x70, 0x00, 0xa5, 0xa8, 0xa6, 0xe4, 0x36, 0x34, 0xd1, 0x0e, 0x8e, 0xe6, 0x0b, 0x3f, 0xad, 0xda, - 0x1b, 0x48, 0x3c, 0x51, 0x42, 0xdb, 0xd0, 0x08, 0xf7, 0xc2, 0x44, 0x64, 0x15, 0x45, 0x20, 0xdc, - 0x0b, 0x95, 0x80, 0xf5, 0x01, 0x74, 0x8a, 0xae, 0x44, 0x3a, 0xb0, 0x7a, 0xca, 0xce, 0xd5, 0x7e, - 0x62, 0x48, 0x36, 0xd5, 0xb5, 0x70, 0x0f, 0xd3, 0x56, 0x77, 0xfc, 0xa2, 0x92, 0x28, 0x27, 0xde, - 0x44, 0xf6, 0xa1, 0x2a, 0x62, 0x19, 0xb5, 0x1b, 0x7b, 0xbd, 0xbe, 0x0c, 0xf4, 0xbe, 0x0e, 0xf4, - 0xfe, 0x53, 0x1d, 0xe8, 0x83, 0xfa, 0x57, 0x2f, 0xb7, 0x57, 0xbe, 0xf8, 0xc3, 0xb6, 0x61, 0xa3, - 0x06, 0xb9, 0x29, 0x1c, 0x82, 0x7a, 0xbe, 0xe3, 0xb9, 0x6a, 0x9f, 0x1a, 0xce, 0x8f, 0x5c, 0x72, - 0x00, 0x9d, 0x51, 0xe0, 0xc7, 0xcc, 0x8f, 0xe7, 0xb1, 0x13, 0xd2, 0x88, 0xce, 0x62, 0x15, 0x6b, - 0xfa, 0xf3, 0x3f, 0xd4, 0xec, 0x63, 0xe4, 0xda, 0xed, 0x51, 0x9e, 0x40, 0x3e, 0x04, 0x38, 0xa3, - 0x53, 0xcf, 0xa5, 0x3c, 0x88, 0xe2, 0x6e, 0xf5, 0xd6, 0x6a, 0x46, 0xf9, 0x44, 0x33, 0x9e, 0x85, - 0x2e, 0xe5, 0x6c, 0x50, 0x15, 0x27, 0xb3, 0x33, 0xf2, 0xe4, 0x4d, 0x68, 0xd3, 0x30, 0x74, 0x62, - 0x4e, 0x39, 0x73, 0x86, 0xe7, 0x9c, 0xc5, 0x18, 0x8f, 0x1b, 0x76, 0x93, 0x86, 0xe1, 0x13, 0x41, - 0x1d, 0x08, 0xa2, 0xe5, 0x26, 0x5f, 0x13, 0x43, 0x85, 0x10, 0xa8, 0xba, 0x94, 0x53, 0xb4, 0xc6, - 0x86, 0x8d, 0x63, 0x41, 0x0b, 0x29, 0x9f, 0xa8, 0x3b, 0xe2, 0x98, 0x5c, 0x87, 0xf5, 0x09, 0xf3, - 0xc6, 0x13, 0x8e, 0xd7, 0x5a, 0xb5, 0xd5, 0x4c, 0x18, 0x3e, 0x8c, 0x82, 0x33, 0x86, 0xd9, 0xa2, - 0x6e, 0xcb, 0x89, 0xf5, 0x17, 0x03, 0xae, 0x5c, 0x0a, 0x2f, 0xb1, 0xee, 0x84, 0xc6, 0x13, 0xbd, - 0x97, 0x18, 0x93, 0xb7, 0xc5, 0xba, 0xd4, 0x65, 0x91, 0xca, 0x62, 0x4d, 0x75, 0xe3, 0x43, 0x24, - 0xaa, 0x8b, 0x2a, 0x11, 0xf2, 0x18, 0x3a, 0x53, 0x1a, 0x73, 0x47, 0x46, 0x81, 0x83, 0x59, 0x6a, - 0x35, 0x17, 0x99, 0x9f, 0x50, 0x1d, 0x2d, 0xc2, 0x39, 0x95, 0x7a, 0x6b, 0x9a, 0xa3, 0x92, 0x43, - 0xd8, 0x1c, 0x9e, 0xbf, 0xa0, 0x3e, 0xf7, 0x7c, 0xe6, 0x5c, 0xb2, 0x79, 0x5b, 0x2d, 0xf5, 0xf8, - 0xcc, 0x73, 0x99, 0x3f, 0xd2, 0xc6, 0xbe, 0x9a, 0xa8, 0x24, 0x1f, 0x23, 0xb6, 0x6e, 0x41, 0x2b, - 0x9f, 0x0b, 0x48, 0x0b, 0x2a, 0x7c, 0xa1, 0x6e, 0x58, 0xe1, 0x0b, 0xcb, 0x4a, 0x3c, 0x30, 0x09, - 0xc8, 0x4b, 0x32, 0x3b, 0xd0, 0x2e, 0x24, 0x87, 0x8c, 0xb9, 0x8d, 0xac, 0xb9, 0xad, 0x36, 0x34, - 0x73, 0x39, 0xc1, 0xfa, 0x7c, 0x0d, 0xea, 0x36, 0x8b, 0x43, 0xe1, 0x4c, 0x64, 0x1f, 0x4c, 0xb6, - 0x18, 0x31, 0x99, 0x8e, 0x8d, 0x42, 0xb2, 0x93, 0x32, 0x8f, 0x35, 0x5f, 0xa4, 0x85, 0x44, 0x98, - 0xec, 0xe4, 0xa0, 0xe4, 0x6a, 0x51, 0x29, 0x8b, 0x25, 0x77, 0xf3, 0x58, 0xb2, 0x59, 0x90, 0x2d, - 0x80, 0xc9, 0x4e, 0x0e, 0x4c, 0x8a, 0x0b, 0xe7, 0xd0, 0xe4, 0x7e, 0x09, 0x9a, 0x14, 0x8f, 0xbf, - 0x04, 0x4e, 0xee, 0x97, 0xc0, 0x49, 0xf7, 0xd2, 0x5e, 0xa5, 0x78, 0x72, 0x37, 0x8f, 0x27, 0xc5, - 0xeb, 0x14, 0x00, 0xe5, 0xc3, 0x32, 0x40, 0xb9, 0x59, 0xd0, 0x59, 0x8a, 0x28, 0xef, 0x5f, 0x42, - 0x94, 0xeb, 0x05, 0xd5, 0x12, 0x48, 0xb9, 0x9f, 0xcb, 0xf5, 0x50, 0x7a, 0xb7, 0xf2, 0x64, 0x4f, - 0xbe, 0x77, 0x19, 0x8d, 0x6e, 0x14, 0x3f, 0x6d, 0x19, 0x1c, 0xed, 0x16, 0xe0, 0xe8, 0x5a, 0xf1, - 0x94, 0x05, 0x3c, 0x4a, 0x51, 0x65, 0x47, 0xc4, 0x7d, 0xc1, 0xd3, 0x44, 0x8e, 0x60, 0x51, 0x14, - 0x44, 0x2a, 0x61, 0xcb, 0x89, 0x75, 0x47, 0x64, 0xa2, 0xd4, 0xbf, 0x5e, 0x81, 0x40, 0xe8, 0xf4, - 0x19, 0xef, 0xb2, 0xbe, 0x34, 0x52, 0x5d, 0x8c, 0xe8, 0x6c, 0x16, 0x33, 0x55, 0x16, 0xcb, 0x00, - 0x53, 0x25, 0x0f, 0x4c, 0xdb, 0xd0, 0x10, 0xb9, 0xb2, 0x80, 0x39, 0x34, 0xd4, 0x98, 0x43, 0xbe, - 0x03, 0x57, 0x30, 0xcf, 0x48, 0xf8, 0x52, 0x81, 0x58, 0xc5, 0x40, 0x6c, 0x0b, 0x86, 0xb4, 0x98, - 0x4c, 0x80, 0xef, 0xc0, 0xd5, 0x8c, 0xac, 0x58, 0x17, 0x73, 0x9c, 0x4c, 0xbe, 0x9d, 0x44, 0xfa, - 0x20, 0x0c, 0x0f, 0x69, 0x3c, 0xb1, 0x7e, 0x94, 0x1a, 0x28, 0xc5, 0x33, 0x02, 0xd5, 0x51, 0xe0, - 0xca, 0x7b, 0x37, 0x6d, 0x1c, 0x0b, 0x8c, 0x9b, 0x06, 0x63, 0x3c, 0x9c, 0x69, 0x8b, 0xa1, 0x90, - 0x4a, 0x42, 0xc9, 0x94, 0x31, 0x63, 0xfd, 0xd2, 0x48, 0xd7, 0x4b, 0x21, 0xae, 0x0c, 0x8d, 0x8c, - 0xff, 0x04, 0x8d, 0x2a, 0xaf, 0x87, 0x46, 0xd6, 0x85, 0x91, 0x7e, 0xb2, 0x04, 0x67, 0xbe, 0xd9, - 0x15, 0x85, 0xf7, 0x78, 0xbe, 0xcb, 0x16, 0x68, 0xd2, 0x55, 0x5b, 0x4e, 0x74, 0x09, 0xb0, 0x8e, - 0x66, 0xce, 0x97, 0x00, 0x35, 0xa4, 0xc9, 0x09, 0xb9, 0x8d, 0xf8, 0x14, 0x3c, 0x57, 0xa1, 0xda, - 0xec, 0xab, 0x6a, 0xfa, 0x58, 0x10, 0x6d, 0xc9, 0xcb, 0x64, 0x5b, 0x33, 0x07, 0x6e, 0x6f, 0x80, - 0x29, 0x0e, 0x1a, 0x87, 0x74, 0xc4, 0x30, 0xf2, 0x4c, 0x3b, 0x25, 0x58, 0xc7, 0x40, 0x2e, 0x47, - 0x3c, 0xf9, 0x00, 0xaa, 0x9c, 0x8e, 0x85, 0xbd, 0x85, 0xc9, 0x5a, 0x7d, 0xd9, 0x00, 0xf4, 0x3f, - 0x3e, 0x39, 0xa6, 0x5e, 0x34, 0xb8, 0x2e, 0x4c, 0xf5, 0xb7, 0x97, 0xdb, 0x2d, 0x21, 0x73, 0x37, - 0x98, 0x79, 0x9c, 0xcd, 0x42, 0x7e, 0x6e, 0xa3, 0x8e, 0xf5, 0x77, 0x43, 0x20, 0x41, 0x2e, 0x13, - 0x94, 0x1a, 0x4e, 0xbb, 0x7b, 0x25, 0x03, 0xda, 0x5f, 0xcf, 0x98, 0xff, 0x0f, 0x30, 0xa6, 0xb1, - 0xf3, 0x19, 0xf5, 0x39, 0x73, 0x95, 0x45, 0xcd, 0x31, 0x8d, 0x7f, 0x86, 0x04, 0x51, 0xe1, 0x08, - 0xf6, 0x3c, 0x66, 0x2e, 0x9a, 0x76, 0xd5, 0xae, 0x8d, 0x69, 0xfc, 0x2c, 0x66, 0x6e, 0x72, 0xaf, - 0xda, 0xeb, 0xdf, 0x2b, 0x6f, 0xc7, 0x7a, 0xd1, 0x8e, 0xff, 0xc8, 0xf8, 0x70, 0x0a, 0x92, 0xff, - 0xfb, 0xf7, 0xfe, 0xab, 0x21, 0x6a, 0x83, 0x7c, 0x1a, 0x26, 0x47, 0x70, 0x25, 0x89, 0x23, 0x67, - 0x8e, 0xf1, 0xa5, 0x7d, 0xe9, 0xd5, 0xe1, 0xd7, 0x39, 0xcb, 0x93, 0x63, 0xf2, 0x63, 0xb8, 0x51, - 0xc8, 0x02, 0xc9, 0x82, 0x95, 0x57, 0x26, 0x83, 0x6b, 0xf9, 0x64, 0xa0, 0xd7, 0xd3, 0x96, 0x58, - 0xfd, 0x06, 0x9e, 0xfd, 0x2d, 0x51, 0x28, 0x65, 0xc1, 0xa3, 0xec, 0x5b, 0x5a, 0xbf, 0x36, 0xa0, - 0x5d, 0x38, 0x0c, 0xb9, 0x07, 0x20, 0x53, 0x6b, 0xec, 0xbd, 0x60, 0x85, 0x2c, 0x86, 0x26, 0x7b, - 0xe2, 0xbd, 0x60, 0xea, 0xe0, 0xe6, 0x50, 0x13, 0xc8, 0x7b, 0x50, 0x67, 0xaa, 0x80, 0x53, 0xb7, - 0xbd, 0x56, 0xa8, 0xeb, 0x94, 0x4e, 0x22, 0x46, 0xbe, 0x0b, 0x66, 0x62, 0xc3, 0x42, 0xf1, 0x9e, - 0x98, 0x5c, 0x6f, 0x94, 0x08, 0x5a, 0x1f, 0x41, 0xbb, 0x70, 0x0c, 0xf2, 0x7f, 0x60, 0xce, 0xe8, - 0x42, 0x55, 0xe1, 0xb2, 0x7e, 0xab, 0xcf, 0xe8, 0x02, 0x0b, 0x70, 0x72, 0x03, 0x6a, 0x82, 0x39, - 0xa6, 0xf2, 0x2b, 0xac, 0xda, 0xeb, 0x33, 0xba, 0xf8, 0x88, 0xc6, 0xd6, 0x0e, 0xb4, 0xf2, 0x47, - 0xd3, 0xa2, 0x1a, 0x11, 0xa5, 0xe8, 0xc1, 0x98, 0x59, 0xf7, 0xa0, 0x5d, 0x38, 0x11, 0xb1, 0xa0, - 0x19, 0xce, 0x87, 0xce, 0x29, 0x3b, 0x77, 0xf0, 0xc8, 0xe8, 0x33, 0xa6, 0xdd, 0x08, 0xe7, 0xc3, - 0x8f, 0xd9, 0xf9, 0x53, 0x41, 0xb2, 0x9e, 0x40, 0x2b, 0x5f, 0x1f, 0x8b, 0x9c, 0x19, 0x05, 0x73, - 0xdf, 0xc5, 0xf5, 0xd7, 0x6c, 0x39, 0x11, 0x2d, 0xf6, 0x59, 0x20, 0xdd, 0x24, 0x5b, 0x10, 0x9f, - 0x04, 0x9c, 0x65, 0xaa, 0x6a, 0x29, 0x63, 0xfd, 0x62, 0x0d, 0xd6, 0x65, 0xb1, 0x4e, 0xfa, 0xf9, - 0x56, 0x50, 0xf8, 0x88, 0xd2, 0x94, 0x54, 0xa5, 0x98, 0xe0, 0xf0, 0x9b, 0xc5, 0x7e, 0x6a, 0xd0, - 0xb8, 0x78, 0xb9, 0x5d, 0x43, 0x0c, 0x3b, 0x7a, 0x94, 0x36, 0x57, 0xcb, 0x7a, 0x0f, 0xdd, 0xc9, - 0x55, 0x5f, 0xbb, 0x93, 0xbb, 0x01, 0x35, 0x7f, 0x3e, 0x73, 0xf8, 0x22, 0x56, 0xb9, 0x60, 0xdd, - 0x9f, 0xcf, 0x9e, 0x2e, 0xf0, 0xd3, 0xf1, 0x80, 0xd3, 0x29, 0xb2, 0x64, 0x26, 0xa8, 0x23, 0x41, - 0x30, 0xf7, 0xa1, 0x99, 0x81, 0x7a, 0xcf, 0x55, 0x25, 0x63, 0x2b, 0xeb, 0x8d, 0x47, 0x8f, 0xd4, - 0x2d, 0x1b, 0x09, 0xf4, 0x1f, 0xb9, 0xe4, 0x4e, 0xbe, 0x71, 0xc1, 0x0a, 0xa1, 0x8e, 0x8e, 0x9f, - 0xe9, 0x4d, 0x44, 0x7d, 0x20, 0x0e, 0x20, 0x42, 0x41, 0x8a, 0x98, 0x28, 0x52, 0x17, 0x04, 0x64, - 0xbe, 0x05, 0xed, 0x14, 0x64, 0xa5, 0x08, 0xc8, 0x55, 0x52, 0x32, 0x0a, 0xbe, 0x0b, 0x9b, 0x3e, - 0x5b, 0x70, 0xa7, 0x28, 0xdd, 0x40, 0x69, 0x22, 0x78, 0x27, 0x79, 0x8d, 0x6f, 0x43, 0x2b, 0x4d, - 0x16, 0x28, 0xbb, 0x21, 0xdb, 0xc7, 0x84, 0x8a, 0x62, 0x37, 0xa1, 0x9e, 0x94, 0x38, 0x4d, 0x14, - 0xa8, 0x51, 0x59, 0xd9, 0x24, 0x45, 0x53, 0xc4, 0xe2, 0xf9, 0x94, 0xab, 0x45, 0x5a, 0x28, 0x83, - 0x45, 0x93, 0x2d, 0xe9, 0x28, 0x7b, 0x1b, 0x9a, 0x3a, 0xec, 0xa4, 0x5c, 0x1b, 0xe5, 0x36, 0x34, - 0x11, 0x85, 0x76, 0xa0, 0x13, 0x46, 0x41, 0x18, 0xc4, 0x2c, 0x72, 0xa8, 0xeb, 0x46, 0x2c, 0x8e, - 0xbb, 0x1d, 0xb9, 0x9e, 0xa6, 0x1f, 0x48, 0xb2, 0xf5, 0x1e, 0xd4, 0x74, 0xed, 0xb6, 0x09, 0x6b, - 0x68, 0x75, 0x74, 0xc1, 0xaa, 0x2d, 0x27, 0x02, 0x25, 0x0e, 0xc2, 0x50, 0xbd, 0x40, 0x88, 0xa1, - 0xf5, 0x73, 0xa8, 0xa9, 0x0f, 0x56, 0xda, 0x97, 0x7e, 0x1f, 0x36, 0x42, 0x1a, 0x89, 0x6b, 0x64, - 0xbb, 0x53, 0xdd, 0x1d, 0x1c, 0xd3, 0x88, 0x3f, 0x61, 0x3c, 0xd7, 0xa4, 0x36, 0x50, 0x5e, 0x92, - 0xac, 0xfb, 0xd0, 0xcc, 0xc9, 0x88, 0x63, 0xa1, 0x1f, 0xe9, 0x48, 0xc3, 0x49, 0xb2, 0x73, 0x25, - 0xdd, 0xd9, 0x7a, 0x00, 0x66, 0xf2, 0x6d, 0x44, 0x11, 0xab, 0xaf, 0x6e, 0x28, 0x73, 0xcb, 0x29, - 0x36, 0xde, 0xc1, 0x67, 0x2c, 0x52, 0x31, 0x21, 0x27, 0xd6, 0xb3, 0x4c, 0x66, 0x90, 0x79, 0x9b, - 0xdc, 0x85, 0x9a, 0xca, 0x0c, 0x2a, 0x2a, 0x75, 0x8b, 0x7d, 0x8c, 0xa9, 0x41, 0xb7, 0xd8, 0x32, - 0x51, 0xa4, 0xcb, 0x56, 0xb2, 0xcb, 0x4e, 0xa1, 0xae, 0xa3, 0x3f, 0x9f, 0x26, 0xe5, 0x8a, 0x9d, - 0x62, 0x9a, 0x54, 0x8b, 0xa6, 0x82, 0xc2, 0x3b, 0x62, 0x6f, 0xec, 0x33, 0xd7, 0x49, 0x43, 0x08, - 0xf7, 0xa8, 0xdb, 0x6d, 0xc9, 0xf8, 0x44, 0xc7, 0x8b, 0xf5, 0x2e, 0xac, 0xcb, 0xb3, 0x09, 0xfb, - 0x88, 0x95, 0x75, 0x5d, 0x2f, 0xc6, 0xa5, 0xc0, 0xf1, 0x7b, 0x03, 0xea, 0x3a, 0x79, 0x96, 0x2a, - 0xe5, 0x0e, 0x5d, 0xf9, 0xba, 0x87, 0xfe, 0xef, 0x27, 0x9e, 0xbb, 0x40, 0x64, 0x7e, 0x39, 0x0b, - 0xb8, 0xe7, 0x8f, 0x1d, 0x69, 0x6b, 0x99, 0x83, 0x3a, 0xc8, 0x39, 0x41, 0xc6, 0xb1, 0xa0, 0xef, - 0x7d, 0xbe, 0x06, 0xed, 0x83, 0xc1, 0xc3, 0xa3, 0x83, 0x30, 0x9c, 0x7a, 0x23, 0x8a, 0xbd, 0xc2, - 0x2e, 0x54, 0xb1, 0x5d, 0x2a, 0x79, 0xee, 0xed, 0x95, 0xf5, 0xed, 0x64, 0x0f, 0xd6, 0xb0, 0x6b, - 0x22, 0x65, 0xaf, 0xbe, 0xbd, 0xd2, 0xf6, 0x5d, 0x6c, 0x22, 0xfb, 0xaa, 0xcb, 0x8f, 0xbf, 0xbd, - 0xb2, 0x1e, 0x9e, 0xfc, 0x00, 0xcc, 0xb4, 0x9d, 0x59, 0xf6, 0x04, 0xdc, 0x5b, 0xda, 0xcd, 0x0b, - 0xfd, 0xb4, 0xf4, 0x5b, 0xf6, 0x92, 0xd9, 0x5b, 0xda, 0xf6, 0x92, 0x7d, 0xa8, 0xe9, 0x82, 0xb9, - 0xfc, 0x91, 0xb6, 0xb7, 0xa4, 0xd3, 0x16, 0xe6, 0x91, 0x1d, 0x4a, 0xd9, 0x4b, 0x72, 0xaf, 0xf4, - 0x39, 0x80, 0xdc, 0x83, 0x75, 0x55, 0xc5, 0x94, 0x3e, 0xd4, 0xf6, 0xca, 0xfb, 0x65, 0x71, 0xc9, - 0xb4, 0x47, 0x5b, 0xf6, 0xda, 0xdd, 0x5b, 0xfa, 0x6e, 0x41, 0x0e, 0x00, 0x32, 0x8d, 0xc6, 0xd2, - 0x67, 0xec, 0xde, 0xf2, 0xf7, 0x08, 0xf2, 0x00, 0xea, 0xe9, 0x1b, 0x53, 0xf9, 0xc3, 0x74, 0x6f, - 0xd9, 0x13, 0xc1, 0xe0, 0x8d, 0x7f, 0xfe, 0x69, 0xcb, 0xf8, 0xd5, 0xc5, 0x96, 0xf1, 0xe5, 0xc5, - 0x96, 0xf1, 0xd5, 0xc5, 0x96, 0xf1, 0xbb, 0x8b, 0x2d, 0xe3, 0x8f, 0x17, 0x5b, 0xc6, 0x6f, 0xfe, - 0xbc, 0x65, 0x0c, 0xd7, 0xd1, 0xfd, 0xdf, 0xff, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x8d, - 0xcb, 0x04, 0x88, 0x19, 0x00, 0x00, +var fileDescriptor_types_e441973ce6650a0d = []byte{ + // 2223 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcd, 0x73, 0x1c, 0x47, + 0x15, 0xd7, 0xec, 0xf7, 0xbc, 0xd5, 0x7e, 0xa4, 0x2d, 0xdb, 0xeb, 0x25, 0x48, 0xae, 0x31, 0x24, + 0x12, 0x51, 0x56, 0x89, 0x82, 0x29, 0x39, 0x0e, 0x54, 0x69, 0x6d, 0x83, 0x54, 0x49, 0x40, 0x8c, + 0x6d, 0x71, 0xa1, 0x6a, 0xaa, 0x77, 0xa7, 0xb5, 0x3b, 0xa5, 0xdd, 0x99, 0xc9, 0x4c, 0xaf, 0xb2, + 0xe2, 0xc8, 0x39, 0x87, 0x1c, 0xf8, 0x13, 0x38, 0xf0, 0x27, 0xe4, 0xc8, 0x89, 0xca, 0x91, 0x03, + 0x67, 0x03, 0xa2, 0x38, 0xc0, 0x95, 0xa2, 0x8a, 0x23, 0xd5, 0xaf, 0x7b, 0x3e, 0x35, 0x6b, 0xe2, + 0xc0, 0x89, 0xcb, 0x6e, 0xf7, 0xfb, 0xe8, 0x8f, 0x37, 0xef, 0xbd, 0xdf, 0x7b, 0x0d, 0xb7, 0xe8, + 0x68, 0xec, 0xec, 0xf1, 0x4b, 0x9f, 0x85, 0xf2, 0x77, 0xe0, 0x07, 0x1e, 0xf7, 0x48, 0x15, 0x27, + 0xfd, 0xb7, 0x27, 0x0e, 0x9f, 0x2e, 0x46, 0x83, 0xb1, 0x37, 0xdf, 0x9b, 0x78, 0x13, 0x6f, 0x0f, + 0xb9, 0xa3, 0xc5, 0x19, 0xce, 0x70, 0x82, 0x23, 0xa9, 0xd5, 0x7f, 0x98, 0x12, 0xe7, 0xcc, 0xb5, + 0x59, 0x30, 0x77, 0x5c, 0x9e, 0x1e, 0x8e, 0x83, 0x4b, 0x9f, 0x7b, 0x7b, 0x73, 0x16, 0x9c, 0xcf, + 0x98, 0xfa, 0x53, 0xca, 0x07, 0xff, 0x51, 0x79, 0xe6, 0x8c, 0xc2, 0xbd, 0xb1, 0x37, 0x9f, 0x7b, + 0x6e, 0xfa, 0xb0, 0xfd, 0xad, 0x89, 0xe7, 0x4d, 0x66, 0x2c, 0x39, 0x1c, 0x77, 0xe6, 0x2c, 0xe4, + 0x74, 0xee, 0x4b, 0x01, 0xe3, 0x77, 0x15, 0xa8, 0x9b, 0xec, 0x93, 0x05, 0x0b, 0x39, 0xd9, 0x86, + 0x0a, 0x1b, 0x4f, 0xbd, 0x5e, 0xe9, 0xae, 0xb6, 0xdd, 0xdc, 0x27, 0x03, 0xb9, 0x90, 0xe2, 0x3e, + 0x19, 0x4f, 0xbd, 0xa3, 0x35, 0x13, 0x25, 0xc8, 0x5b, 0x50, 0x3d, 0x9b, 0x2d, 0xc2, 0x69, 0xaf, + 0x8c, 0xa2, 0x37, 0xb2, 0xa2, 0x3f, 0x14, 0xac, 0xa3, 0x35, 0x53, 0xca, 0x88, 0x65, 0x1d, 0xf7, + 0xcc, 0xeb, 0x55, 0x8a, 0x96, 0x3d, 0x76, 0xcf, 0x70, 0x59, 0x21, 0x41, 0x0e, 0x00, 0x42, 0xc6, + 0x2d, 0xcf, 0xe7, 0x8e, 0xe7, 0xf6, 0xaa, 0x28, 0x7f, 0x3b, 0x2b, 0xff, 0x94, 0xf1, 0x9f, 0x20, + 0xfb, 0x68, 0xcd, 0xd4, 0xc3, 0x68, 0x22, 0x34, 0x1d, 0xd7, 0xe1, 0xd6, 0x78, 0x4a, 0x1d, 0xb7, + 0x57, 0x2b, 0xd2, 0x3c, 0x76, 0x1d, 0xfe, 0x48, 0xb0, 0x85, 0xa6, 0x13, 0x4d, 0xc4, 0x55, 0x3e, + 0x59, 0xb0, 0xe0, 0xb2, 0x57, 0x2f, 0xba, 0xca, 0x4f, 0x05, 0x4b, 0x5c, 0x05, 0x65, 0xc8, 0x43, + 0x68, 0x8e, 0xd8, 0xc4, 0x71, 0xad, 0xd1, 0xcc, 0x1b, 0x9f, 0xf7, 0x1a, 0xa8, 0xd2, 0xcb, 0xaa, + 0x0c, 0x85, 0xc0, 0x50, 0xf0, 0x8f, 0xd6, 0x4c, 0x18, 0xc5, 0x33, 0xb2, 0x0f, 0x8d, 0xf1, 0x94, + 0x8d, 0xcf, 0x2d, 0xbe, 0xec, 0xe9, 0xa8, 0x79, 0x33, 0xab, 0xf9, 0x48, 0x70, 0x9f, 0x2d, 0x8f, + 0xd6, 0xcc, 0xfa, 0x58, 0x0e, 0xc9, 0x7d, 0xd0, 0x99, 0x6b, 0xab, 0xed, 0x9a, 0xa8, 0x74, 0x2b, + 0xf7, 0x5d, 0x5c, 0x3b, 0xda, 0xac, 0xc1, 0xd4, 0x98, 0x0c, 0xa0, 0x26, 0x9c, 0xc1, 0xe1, 0xbd, + 0x75, 0xd4, 0xd9, 0xc8, 0x6d, 0x84, 0xbc, 0xa3, 0x35, 0x53, 0x49, 0x09, 0xf3, 0xd9, 0x6c, 0xe6, + 0x5c, 0xb0, 0x40, 0x1c, 0xee, 0x46, 0x91, 0xf9, 0x1e, 0x4b, 0x3e, 0x1e, 0x4f, 0xb7, 0xa3, 0xc9, + 0xb0, 0x0e, 0xd5, 0x0b, 0x3a, 0x5b, 0x30, 0xe3, 0x4d, 0x68, 0xa6, 0x3c, 0x85, 0xf4, 0xa0, 0x3e, + 0x67, 0x61, 0x48, 0x27, 0xac, 0xa7, 0xdd, 0xd5, 0xb6, 0x75, 0x33, 0x9a, 0x1a, 0x6d, 0x58, 0x4f, + 0xfb, 0x89, 0x31, 0x8f, 0x15, 0x85, 0x2f, 0x08, 0xc5, 0x0b, 0x16, 0x84, 0xc2, 0x01, 0x94, 0xa2, + 0x9a, 0x92, 0x7b, 0xd0, 0x42, 0x3b, 0x58, 0x11, 0x5f, 0xf8, 0x69, 0xc5, 0x5c, 0x47, 0xe2, 0xa9, + 0x12, 0xda, 0x82, 0xa6, 0xbf, 0xef, 0xc7, 0x22, 0x65, 0x14, 0x01, 0x7f, 0xdf, 0x57, 0x02, 0xc6, + 0xfb, 0xd0, 0xcd, 0xbb, 0x12, 0xe9, 0x42, 0xf9, 0x9c, 0x5d, 0xaa, 0xfd, 0xc4, 0x90, 0x6c, 0xa8, + 0x6b, 0xe1, 0x1e, 0xba, 0xa9, 0xee, 0xf8, 0x79, 0x29, 0x56, 0x8e, 0xbd, 0x89, 0x1c, 0x40, 0x45, + 0x04, 0x15, 0x6a, 0x37, 0xf7, 0xfb, 0x03, 0x19, 0x71, 0x83, 0x28, 0xe2, 0x06, 0xcf, 0xa2, 0x88, + 0x1b, 0x36, 0xbe, 0x7c, 0xb1, 0xb5, 0xf6, 0xf9, 0x1f, 0xb7, 0x34, 0x13, 0x35, 0xc8, 0x1d, 0xe1, + 0x10, 0xd4, 0x71, 0x2d, 0xc7, 0x56, 0xfb, 0xd4, 0x71, 0x7e, 0x6c, 0x93, 0x43, 0xe8, 0x8e, 0x3d, + 0x37, 0x64, 0x6e, 0xb8, 0x08, 0x2d, 0x9f, 0x06, 0x74, 0x1e, 0xaa, 0x58, 0x8b, 0x3e, 0xff, 0xa3, + 0x88, 0x7d, 0x82, 0x5c, 0xb3, 0x33, 0xce, 0x12, 0xc8, 0x07, 0x00, 0x17, 0x74, 0xe6, 0xd8, 0x94, + 0x7b, 0x41, 0xd8, 0xab, 0xdc, 0x2d, 0xa7, 0x94, 0x4f, 0x23, 0xc6, 0x73, 0xdf, 0xa6, 0x9c, 0x0d, + 0x2b, 0xe2, 0x64, 0x66, 0x4a, 0x9e, 0xbc, 0x01, 0x1d, 0xea, 0xfb, 0x56, 0xc8, 0x29, 0x67, 0xd6, + 0xe8, 0x92, 0xb3, 0x10, 0xe3, 0x71, 0xdd, 0x6c, 0x51, 0xdf, 0x7f, 0x2a, 0xa8, 0x43, 0x41, 0x34, + 0xec, 0xf8, 0x6b, 0x62, 0xa8, 0x10, 0x02, 0x15, 0x9b, 0x72, 0x8a, 0xd6, 0x58, 0x37, 0x71, 0x2c, + 0x68, 0x3e, 0xe5, 0x53, 0x75, 0x47, 0x1c, 0x93, 0x5b, 0x50, 0x9b, 0x32, 0x67, 0x32, 0xe5, 0x78, + 0xad, 0xb2, 0xa9, 0x66, 0xc2, 0xf0, 0x7e, 0xe0, 0x5d, 0x30, 0xcc, 0x16, 0x0d, 0x53, 0x4e, 0x8c, + 0xbf, 0x6a, 0xf0, 0xda, 0xb5, 0xf0, 0x12, 0xeb, 0x4e, 0x69, 0x38, 0x8d, 0xf6, 0x12, 0x63, 0xf2, + 0x96, 0x58, 0x97, 0xda, 0x2c, 0x50, 0x59, 0xac, 0xa5, 0x6e, 0x7c, 0x84, 0x44, 0x75, 0x51, 0x25, + 0x42, 0x9e, 0x40, 0x77, 0x46, 0x43, 0x6e, 0xc9, 0x28, 0xb0, 0x30, 0x4b, 0x95, 0x33, 0x91, 0xf9, + 0x11, 0x8d, 0xa2, 0x45, 0x38, 0xa7, 0x52, 0x6f, 0xcf, 0x32, 0x54, 0x72, 0x04, 0x1b, 0xa3, 0xcb, + 0x5f, 0x50, 0x97, 0x3b, 0x2e, 0xb3, 0xae, 0xd9, 0xbc, 0xa3, 0x96, 0x7a, 0x72, 0xe1, 0xd8, 0xcc, + 0x1d, 0x47, 0xc6, 0xbe, 0x11, 0xab, 0xc4, 0x1f, 0x23, 0x34, 0xee, 0x42, 0x3b, 0x9b, 0x0b, 0x48, + 0x1b, 0x4a, 0x7c, 0xa9, 0x6e, 0x58, 0xe2, 0x4b, 0xc3, 0x88, 0x3d, 0x30, 0x0e, 0xc8, 0x6b, 0x32, + 0x3b, 0xd0, 0xc9, 0x25, 0x87, 0x94, 0xb9, 0xb5, 0xb4, 0xb9, 0x8d, 0x0e, 0xb4, 0x32, 0x39, 0xc1, + 0xf8, 0xac, 0x0a, 0x0d, 0x93, 0x85, 0xbe, 0x70, 0x26, 0x72, 0x00, 0x3a, 0x5b, 0x8e, 0x99, 0x4c, + 0xc7, 0x5a, 0x2e, 0xd9, 0x49, 0x99, 0x27, 0x11, 0x5f, 0xa4, 0x85, 0x58, 0x98, 0xec, 0x64, 0xa0, + 0xe4, 0x46, 0x5e, 0x29, 0x8d, 0x25, 0xbb, 0x59, 0x2c, 0xd9, 0xc8, 0xc9, 0xe6, 0xc0, 0x64, 0x27, + 0x03, 0x26, 0xf9, 0x85, 0x33, 0x68, 0xf2, 0xa0, 0x00, 0x4d, 0xf2, 0xc7, 0x5f, 0x01, 0x27, 0x0f, + 0x0a, 0xe0, 0xa4, 0x77, 0x6d, 0xaf, 0x42, 0x3c, 0xd9, 0xcd, 0xe2, 0x49, 0xfe, 0x3a, 0x39, 0x40, + 0xf9, 0xa0, 0x08, 0x50, 0xee, 0xe4, 0x74, 0x56, 0x22, 0xca, 0x7b, 0xd7, 0x10, 0xe5, 0x56, 0x4e, + 0xb5, 0x00, 0x52, 0x1e, 0x64, 0x72, 0x3d, 0x14, 0xde, 0xad, 0x38, 0xd9, 0x93, 0xef, 0x5d, 0x47, + 0xa3, 0xdb, 0xf9, 0x4f, 0x5b, 0x04, 0x47, 0x7b, 0x39, 0x38, 0xba, 0x99, 0x3f, 0x65, 0x0e, 0x8f, + 0x12, 0x54, 0xd9, 0x11, 0x71, 0x9f, 0xf3, 0x34, 0x91, 0x23, 0x58, 0x10, 0x78, 0x81, 0x4a, 0xd8, + 0x72, 0x62, 0x6c, 0x8b, 0x4c, 0x94, 0xf8, 0xd7, 0x4b, 0x10, 0x08, 0x9d, 0x3e, 0xe5, 0x5d, 0xc6, + 0x17, 0x5a, 0xa2, 0x8b, 0x11, 0x9d, 0xce, 0x62, 0xba, 0xca, 0x62, 0x29, 0x60, 0x2a, 0x65, 0x81, + 0x69, 0x0b, 0x9a, 0x22, 0x57, 0xe6, 0x30, 0x87, 0xfa, 0x11, 0xe6, 0x90, 0xef, 0xc0, 0x6b, 0x98, + 0x67, 0x24, 0x7c, 0xa9, 0x40, 0xac, 0x60, 0x20, 0x76, 0x04, 0x43, 0x5a, 0x4c, 0x26, 0xc0, 0xb7, + 0xe1, 0x46, 0x4a, 0x56, 0xac, 0x8b, 0x39, 0x4e, 0x26, 0xdf, 0x6e, 0x2c, 0x7d, 0xe8, 0xfb, 0x47, + 0x34, 0x9c, 0x1a, 0x1f, 0x27, 0x06, 0x4a, 0xf0, 0x8c, 0x40, 0x65, 0xec, 0xd9, 0xf2, 0xde, 0x2d, + 0x13, 0xc7, 0x02, 0xe3, 0x66, 0xde, 0x04, 0x0f, 0xa7, 0x9b, 0x62, 0x28, 0xa4, 0xe2, 0x50, 0xd2, + 0x65, 0xcc, 0x18, 0xbf, 0xd2, 0x92, 0xf5, 0x12, 0x88, 0x2b, 0x42, 0x23, 0xed, 0xbf, 0x41, 0xa3, + 0xd2, 0xab, 0xa1, 0x91, 0x71, 0xa5, 0x25, 0x9f, 0x2c, 0xc6, 0x99, 0xaf, 0x77, 0x45, 0xe1, 0x3d, + 0x8e, 0x6b, 0xb3, 0x25, 0x9a, 0xb4, 0x6c, 0xca, 0x49, 0x54, 0x02, 0xd4, 0xd0, 0xcc, 0xd9, 0x12, + 0xa0, 0x8e, 0x34, 0x39, 0x21, 0xf7, 0x10, 0x9f, 0xbc, 0x33, 0x15, 0xaa, 0xad, 0x81, 0x2a, 0xd4, + 0x4f, 0x04, 0xd1, 0x94, 0xbc, 0x54, 0xb6, 0xd5, 0x33, 0xe0, 0xf6, 0x3a, 0xe8, 0xe2, 0xa0, 0xa1, + 0x4f, 0xc7, 0x0c, 0x23, 0x4f, 0x37, 0x13, 0x82, 0x71, 0x02, 0xe4, 0x7a, 0xc4, 0x93, 0xf7, 0xa1, + 0xc2, 0xe9, 0x44, 0xd8, 0x5b, 0x98, 0xac, 0x3d, 0x90, 0x45, 0xfe, 0xe0, 0xc3, 0xd3, 0x13, 0xea, + 0x04, 0xc3, 0x5b, 0xc2, 0x54, 0x7f, 0x7f, 0xb1, 0xd5, 0x16, 0x32, 0xbb, 0xde, 0xdc, 0xe1, 0x6c, + 0xee, 0xf3, 0x4b, 0x13, 0x75, 0x8c, 0x7f, 0x68, 0x02, 0x09, 0x32, 0x99, 0xa0, 0xd0, 0x70, 0x91, + 0xbb, 0x97, 0x52, 0xa0, 0xfd, 0xd5, 0x8c, 0xf9, 0x4d, 0x80, 0x09, 0x0d, 0xad, 0x4f, 0xa9, 0xcb, + 0x99, 0xad, 0x2c, 0xaa, 0x4f, 0x68, 0xf8, 0x33, 0x24, 0x88, 0x0a, 0x47, 0xb0, 0x17, 0x21, 0xb3, + 0xd1, 0xb4, 0x65, 0xb3, 0x3e, 0xa1, 0xe1, 0xf3, 0x90, 0xd9, 0xf1, 0xbd, 0xea, 0xaf, 0x7e, 0xaf, + 0xac, 0x1d, 0x1b, 0x79, 0x3b, 0xfe, 0x33, 0xe5, 0xc3, 0x09, 0x48, 0xfe, 0xff, 0xdf, 0xfb, 0x6f, + 0x9a, 0xa8, 0x0d, 0xb2, 0x69, 0x98, 0x1c, 0xc3, 0x6b, 0x71, 0x1c, 0x59, 0x0b, 0x8c, 0xaf, 0xc8, + 0x97, 0x5e, 0x1e, 0x7e, 0xdd, 0x8b, 0x2c, 0x39, 0x24, 0x3f, 0x86, 0xdb, 0xb9, 0x2c, 0x10, 0x2f, + 0x58, 0x7a, 0x69, 0x32, 0xb8, 0x99, 0x4d, 0x06, 0xd1, 0x7a, 0x91, 0x25, 0xca, 0x5f, 0xc3, 0xb3, + 0xbf, 0x25, 0x0a, 0xa5, 0x34, 0x78, 0x14, 0x7d, 0x4b, 0xe3, 0xd7, 0x1a, 0x74, 0x72, 0x87, 0x21, + 0xdb, 0x50, 0x95, 0xf8, 0xa5, 0x65, 0xda, 0x51, 0xb4, 0x96, 0x3a, 0xaf, 0x14, 0x20, 0xef, 0x42, + 0x83, 0xa9, 0x9a, 0x4d, 0x5d, 0xf0, 0x66, 0xae, 0x94, 0x53, 0xf2, 0xb1, 0x18, 0xf9, 0x2e, 0xe8, + 0xb1, 0xd9, 0x72, 0xf5, 0x7a, 0x6c, 0x65, 0xa5, 0x94, 0x08, 0x1a, 0x0c, 0x9a, 0xa9, 0xed, 0xc9, + 0x37, 0x40, 0x9f, 0xd3, 0xa5, 0x2a, 0xba, 0x65, 0xb9, 0xd6, 0x98, 0xd3, 0x25, 0xd6, 0xdb, 0xe4, + 0x36, 0xd4, 0x05, 0x73, 0x42, 0xa5, 0xd1, 0xcb, 0x66, 0x6d, 0x4e, 0x97, 0x3f, 0xa2, 0x21, 0xb9, + 0x0b, 0xeb, 0xa2, 0xa9, 0xb0, 0x1c, 0x8f, 0x53, 0x4b, 0x75, 0x0b, 0x65, 0x13, 0x04, 0xed, 0xd8, + 0xe3, 0xf4, 0xe3, 0xd0, 0xd8, 0x81, 0x76, 0xf6, 0xe0, 0xd1, 0x62, 0x11, 0x44, 0xca, 0xc5, 0x0e, + 0x27, 0xcc, 0xb8, 0x0f, 0x9d, 0xdc, 0x79, 0x89, 0x01, 0x2d, 0x7f, 0x31, 0xb2, 0xce, 0xd9, 0xa5, + 0x85, 0x17, 0x42, 0x27, 0xd2, 0xcd, 0xa6, 0xbf, 0x18, 0x7d, 0xc8, 0x2e, 0x9f, 0x09, 0x92, 0xf1, + 0x14, 0xda, 0xd9, 0x82, 0x59, 0x24, 0xd1, 0xc0, 0x5b, 0xb8, 0x36, 0xae, 0x5f, 0x35, 0xe5, 0x44, + 0xf4, 0xdc, 0x17, 0x9e, 0xf4, 0x9b, 0x74, 0x85, 0x7c, 0xea, 0x71, 0x96, 0x2a, 0xb3, 0xa5, 0x8c, + 0xf1, 0xcb, 0x2a, 0xd4, 0x64, 0xf5, 0x4e, 0x06, 0xd9, 0xde, 0x50, 0x38, 0x8d, 0xd2, 0x94, 0x54, + 0xa5, 0x18, 0x03, 0xf3, 0x1b, 0xf9, 0x06, 0x6b, 0xd8, 0xbc, 0x7a, 0xb1, 0x55, 0x47, 0x50, 0x3b, + 0x7e, 0x9c, 0x74, 0x5b, 0xab, 0x9a, 0x91, 0xa8, 0xb5, 0xab, 0xbc, 0x72, 0x6b, 0x77, 0x1b, 0xea, + 0xee, 0x62, 0x6e, 0xf1, 0x65, 0xa8, 0x92, 0x43, 0xcd, 0x5d, 0xcc, 0x9f, 0x2d, 0xf1, 0xe3, 0x72, + 0x8f, 0xd3, 0x19, 0xb2, 0x64, 0x6a, 0x68, 0x20, 0x41, 0x30, 0x0f, 0xa0, 0x95, 0xc2, 0x7e, 0xc7, + 0x56, 0x35, 0x64, 0x3b, 0xed, 0xa3, 0xc7, 0x8f, 0xd5, 0x2d, 0x9b, 0x71, 0x2d, 0x70, 0x6c, 0x93, + 0xed, 0x6c, 0x27, 0x83, 0x25, 0x43, 0x03, 0x23, 0x21, 0xd5, 0xac, 0x88, 0x82, 0x41, 0x1c, 0x40, + 0xc4, 0x86, 0x14, 0xd1, 0x51, 0xa4, 0x21, 0x08, 0xc8, 0x7c, 0x13, 0x3a, 0x09, 0xea, 0x4a, 0x11, + 0x90, 0xab, 0x24, 0x64, 0x14, 0x7c, 0x07, 0x36, 0x5c, 0xb6, 0xe4, 0x56, 0x5e, 0xba, 0x89, 0xd2, + 0x44, 0xf0, 0x4e, 0xb3, 0x1a, 0xdf, 0x86, 0x76, 0x92, 0x3d, 0x50, 0x76, 0x5d, 0xf6, 0x93, 0x31, + 0x15, 0xc5, 0xee, 0x40, 0x23, 0xae, 0x79, 0x5a, 0x28, 0x50, 0xa7, 0xb2, 0xd4, 0x89, 0xab, 0xa8, + 0x80, 0x85, 0x8b, 0x19, 0x57, 0x8b, 0xb4, 0x51, 0x06, 0xab, 0x28, 0x53, 0xd2, 0x51, 0xf6, 0x1e, + 0xb4, 0xa2, 0xa0, 0x94, 0x72, 0x1d, 0x94, 0x5b, 0x8f, 0x88, 0x28, 0xb4, 0x03, 0x5d, 0x3f, 0xf0, + 0x7c, 0x2f, 0x64, 0x81, 0x45, 0x6d, 0x3b, 0x60, 0x61, 0xd8, 0xeb, 0xca, 0xf5, 0x22, 0xfa, 0xa1, + 0x24, 0x1b, 0xef, 0x42, 0x3d, 0x2a, 0xe6, 0x36, 0xa0, 0x3a, 0x8c, 0x13, 0x48, 0xc5, 0x94, 0x13, + 0x01, 0x1b, 0x87, 0xbe, 0xaf, 0x9e, 0x24, 0xc4, 0xd0, 0xf8, 0x39, 0xd4, 0xd5, 0x07, 0x2b, 0x6c, + 0x54, 0xbf, 0x0f, 0xeb, 0x3e, 0x0d, 0xc4, 0x35, 0xd2, 0xed, 0x6a, 0xd4, 0x2e, 0x9c, 0xd0, 0x80, + 0x3f, 0x65, 0x3c, 0xd3, 0xb5, 0x36, 0x51, 0x5e, 0x92, 0x8c, 0x07, 0xd0, 0xca, 0xc8, 0x88, 0x63, + 0xa1, 0x1f, 0x45, 0x91, 0x86, 0x93, 0x78, 0xe7, 0x52, 0xb2, 0xb3, 0xf1, 0x10, 0xf4, 0xf8, 0xdb, + 0x88, 0xaa, 0x36, 0xba, 0xba, 0xa6, 0xcc, 0x2d, 0xa7, 0xd8, 0x89, 0x7b, 0x9f, 0xb2, 0x40, 0xc5, + 0x84, 0x9c, 0x18, 0xcf, 0x53, 0x99, 0x41, 0x26, 0x72, 0xb2, 0x0b, 0x75, 0x95, 0x19, 0x54, 0x54, + 0x46, 0x3d, 0xf7, 0x09, 0xa6, 0x86, 0xa8, 0xe7, 0x96, 0x89, 0x22, 0x59, 0xb6, 0x94, 0x5e, 0x76, + 0x06, 0x8d, 0x28, 0xfa, 0xb3, 0x49, 0x54, 0xae, 0xd8, 0xcd, 0x27, 0x51, 0xb5, 0x68, 0x22, 0x28, + 0xbc, 0x23, 0x74, 0x26, 0x2e, 0xb3, 0xad, 0x24, 0x84, 0x70, 0x8f, 0x86, 0xd9, 0x91, 0x8c, 0x8f, + 0xa2, 0x78, 0x31, 0xde, 0x81, 0x9a, 0x3c, 0x9b, 0xb0, 0x8f, 0x58, 0x39, 0x2a, 0xf4, 0xc5, 0xb8, + 0x10, 0x49, 0xfe, 0xa0, 0x41, 0x23, 0x4a, 0x9e, 0x85, 0x4a, 0x99, 0x43, 0x97, 0xbe, 0xea, 0xa1, + 0xff, 0xf7, 0x89, 0x67, 0x17, 0x88, 0xcc, 0x2f, 0x17, 0x1e, 0x77, 0xdc, 0x89, 0x25, 0x6d, 0x2d, + 0x73, 0x50, 0x17, 0x39, 0xa7, 0xc8, 0x38, 0x11, 0xf4, 0xfd, 0xcf, 0xaa, 0xd0, 0x39, 0x1c, 0x3e, + 0x3a, 0x3e, 0xf4, 0xfd, 0x99, 0x33, 0xa6, 0xd8, 0x3c, 0xec, 0x41, 0x05, 0xfb, 0xa7, 0x82, 0xf7, + 0xdf, 0x7e, 0x51, 0x23, 0x4f, 0xf6, 0xa1, 0x8a, 0x6d, 0x14, 0x29, 0x7a, 0x06, 0xee, 0x17, 0xf6, + 0xf3, 0x62, 0x13, 0xd9, 0x68, 0x5d, 0x7f, 0x0d, 0xee, 0x17, 0x35, 0xf5, 0xe4, 0x07, 0xa0, 0x27, + 0xfd, 0xcd, 0xaa, 0x37, 0xe1, 0xfe, 0xca, 0xf6, 0x5e, 0xe8, 0x27, 0xb5, 0xe0, 0xaa, 0xa7, 0xcd, + 0xfe, 0xca, 0x3e, 0x98, 0x1c, 0x40, 0x3d, 0xaa, 0xa0, 0x8b, 0x5f, 0x6d, 0xfb, 0x2b, 0x5a, 0x6f, + 0x61, 0x1e, 0xd9, 0xb2, 0x14, 0x3d, 0x2d, 0xf7, 0x0b, 0xdf, 0x07, 0xc8, 0x7d, 0xa8, 0xa9, 0xb2, + 0xa6, 0xf0, 0xe5, 0xb6, 0x5f, 0xdc, 0x40, 0x8b, 0x4b, 0x26, 0x4d, 0xdb, 0xaa, 0xe7, 0xef, 0xfe, + 0xca, 0x87, 0x0c, 0x72, 0x08, 0x90, 0xea, 0x3c, 0x56, 0xbe, 0x6b, 0xf7, 0x57, 0x3f, 0x50, 0x90, + 0x87, 0xd0, 0x48, 0x1e, 0x9d, 0x8a, 0x5f, 0xaa, 0xfb, 0xab, 0xde, 0x0c, 0x86, 0xaf, 0xff, 0xeb, + 0xcf, 0x9b, 0xda, 0x6f, 0xae, 0x36, 0xb5, 0x2f, 0xae, 0x36, 0xb5, 0x2f, 0xaf, 0x36, 0xb5, 0xdf, + 0x5f, 0x6d, 0x6a, 0x7f, 0xba, 0xda, 0xd4, 0x7e, 0xfb, 0x97, 0x4d, 0x6d, 0x54, 0x43, 0xf7, 0x7f, + 0xef, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x85, 0xdc, 0x74, 0x8d, 0x99, 0x19, 0x00, 0x00, } diff --git a/abci/types/types.proto b/abci/types/types.proto index b48ff1e8b..86f5bbc58 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -4,9 +4,9 @@ package types; // For more information on gogo.proto, see: // https://github.com/gogo/protobuf/blob/master/extensions.md import "github.com/gogo/protobuf/gogoproto/gogo.proto"; -import "google/protobuf/timestamp.proto"; -import "github.com/tendermint/tendermint/libs/common/types.proto"; import "github.com/tendermint/tendermint/crypto/merkle/merkle.proto"; +import "github.com/tendermint/tendermint/libs/common/types.proto"; +import "google/protobuf/timestamp.proto"; // This file is copied from http://github.com/tendermint/abci // NOTE: When using custom types, mind the warnings. @@ -207,17 +207,19 @@ message ResponseCommit { // ConsensusParams contains all consensus-relevant parameters // that can be adjusted by the abci app message ConsensusParams { - BlockSizeParams block_size = 1; + BlockParams block = 1; EvidenceParams evidence = 2; ValidatorParams validator = 3; } -// BlockSize contains limits on the block size. -message BlockSizeParams { +// BlockParams contains limits on the block size and timestamp. +message BlockParams { // Note: must be greater than 0 int64 max_bytes = 1; // Note: must be greater or equal to -1 int64 max_gas = 2; + // Note: must be greater than 0 + int64 time_iota_ms = 3; } // EvidenceParams contains limits on the evidence. diff --git a/abci/types/typespb_test.go b/abci/types/typespb_test.go index 9375cc7f1..a4c0a3f82 100644 --- a/abci/types/typespb_test.go +++ b/abci/types/typespb_test.go @@ -1479,15 +1479,15 @@ func TestConsensusParamsMarshalTo(t *testing.T) { } } -func TestBlockSizeParamsProto(t *testing.T) { +func TestBlockParamsProto(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedBlockSizeParams(popr, false) + p := NewPopulatedBlockParams(popr, false) dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } - msg := &BlockSizeParams{} + msg := &BlockParams{} if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } @@ -1510,10 +1510,10 @@ func TestBlockSizeParamsProto(t *testing.T) { } } -func TestBlockSizeParamsMarshalTo(t *testing.T) { +func TestBlockParamsMarshalTo(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedBlockSizeParams(popr, false) + p := NewPopulatedBlockParams(popr, false) size := p.Size() dAtA := make([]byte, size) for i := range dAtA { @@ -1523,7 +1523,7 @@ func TestBlockSizeParamsMarshalTo(t *testing.T) { if err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } - msg := &BlockSizeParams{} + msg := &BlockParams{} if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } @@ -2675,16 +2675,16 @@ func TestConsensusParamsJSON(t *testing.T) { t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) } } -func TestBlockSizeParamsJSON(t *testing.T) { +func TestBlockParamsJSON(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedBlockSizeParams(popr, true) + p := NewPopulatedBlockParams(popr, true) marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{} jsondata, err := marshaler.MarshalToString(p) if err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } - msg := &BlockSizeParams{} + msg := &BlockParams{} err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg) if err != nil { t.Fatalf("seed = %d, err = %v", seed, err) @@ -3637,12 +3637,12 @@ func TestConsensusParamsProtoCompactText(t *testing.T) { } } -func TestBlockSizeParamsProtoText(t *testing.T) { +func TestBlockParamsProtoText(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedBlockSizeParams(popr, true) + p := NewPopulatedBlockParams(popr, true) dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p) - msg := &BlockSizeParams{} + msg := &BlockParams{} if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } @@ -3651,12 +3651,12 @@ func TestBlockSizeParamsProtoText(t *testing.T) { } } -func TestBlockSizeParamsProtoCompactText(t *testing.T) { +func TestBlockParamsProtoCompactText(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedBlockSizeParams(popr, true) + p := NewPopulatedBlockParams(popr, true) dAtA := github_com_gogo_protobuf_proto.CompactTextString(p) - msg := &BlockSizeParams{} + msg := &BlockParams{} if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { t.Fatalf("seed = %d, err = %v", seed, err) } @@ -4573,10 +4573,10 @@ func TestConsensusParamsSize(t *testing.T) { } } -func TestBlockSizeParamsSize(t *testing.T) { +func TestBlockParamsSize(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) - p := NewPopulatedBlockSizeParams(popr, true) + p := NewPopulatedBlockParams(popr, true) size2 := github_com_gogo_protobuf_proto.Size(p) dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) if err != nil { diff --git a/config/config.go b/config/config.go index c7f966bf0..cfd76060d 100644 --- a/config/config.go +++ b/config/config.go @@ -613,9 +613,6 @@ type ConsensusConfig struct { // Reactor sleep duration parameters PeerGossipSleepDuration time.Duration `mapstructure:"peer_gossip_sleep_duration"` PeerQueryMaj23SleepDuration time.Duration `mapstructure:"peer_query_maj23_sleep_duration"` - - // Block time parameters. Corresponds to the minimum time increment between consecutive blocks. - BlockTimeIota time.Duration `mapstructure:"blocktime_iota"` } // DefaultConsensusConfig returns a default configuration for the consensus service @@ -634,7 +631,6 @@ func DefaultConsensusConfig() *ConsensusConfig { CreateEmptyBlocksInterval: 0 * time.Second, PeerGossipSleepDuration: 100 * time.Millisecond, PeerQueryMaj23SleepDuration: 2000 * time.Millisecond, - BlockTimeIota: 1000 * time.Millisecond, } } @@ -651,16 +647,9 @@ func TestConsensusConfig() *ConsensusConfig { cfg.SkipTimeoutCommit = true cfg.PeerGossipSleepDuration = 5 * time.Millisecond cfg.PeerQueryMaj23SleepDuration = 250 * time.Millisecond - cfg.BlockTimeIota = 10 * time.Millisecond return cfg } -// MinValidVoteTime returns the minimum acceptable block time. -// See the [BFT time spec](https://godoc.org/github.com/tendermint/tendermint/docs/spec/consensus/bft-time.md). -func (cfg *ConsensusConfig) MinValidVoteTime(lastBlockTime time.Time) time.Time { - return lastBlockTime.Add(cfg.BlockTimeIota) -} - // WaitForTxs returns true if the consensus should wait for transactions before entering the propose step func (cfg *ConsensusConfig) WaitForTxs() bool { return !cfg.CreateEmptyBlocks || cfg.CreateEmptyBlocksInterval > 0 @@ -738,9 +727,6 @@ func (cfg *ConsensusConfig) ValidateBasic() error { if cfg.PeerQueryMaj23SleepDuration < 0 { return errors.New("peer_query_maj23_sleep_duration can't be negative") } - if cfg.BlockTimeIota < 0 { - return errors.New("blocktime_iota can't be negative") - } return nil } diff --git a/config/toml.go b/config/toml.go index d71bfb519..45b9a6719 100644 --- a/config/toml.go +++ b/config/toml.go @@ -272,9 +272,6 @@ create_empty_blocks_interval = "{{ .Consensus.CreateEmptyBlocksInterval }}" peer_gossip_sleep_duration = "{{ .Consensus.PeerGossipSleepDuration }}" peer_query_maj23_sleep_duration = "{{ .Consensus.PeerQueryMaj23SleepDuration }}" -# Block time parameters. Corresponds to the minimum time increment between consecutive blocks. -blocktime_iota = "{{ .Consensus.BlockTimeIota }}" - ##### transactions indexer configuration options ##### [tx_index] diff --git a/consensus/state.go b/consensus/state.go index 865cd553f..cf32afe7b 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -1459,7 +1459,7 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p _, err = cdc.UnmarshalBinaryLengthPrefixedReader( cs.ProposalBlockParts.GetReader(), &cs.ProposalBlock, - int64(cs.state.ConsensusParams.BlockSize.MaxBytes), + int64(cs.state.ConsensusParams.Block.MaxBytes), ) if err != nil { return added, err @@ -1701,10 +1701,12 @@ func (cs *ConsensusState) voteTime() time.Time { minVoteTime := now // TODO: We should remove next line in case we don't vote for v in case cs.ProposalBlock == nil, // even if cs.LockedBlock != nil. See https://github.com/tendermint/spec. + timeIotaMs := time.Duration(cs.state.ConsensusParams.Block.TimeIotaMs) * time.Millisecond if cs.LockedBlock != nil { - minVoteTime = cs.config.MinValidVoteTime(cs.LockedBlock.Time) + // See the BFT time spec https://tendermint.com/docs/spec/consensus/bft-time.html + minVoteTime = cs.LockedBlock.Time.Add(timeIotaMs) } else if cs.ProposalBlock != nil { - minVoteTime = cs.config.MinValidVoteTime(cs.ProposalBlock.Time) + minVoteTime = cs.ProposalBlock.Time.Add(timeIotaMs) } if now.After(minVoteTime) { diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index b9dc744de..6f624a562 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -443,12 +443,12 @@ Commit are included in the header of the next block. ### ConsensusParams - **Fields**: - - `BlockSize (BlockSizeParams)`: Parameters limiting the size of a block. + - `Block (BlockParams)`: Parameters limiting the size of a block and time between consecutive blocks. - `Evidence (EvidenceParams)`: Parameters limiting the validity of evidence of byzantine behaviour. - `Validator (ValidatorParams)`: Parameters limitng the types of pubkeys validators can use. -### BlockSizeParams +### BlockParams - **Fields**: - `MaxBytes (int64)`: Max size of a block, in bytes. @@ -456,6 +456,8 @@ Commit are included in the header of the next block. - NOTE: blocks that violate this may be committed if there are Byzantine proposers. It's the application's responsibility to handle this when processing a block! + - `TimeIotaMs (int64)`: Minimum time increment between consecutive blocks (in milliseconds). + ### EvidenceParams diff --git a/docs/spec/abci/apps.md b/docs/spec/abci/apps.md index caffaaea3..ca6abe7f8 100644 --- a/docs/spec/abci/apps.md +++ b/docs/spec/abci/apps.md @@ -103,7 +103,7 @@ the difference credited back. Tendermint adopts a similar abstraction, though uses it only optionally and weakly, allowing applications to define their own sense of the cost of execution. -In Tendermint, the `ConsensusParams.BlockSize.MaxGas` limits the amount of `gas` that can be used in a block. +In Tendermint, the `ConsensusParams.Block.MaxGas` limits the amount of `gas` that can be used in a block. The default value is `-1`, meaning no limit, or that the concept of gas is meaningless. @@ -225,7 +225,7 @@ ConsensusParams enforce certain limits in the blockchain, like the maximum size of blocks, amount of gas used in a block, and the maximum acceptable age of evidence. They can be set in InitChain and updated in EndBlock. -### BlockSize.MaxBytes +### Block.MaxBytes The maximum size of a complete Amino encoded block. This is enforced by Tendermint consensus. @@ -235,7 +235,7 @@ the header, the validator set, and any included evidence in the block. Must have `0 < MaxBytes < 100 MB`. -### BlockSize.MaxGas +### Block.MaxGas The maximum of the sum of `GasWanted` in a proposed block. This is *not* enforced by Tendermint consensus. @@ -246,6 +246,13 @@ txs included in a proposed block. Must have `MaxGas >= -1`. If `MaxGas == -1`, no limit is enforced. +### Block.TimeIotaMs + +The minimum time between consecutive blocks (in milliseconds). +This is enforced by Tendermint consensus. + +Must have `TimeIotaMs > 0` to ensure time monotonicity. + ### EvidenceParams.MaxAge This is the maximum age of evidence. @@ -260,8 +267,8 @@ Must have `0 < MaxAge`. The application may set the ConsensusParams during InitChain, and update them during EndBlock. If the ConsensusParams is empty, it will be ignored. Each field that is not empty will be applied in full. For instance, if updating the -BlockSize.MaxBytes, applications must also set the other BlockSize fields (like -BlockSize.MaxGas), even if they are unchanged, as they will otherwise cause the +Block.MaxBytes, applications must also set the other Block fields (like +Block.MaxGas), even if they are unchanged, as they will otherwise cause the value to be updated to 0. #### InitChain diff --git a/docs/spec/blockchain/state.md b/docs/spec/blockchain/state.md index 7df096bc9..3ab65e12b 100644 --- a/docs/spec/blockchain/state.md +++ b/docs/spec/blockchain/state.md @@ -83,7 +83,7 @@ evolve without breaking the header. ```go type ConsensusParams struct { - BlockSize + Block Evidence Validator } @@ -95,38 +95,38 @@ type hashedParams struct { func (params ConsensusParams) Hash() []byte { SHA256(hashedParams{ - BlockMaxBytes: params.BlockSize.MaxBytes, - BlockMaxGas: params.BlockSize.MaxGas, + BlockMaxBytes: params.Block.MaxBytes, + BlockMaxGas: params.Block.MaxGas, }) } -type BlockSize struct { +type BlockParams struct { MaxBytes int64 MaxGas int64 + TimeIotaMs int64 } -type Evidence struct { +type EvidenceParams struct { MaxAge int64 } -type Validator struct { - PubKeyTypes []string -} - type ValidatorParams struct { PubKeyTypes []string } ``` -#### BlockSize +#### Block -The total size of a block is limited in bytes by the `ConsensusParams.BlockSize.MaxBytes`. +The total size of a block is limited in bytes by the `ConsensusParams.Block.MaxBytes`. Proposed blocks must be less than this size, and will be considered invalid otherwise. Blocks should additionally be limited by the amount of "gas" consumed by the transactions in the block, though this is not yet implemented. +The minimal time between consecutive blocks is controlled by the +`ConsensusParams.Block.TimeIotaMs`. + #### Evidence For evidence in a block to be valid, it must satisfy: diff --git a/docs/spec/consensus/creating-proposal.md b/docs/spec/consensus/creating-proposal.md index 03f5866dc..831d16571 100644 --- a/docs/spec/consensus/creating-proposal.md +++ b/docs/spec/consensus/creating-proposal.md @@ -4,7 +4,7 @@ A block consists of a header, transactions, votes (the commit), and a list of evidence of malfeasance (ie. signing conflicting votes). We include no more than 1/10th of the maximum block size -(`ConsensusParams.BlockSize.MaxBytes`) of evidence with each block. +(`ConsensusParams.Block.MaxBytes`) of evidence with each block. ## Reaping transactions from the mempool diff --git a/node/node_test.go b/node/node_test.go index ebc3f2102..a2725d845 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -219,7 +219,7 @@ func TestCreateProposalBlock(t *testing.T) { var height int64 = 1 state, stateDB := state(1, height) maxBytes := 16384 - state.ConsensusParams.BlockSize.MaxBytes = int64(maxBytes) + state.ConsensusParams.Block.MaxBytes = int64(maxBytes) proposerAddr, _ := state.Validators.GetByIndex(0) // Make Mempool diff --git a/state/execution.go b/state/execution.go index c4b94fb9f..3a11ecca4 100644 --- a/state/execution.go +++ b/state/execution.go @@ -82,8 +82,8 @@ func (blockExec *BlockExecutor) CreateProposalBlock( proposerAddr []byte, ) (*types.Block, *types.PartSet) { - maxBytes := state.ConsensusParams.BlockSize.MaxBytes - maxGas := state.ConsensusParams.BlockSize.MaxGas + maxBytes := state.ConsensusParams.Block.MaxBytes + maxGas := state.ConsensusParams.Block.MaxGas // Fetch a limited amount of valid evidence maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBytes) diff --git a/state/state_test.go b/state/state_test.go index bdec63a68..4566d93ea 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -908,7 +908,7 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { params[0] = state.ConsensusParams for i := 1; i < N+1; i++ { params[i] = *types.DefaultConsensusParams() - params[i].BlockSize.MaxBytes += int64(i) + params[i].Block.MaxBytes += int64(i) } // Build the params history by running updateState @@ -956,11 +956,16 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { } } -func makeParams(blockBytes, blockGas, evidenceAge int64) types.ConsensusParams { +func makeParams( + blockBytes, blockGas int64, + blockTimeIotaMs int64, + evidenceAge int64, +) types.ConsensusParams { return types.ConsensusParams{ - BlockSize: types.BlockSizeParams{ - MaxBytes: blockBytes, - MaxGas: blockGas, + Block: types.BlockParams{ + MaxBytes: blockBytes, + MaxGas: blockGas, + TimeIotaMs: blockTimeIotaMs, }, Evidence: types.EvidenceParams{ MaxAge: evidenceAge, @@ -969,7 +974,7 @@ func makeParams(blockBytes, blockGas, evidenceAge int64) types.ConsensusParams { } func TestApplyUpdates(t *testing.T) { - initParams := makeParams(1, 2, 3) + initParams := makeParams(1, 2, 3, 4) cases := [...]struct { init types.ConsensusParams @@ -980,19 +985,20 @@ func TestApplyUpdates(t *testing.T) { 1: {initParams, abci.ConsensusParams{}, initParams}, 2: {initParams, abci.ConsensusParams{ - BlockSize: &abci.BlockSizeParams{ - MaxBytes: 44, - MaxGas: 55, + Block: &abci.BlockParams{ + MaxBytes: 44, + MaxGas: 55, + TimeIotaMs: 66, }, }, - makeParams(44, 55, 3)}, + makeParams(44, 55, 66, 4)}, 3: {initParams, abci.ConsensusParams{ Evidence: &abci.EvidenceParams{ MaxAge: 66, }, }, - makeParams(1, 2, 66)}, + makeParams(1, 2, 3, 66)}, } for i, tc := range cases { diff --git a/state/tx_filter.go b/state/tx_filter.go index 518eb1877..a8c0627dc 100644 --- a/state/tx_filter.go +++ b/state/tx_filter.go @@ -9,7 +9,7 @@ import ( // The function limits the size of a transaction to the block's maximum data size. func TxPreCheck(state State) mempl.PreCheckFunc { maxDataBytes := types.MaxDataBytesUnknownEvidence( - state.ConsensusParams.BlockSize.MaxBytes, + state.ConsensusParams.Block.MaxBytes, state.Validators.Size(), ) return mempl.PreCheckAminoMaxBytes(maxDataBytes) @@ -18,5 +18,5 @@ func TxPreCheck(state State) mempl.PreCheckFunc { // TxPostCheck returns a function to filter transactions after processing. // The function limits the gas wanted by a transaction to the block's maximum total gas. func TxPostCheck(state State) mempl.PostCheckFunc { - return mempl.PostCheckMaxGas(state.ConsensusParams.BlockSize.MaxGas) + return mempl.PostCheckMaxGas(state.ConsensusParams.Block.MaxGas) } diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index 52ae396bf..ffb41c178 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -16,7 +16,7 @@ import ( func TestTxFilter(t *testing.T) { genDoc := randomGenesisDoc() - genDoc.ConsensusParams.BlockSize.MaxBytes = 3000 + genDoc.ConsensusParams.Block.MaxBytes = 3000 // Max size of Txs is much smaller than size of block, // since we need to account for commits and evidence. diff --git a/state/validation.go b/state/validation.go index 3cb0ee8fb..3c63c35b7 100644 --- a/state/validation.go +++ b/state/validation.go @@ -133,7 +133,7 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block } // Limit the amount of evidence - maxNumEvidence, _ := types.MaxEvidencePerBlock(state.ConsensusParams.BlockSize.MaxBytes) + maxNumEvidence, _ := types.MaxEvidencePerBlock(state.ConsensusParams.Block.MaxBytes) numEvidence := int64(len(block.Evidence.Evidence)) if numEvidence > maxNumEvidence { return types.NewErrEvidenceOverflow(maxNumEvidence, numEvidence) diff --git a/state/validation_test.go b/state/validation_test.go index a873855a9..705f843df 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -108,7 +108,7 @@ func TestValidateBlockEvidence(t *testing.T) { require.NoError(t, err) // A block with too much evidence fails. - maxBlockSize := state.ConsensusParams.BlockSize.MaxBytes + maxBlockSize := state.ConsensusParams.Block.MaxBytes maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize) require.True(t, maxNumEvidence > 2) for i := int64(0); i < maxNumEvidence; i++ { diff --git a/tools/tm-signer-harness/internal/test_harness_test.go b/tools/tm-signer-harness/internal/test_harness_test.go index adb818b0b..c249bd2b6 100644 --- a/tools/tm-signer-harness/internal/test_harness_test.go +++ b/tools/tm-signer-harness/internal/test_harness_test.go @@ -41,17 +41,18 @@ const ( "genesis_time": "2019-01-15T11:56:34.8963Z", "chain_id": "test-chain-0XwP5E", "consensus_params": { - "block_size": { - "max_bytes": "22020096", - "max_gas": "-1" + "block": { + "max_bytes": "22020096", + "max_gas": "-1", + "time_iota_ms": "1000" }, "evidence": { - "max_age": "100000" + "max_age": "100000" }, "validator": { - "pub_key_types": [ - "ed25519" - ] + "pub_key_types": [ + "ed25519" + ] } }, "validators": [ diff --git a/types/block.go b/types/block.go index bcaf0725c..6616c0ee6 100644 --- a/types/block.go +++ b/types/block.go @@ -312,7 +312,7 @@ func MaxDataBytes(maxBytes int64, valsCount, evidenceCount int) int64 { if maxDataBytes < 0 { panic(fmt.Sprintf( - "Negative MaxDataBytes. BlockSize.MaxBytes=%d is too small to accommodate header&lastCommit&evidence=%d", + "Negative MaxDataBytes. Block.MaxBytes=%d is too small to accommodate header&lastCommit&evidence=%d", maxBytes, -(maxDataBytes - maxBytes), )) @@ -337,7 +337,7 @@ func MaxDataBytesUnknownEvidence(maxBytes int64, valsCount int) int64 { if maxDataBytes < 0 { panic(fmt.Sprintf( - "Negative MaxDataBytesUnknownEvidence. BlockSize.MaxBytes=%d is too small to accommodate header&lastCommit&evidence=%d", + "Negative MaxDataBytesUnknownEvidence. Block.MaxBytes=%d is too small to accommodate header&lastCommit&evidence=%d", maxBytes, -(maxDataBytes - maxBytes), )) diff --git a/types/genesis_test.go b/types/genesis_test.go index 0e81187e3..f977513e7 100644 --- a/types/genesis_test.go +++ b/types/genesis_test.go @@ -65,7 +65,7 @@ func TestGenesisGood(t *testing.T) { assert.NoError(t, err, "expected no error for valid genDoc json") // test with invalid consensus params - genDoc.ConsensusParams.BlockSize.MaxBytes = 0 + genDoc.ConsensusParams.Block.MaxBytes = 0 genDocBytes, err = cdc.MarshalJSON(genDoc) assert.NoError(t, err, "error marshalling genDoc") genDoc, err = GenesisDocFromJSON(genDocBytes) diff --git a/types/params.go b/types/params.go index 03e43c191..ce9a6bc68 100644 --- a/types/params.go +++ b/types/params.go @@ -17,7 +17,7 @@ const ( // ConsensusParams contains consensus critical parameters that determine the // validity of blocks. type ConsensusParams struct { - BlockSize BlockSizeParams `json:"block_size"` + Block BlockParams `json:"block"` Evidence EvidenceParams `json:"evidence"` Validator ValidatorParams `json:"validator"` } @@ -30,13 +30,16 @@ type HashedParams struct { BlockMaxGas int64 } -// BlockSizeParams define limits on the block size. -type BlockSizeParams struct { +// BlockParams define limits on the block size and gas plus minimum time +// between blocks. +type BlockParams struct { MaxBytes int64 `json:"max_bytes"` MaxGas int64 `json:"max_gas"` + // Minimum time increment between consecutive blocks (in milliseconds) + TimeIotaMs int64 `json:"time_iota_ms"` } -// EvidenceParams determine how we handle evidence of malfeasance +// EvidenceParams determine how we handle evidence of malfeasance. type EvidenceParams struct { MaxAge int64 `json:"max_age"` // only accept new evidence more recent than this } @@ -50,17 +53,18 @@ type ValidatorParams struct { // DefaultConsensusParams returns a default ConsensusParams. func DefaultConsensusParams() *ConsensusParams { return &ConsensusParams{ - DefaultBlockSizeParams(), + DefaultBlockParams(), DefaultEvidenceParams(), DefaultValidatorParams(), } } -// DefaultBlockSizeParams returns a default BlockSizeParams. -func DefaultBlockSizeParams() BlockSizeParams { - return BlockSizeParams{ - MaxBytes: 22020096, // 21MB - MaxGas: -1, +// DefaultBlockParams returns a default BlockParams. +func DefaultBlockParams() BlockParams { + return BlockParams{ + MaxBytes: 22020096, // 21MB + MaxGas: -1, + TimeIotaMs: 1000, // 1s } } @@ -89,18 +93,23 @@ func (params *ValidatorParams) IsValidPubkeyType(pubkeyType string) bool { // Validate validates the ConsensusParams to ensure all values are within their // allowed limits, and returns an error if they are not. func (params *ConsensusParams) Validate() error { - if params.BlockSize.MaxBytes <= 0 { - return cmn.NewError("BlockSize.MaxBytes must be greater than 0. Got %d", - params.BlockSize.MaxBytes) + if params.Block.MaxBytes <= 0 { + return cmn.NewError("Block.MaxBytes must be greater than 0. Got %d", + params.Block.MaxBytes) } - if params.BlockSize.MaxBytes > MaxBlockSizeBytes { - return cmn.NewError("BlockSize.MaxBytes is too big. %d > %d", - params.BlockSize.MaxBytes, MaxBlockSizeBytes) + if params.Block.MaxBytes > MaxBlockSizeBytes { + return cmn.NewError("Block.MaxBytes is too big. %d > %d", + params.Block.MaxBytes, MaxBlockSizeBytes) } - if params.BlockSize.MaxGas < -1 { - return cmn.NewError("BlockSize.MaxGas must be greater or equal to -1. Got %d", - params.BlockSize.MaxGas) + if params.Block.MaxGas < -1 { + return cmn.NewError("Block.MaxGas must be greater or equal to -1. Got %d", + params.Block.MaxGas) + } + + if params.Block.TimeIotaMs <= 0 { + return cmn.NewError("Block.TimeIotaMs must be greater than 0. Got %v", + params.Block.TimeIotaMs) } if params.Evidence.MaxAge <= 0 { @@ -131,8 +140,8 @@ func (params *ConsensusParams) Validate() error { func (params *ConsensusParams) Hash() []byte { hasher := tmhash.New() bz := cdcEncode(HashedParams{ - params.BlockSize.MaxBytes, - params.BlockSize.MaxGas, + params.Block.MaxBytes, + params.Block.MaxGas, }) if bz == nil { panic("cannot fail to encode ConsensusParams") @@ -142,7 +151,7 @@ func (params *ConsensusParams) Hash() []byte { } func (params *ConsensusParams) Equals(params2 *ConsensusParams) bool { - return params.BlockSize == params2.BlockSize && + return params.Block == params2.Block && params.Evidence == params2.Evidence && cmn.StringSliceEqual(params.Validator.PubKeyTypes, params2.Validator.PubKeyTypes) } @@ -157,9 +166,10 @@ func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusPar } // we must defensively consider any structs may be nil - if params2.BlockSize != nil { - res.BlockSize.MaxBytes = params2.BlockSize.MaxBytes - res.BlockSize.MaxGas = params2.BlockSize.MaxGas + if params2.Block != nil { + res.Block.MaxBytes = params2.Block.MaxBytes + res.Block.MaxGas = params2.Block.MaxGas + res.Block.TimeIotaMs = params2.Block.TimeIotaMs } if params2.Evidence != nil { res.Evidence.MaxAge = params2.Evidence.MaxAge diff --git a/types/params_test.go b/types/params_test.go index dc1936fbf..ade7c89f5 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -19,22 +19,23 @@ func TestConsensusParamsValidation(t *testing.T) { params ConsensusParams valid bool }{ - // test block size - 0: {makeParams(1, 0, 1, valEd25519), true}, - 1: {makeParams(0, 0, 1, valEd25519), false}, - 2: {makeParams(47*1024*1024, 0, 1, valEd25519), true}, - 3: {makeParams(10, 0, 1, valEd25519), true}, - 4: {makeParams(100*1024*1024, 0, 1, valEd25519), true}, - 5: {makeParams(101*1024*1024, 0, 1, valEd25519), false}, - 6: {makeParams(1024*1024*1024, 0, 1, valEd25519), false}, - 7: {makeParams(1024*1024*1024, 0, -1, valEd25519), false}, - // test evidence age - 8: {makeParams(1, 0, 0, valEd25519), false}, - 9: {makeParams(1, 0, -1, valEd25519), false}, + // test block params + 0: {makeParams(1, 0, 10, 1, valEd25519), true}, + 1: {makeParams(0, 0, 10, 1, valEd25519), false}, + 2: {makeParams(47*1024*1024, 0, 10, 1, valEd25519), true}, + 3: {makeParams(10, 0, 10, 1, valEd25519), true}, + 4: {makeParams(100*1024*1024, 0, 10, 1, valEd25519), true}, + 5: {makeParams(101*1024*1024, 0, 10, 1, valEd25519), false}, + 6: {makeParams(1024*1024*1024, 0, 10, 1, valEd25519), false}, + 7: {makeParams(1024*1024*1024, 0, 10, -1, valEd25519), false}, + 8: {makeParams(1, 0, -10, 1, valEd25519), false}, + // test evidence params + 9: {makeParams(1, 0, 10, 0, valEd25519), false}, + 10: {makeParams(1, 0, 10, -1, valEd25519), false}, // test no pubkey type provided - 10: {makeParams(1, 0, 1, []string{}), false}, + 11: {makeParams(1, 0, 10, 1, []string{}), false}, // test invalid pubkey type provided - 11: {makeParams(1, 0, 1, []string{"potatoes make good pubkeys"}), false}, + 12: {makeParams(1, 0, 10, 1, []string{"potatoes make good pubkeys"}), false}, } for i, tc := range testCases { if tc.valid { @@ -45,11 +46,17 @@ func TestConsensusParamsValidation(t *testing.T) { } } -func makeParams(blockBytes, blockGas, evidenceAge int64, pubkeyTypes []string) ConsensusParams { +func makeParams( + blockBytes, blockGas int64, + blockTimeIotaMs int64, + evidenceAge int64, + pubkeyTypes []string, +) ConsensusParams { return ConsensusParams{ - BlockSize: BlockSizeParams{ - MaxBytes: blockBytes, - MaxGas: blockGas, + Block: BlockParams{ + MaxBytes: blockBytes, + MaxGas: blockGas, + TimeIotaMs: blockTimeIotaMs, }, Evidence: EvidenceParams{ MaxAge: evidenceAge, @@ -62,14 +69,14 @@ func makeParams(blockBytes, blockGas, evidenceAge int64, pubkeyTypes []string) C func TestConsensusParamsHash(t *testing.T) { params := []ConsensusParams{ - makeParams(4, 2, 3, valEd25519), - makeParams(1, 4, 3, valEd25519), - makeParams(1, 2, 4, valEd25519), - makeParams(2, 5, 7, valEd25519), - makeParams(1, 7, 6, valEd25519), - makeParams(9, 5, 4, valEd25519), - makeParams(7, 8, 9, valEd25519), - makeParams(4, 6, 5, valEd25519), + makeParams(4, 2, 10, 3, valEd25519), + makeParams(1, 4, 10, 3, valEd25519), + makeParams(1, 2, 10, 4, valEd25519), + makeParams(2, 5, 10, 7, valEd25519), + makeParams(1, 7, 10, 6, valEd25519), + makeParams(9, 5, 10, 4, valEd25519), + makeParams(7, 8, 10, 9, valEd25519), + makeParams(4, 6, 10, 5, valEd25519), } hashes := make([][]byte, len(params)) @@ -95,26 +102,27 @@ func TestConsensusParamsUpdate(t *testing.T) { }{ // empty updates { - makeParams(1, 2, 3, valEd25519), + makeParams(1, 2, 10, 3, valEd25519), &abci.ConsensusParams{}, - makeParams(1, 2, 3, valEd25519), + makeParams(1, 2, 10, 3, valEd25519), }, // fine updates { - makeParams(1, 2, 3, valEd25519), + makeParams(1, 2, 10, 3, valEd25519), &abci.ConsensusParams{ - BlockSize: &abci.BlockSizeParams{ - MaxBytes: 100, - MaxGas: 200, + Block: &abci.BlockParams{ + MaxBytes: 100, + MaxGas: 200, + TimeIotaMs: 300, }, Evidence: &abci.EvidenceParams{ - MaxAge: 300, + MaxAge: 400, }, Validator: &abci.ValidatorParams{ PubKeyTypes: valSecp256k1, }, }, - makeParams(100, 200, 300, valSecp256k1), + makeParams(100, 200, 300, 400, valSecp256k1), }, } for _, tc := range testCases { diff --git a/types/protobuf.go b/types/protobuf.go index eed73b568..81b13874f 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -125,9 +125,10 @@ func (tm2pb) ValidatorUpdates(vals *ValidatorSet) []abci.ValidatorUpdate { func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams { return &abci.ConsensusParams{ - BlockSize: &abci.BlockSizeParams{ - MaxBytes: params.BlockSize.MaxBytes, - MaxGas: params.BlockSize.MaxGas, + Block: &abci.BlockParams{ + MaxBytes: params.Block.MaxBytes, + MaxGas: params.Block.MaxGas, + TimeIotaMs: params.Block.TimeIotaMs, }, Evidence: &abci.EvidenceParams{ MaxAge: params.Evidence.MaxAge, @@ -222,16 +223,28 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) } func (pb2tm) ConsensusParams(csp *abci.ConsensusParams) ConsensusParams { - return ConsensusParams{ - BlockSize: BlockSizeParams{ - MaxBytes: csp.BlockSize.MaxBytes, - MaxGas: csp.BlockSize.MaxGas, - }, - Evidence: EvidenceParams{ - MaxAge: csp.Evidence.MaxAge, - }, - Validator: ValidatorParams{ - PubKeyTypes: csp.Validator.PubKeyTypes, - }, + params := ConsensusParams{} + + // we must defensively consider any structs may be nil + if csp.Block != nil { + params.Block = BlockParams{ + MaxBytes: csp.Block.MaxBytes, + MaxGas: csp.Block.MaxGas, + TimeIotaMs: csp.Block.TimeIotaMs, + } } + + if csp.Evidence != nil { + params.Evidence = EvidenceParams{ + MaxAge: csp.Evidence.MaxAge, + } + } + + if csp.Validator != nil { + params.Validator = ValidatorParams{ + PubKeyTypes: csp.Validator.PubKeyTypes, + } + } + + return params } From 8c9df30e28a3b95efe537afe9930de2150999441 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Mon, 4 Mar 2019 22:56:46 -0800 Subject: [PATCH 082/259] libs/db: Add cleveldb.Stats() (#3379) Fixes: #3378 * Add stats to cleveldb implementation * update changelog * remote TODO also - sort keys - preallocate memory * fix const initializer []string literal is not a constant * add test --- CHANGELOG_PENDING.md | 9 +++++++++ libs/db/c_level_db.go | 14 +++++++++++--- libs/db/c_level_db_test.go | 9 +++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fe9a87d8a..cbb4077c7 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -30,6 +30,15 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: - [libs/common] \#3238 exit with zero (0) code upon receiving SIGTERM/SIGINT +- [libs/db] \#3378 CLevelDB#Stats now returns the following properties: + - leveldb.num-files-at-level{n} + - leveldb.stats + - leveldb.sstables + - leveldb.blockpool + - leveldb.cachedblock + - leveldb.openedtables + - leveldb.alivesnaps + - leveldb.aliveiters ### BUG FIXES: diff --git a/libs/db/c_level_db.go b/libs/db/c_level_db.go index 7f74b2a71..e411dfdd0 100644 --- a/libs/db/c_level_db.go +++ b/libs/db/c_level_db.go @@ -128,10 +128,18 @@ func (db *CLevelDB) Print() { // Implements DB. func (db *CLevelDB) Stats() map[string]string { - // TODO: Find the available properties for the C LevelDB implementation - keys := []string{} + keys := []string{ + "leveldb.aliveiters", + "leveldb.alivesnaps", + "leveldb.blockpool", + "leveldb.cachedblock", + "leveldb.num-files-at-level{n}", + "leveldb.openedtables", + "leveldb.sstables", + "leveldb.stats", + } - stats := make(map[string]string) + stats := make(map[string]string, len(keys)) for _, key := range keys { str := db.db.PropertyValue(key) stats[key] = str diff --git a/libs/db/c_level_db_test.go b/libs/db/c_level_db_test.go index eab3dfc41..e71dee0c1 100644 --- a/libs/db/c_level_db_test.go +++ b/libs/db/c_level_db_test.go @@ -99,3 +99,12 @@ func TestCLevelDBBackend(t *testing.T) { _, ok := db.(*CLevelDB) assert.True(t, ok) } + +func TestCLevelDBStats(t *testing.T) { + name := fmt.Sprintf("test_%x", cmn.RandStr(12)) + dir := os.TempDir() + db := NewDB(name, LevelDBBackend, dir) + defer cleanupDBDir(dir, name) + + assert.NotEmpty(t, db.Stats()) +} From 1eaa42cd25f2355f4b30440bddae435d056d1f44 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 5 Mar 2019 08:08:52 +0100 Subject: [PATCH 083/259] golang 1.12.0 (#3376) - update docker image on circleci - remove GOCACHE=off from Makefile (see: https://tip.golang.org/doc/go1.12#gocache) - update badge in readme - update in scripts/install - update Vagrantfile - update in networks/remote/integration.sh - tools/build/Makefile --- .circleci/config.yml | 4 ++-- Makefile | 6 +++--- README.md | 2 +- Vagrantfile | 6 +++--- networks/remote/integration.sh | 4 ++-- scripts/install/install_tendermint_arm.sh | 2 +- scripts/install/install_tendermint_bsd.sh | 2 +- scripts/install/install_tendermint_ubuntu.sh | 2 +- tools/build/Makefile | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d6440de1e..025fc48e8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &defaults working_directory: /go/src/github.com/tendermint/tendermint docker: - - image: circleci/golang:1.11.4 + - image: circleci/golang:1.12.0 environment: GOBIN: /tmp/workspace/bin @@ -154,7 +154,7 @@ jobs: for pkg in $(go list github.com/tendermint/tendermint/... | circleci tests split --split-by=timings); do id=$(basename "$pkg") - GOCACHE=off go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" + go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" done - persist_to_workspace: root: /tmp/workspace diff --git a/Makefile b/Makefile index a1ba06aa9..08373644c 100644 --- a/Makefile +++ b/Makefile @@ -131,7 +131,7 @@ clean_certs: rm -f db/remotedb/::.crt db/remotedb/::.key test_libs: gen_certs - GOCACHE=off go test -tags gcc $(PACKAGES) + go test -tags gcc $(PACKAGES) make clean_certs grpc_dbserver: @@ -214,11 +214,11 @@ vagrant_test: ### go tests test: @echo "--> Running go test" - @GOCACHE=off go test -p 1 $(PACKAGES) + go test -p 1 $(PACKAGES) test_race: @echo "--> Running go test --race" - @GOCACHE=off go test -p 1 -v -race $(PACKAGES) + go test -p 1 -v -race $(PACKAGES) # uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks test_with_deadlock: diff --git a/README.md b/README.md index 9251e3ca5..ad4fc1308 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)), for short. [![API Reference]( https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667 )](https://godoc.org/github.com/tendermint/tendermint) -[![Go version](https://img.shields.io/badge/go-1.10.4-blue.svg)](https://github.com/moovweb/gvm) +[![Go version](https://img.shields.io/badge/go-1.12.0-blue.svg)](https://github.com/moovweb/gvm) [![riot.im](https://img.shields.io/badge/riot.im-JOIN%20CHAT-green.svg)](https://riot.im/app/#/room/#tendermint:matrix.org) [![license](https://img.shields.io/github/license/tendermint/tendermint.svg)](https://github.com/tendermint/tendermint/blob/master/LICENSE) [![](https://tokei.rs/b1/github/tendermint/tendermint?category=lines)](https://github.com/tendermint/tendermint) diff --git a/Vagrantfile b/Vagrantfile index 515a48791..da4f8ac3d 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -29,10 +29,10 @@ Vagrant.configure("2") do |config| usermod -a -G docker vagrant # install go - wget -q https://dl.google.com/go/go1.11.linux-amd64.tar.gz - tar -xvf go1.11.linux-amd64.tar.gz + wget -q https://dl.google.com/go/go1.12.linux-amd64.tar.gz + tar -xvf go1.12.linux-amd64.tar.gz mv go /usr/local - rm -f go1.11.linux-amd64.tar.gz + rm -f go1.12.linux-amd64.tar.gz # install nodejs (for docs) curl -sL https://deb.nodesource.com/setup_11.x | bash - diff --git a/networks/remote/integration.sh b/networks/remote/integration.sh index 1624711f9..8150aad48 100644 --- a/networks/remote/integration.sh +++ b/networks/remote/integration.sh @@ -10,8 +10,8 @@ sudo apt-get upgrade -y sudo apt-get install -y jq unzip python-pip software-properties-common make # get and unpack golang -curl -O https://storage.googleapis.com/golang/go1.10.linux-amd64.tar.gz -tar -xvf go1.10.linux-amd64.tar.gz +curl -O https://storage.googleapis.com/golang/go1.12.linux-amd64.tar.gz +tar -xvf go1.12.linux-amd64.tar.gz ## move binary and add to path mv go /usr/local diff --git a/scripts/install/install_tendermint_arm.sh b/scripts/install/install_tendermint_arm.sh index 06a20f14d..703a736f4 100644 --- a/scripts/install/install_tendermint_arm.sh +++ b/scripts/install/install_tendermint_arm.sh @@ -5,7 +5,7 @@ REPO=github.com/tendermint/tendermint # change this to a specific release or branch BRANCH=master -GO_VERSION=1.11.4 +GO_VERSION=1.12.0 sudo apt-get update -y diff --git a/scripts/install/install_tendermint_bsd.sh b/scripts/install/install_tendermint_bsd.sh index c3834058e..ebada72ec 100644 --- a/scripts/install/install_tendermint_bsd.sh +++ b/scripts/install/install_tendermint_bsd.sh @@ -16,7 +16,7 @@ set BRANCH=master set REPO=github.com/tendermint/tendermint -set GO_VERSION=1.11.4 +set GO_VERSION=1.12.0 sudo pkg update diff --git a/scripts/install/install_tendermint_ubuntu.sh b/scripts/install/install_tendermint_ubuntu.sh index 59ab7a0a5..20e611294 100644 --- a/scripts/install/install_tendermint_ubuntu.sh +++ b/scripts/install/install_tendermint_ubuntu.sh @@ -13,7 +13,7 @@ REPO=github.com/tendermint/tendermint # change this to a specific release or branch BRANCH=master -GO_VERSION=1.11.4 +GO_VERSION=1.12.0 sudo apt-get update -y sudo apt-get install -y make diff --git a/tools/build/Makefile b/tools/build/Makefile index a47644b63..f9384ac64 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -4,7 +4,7 @@ requirements_check = true gpg_check = false -go_min_version = 1.9.4 +go_min_version = 1.12.0 gpg_key = 2122CBE9 ifeq ($(requirements_check),true) From 858875fbb8b94debc4973fdf5923ced6564d3c39 Mon Sep 17 00:00:00 2001 From: Silas Davis Date: Wed, 6 Mar 2019 08:22:35 +0000 Subject: [PATCH 084/259] Copy secp256k1 code from go-ethereum to avoid GPL vendoring issues in (#3371) downstream Signed-off-by: Silas Davis --- Gopkg.lock | 9 - Gopkg.toml | 12 - .../secp256k1/internal/secp256k1/.gitignore | 24 + crypto/secp256k1/internal/secp256k1/LICENSE | 31 + crypto/secp256k1/internal/secp256k1/README.md | 3 + crypto/secp256k1/internal/secp256k1/curve.go | 325 ++ crypto/secp256k1/internal/secp256k1/ext.h | 130 + .../secp256k1/libsecp256k1/.gitignore | 49 + .../secp256k1/libsecp256k1/.travis.yml | 69 + .../internal/secp256k1/libsecp256k1/COPYING | 19 + .../secp256k1/libsecp256k1/Makefile.am | 177 + .../internal/secp256k1/libsecp256k1/README.md | 61 + .../internal/secp256k1/libsecp256k1/TODO | 3 + .../secp256k1/libsecp256k1/autogen.sh | 3 + .../build-aux/m4/ax_jni_include_dir.m4 | 140 + .../build-aux/m4/ax_prog_cc_for_build.m4 | 125 + .../libsecp256k1/build-aux/m4/bitcoin_secp.m4 | 69 + .../secp256k1/libsecp256k1/configure.ac | 493 ++ .../libsecp256k1/contrib/lax_der_parsing.c | 150 + .../libsecp256k1/contrib/lax_der_parsing.h | 91 + .../contrib/lax_der_privatekey_parsing.c | 113 + .../contrib/lax_der_privatekey_parsing.h | 90 + .../libsecp256k1/include/secp256k1.h | 577 +++ .../libsecp256k1/include/secp256k1_ecdh.h | 31 + .../libsecp256k1/include/secp256k1_recovery.h | 110 + .../secp256k1/libsecp256k1/libsecp256k1.pc.in | 13 + .../secp256k1/libsecp256k1/obj/.gitignore | 0 .../libsecp256k1/sage/group_prover.sage | 322 ++ .../libsecp256k1/sage/secp256k1.sage | 306 ++ .../libsecp256k1/sage/weierstrass_prover.sage | 264 + .../libsecp256k1/src/asm/field_10x26_arm.s | 919 ++++ .../secp256k1/libsecp256k1/src/basic-config.h | 32 + .../secp256k1/libsecp256k1/src/bench.h | 66 + .../secp256k1/libsecp256k1/src/bench_ecdh.c | 54 + .../libsecp256k1/src/bench_internal.c | 382 ++ .../libsecp256k1/src/bench_recover.c | 60 + .../libsecp256k1/src/bench_schnorr_verify.c | 73 + .../secp256k1/libsecp256k1/src/bench_sign.c | 56 + .../secp256k1/libsecp256k1/src/bench_verify.c | 112 + .../secp256k1/libsecp256k1/src/ecdsa.h | 21 + .../secp256k1/libsecp256k1/src/ecdsa_impl.h | 315 ++ .../secp256k1/libsecp256k1/src/eckey.h | 25 + .../secp256k1/libsecp256k1/src/eckey_impl.h | 99 + .../secp256k1/libsecp256k1/src/ecmult.h | 31 + .../secp256k1/libsecp256k1/src/ecmult_const.h | 15 + .../libsecp256k1/src/ecmult_const_impl.h | 239 + .../secp256k1/libsecp256k1/src/ecmult_gen.h | 43 + .../libsecp256k1/src/ecmult_gen_impl.h | 210 + .../secp256k1/libsecp256k1/src/ecmult_impl.h | 406 ++ .../secp256k1/libsecp256k1/src/field.h | 132 + .../secp256k1/libsecp256k1/src/field_10x26.h | 47 + .../libsecp256k1/src/field_10x26_impl.h | 1140 +++++ .../secp256k1/libsecp256k1/src/field_5x52.h | 47 + .../libsecp256k1/src/field_5x52_asm_impl.h | 502 ++ .../libsecp256k1/src/field_5x52_impl.h | 451 ++ .../libsecp256k1/src/field_5x52_int128_impl.h | 277 + .../secp256k1/libsecp256k1/src/field_impl.h | 315 ++ .../secp256k1/libsecp256k1/src/gen_context.c | 74 + .../secp256k1/libsecp256k1/src/group.h | 144 + .../secp256k1/libsecp256k1/src/group_impl.h | 700 +++ .../secp256k1/libsecp256k1/src/hash.h | 41 + .../secp256k1/libsecp256k1/src/hash_impl.h | 281 + .../src/java/org/bitcoin/NativeSecp256k1.java | 446 ++ .../java/org/bitcoin/NativeSecp256k1Test.java | 226 + .../java/org/bitcoin/NativeSecp256k1Util.java | 45 + .../java/org/bitcoin/Secp256k1Context.java | 51 + .../src/java/org_bitcoin_NativeSecp256k1.c | 377 ++ .../src/java/org_bitcoin_NativeSecp256k1.h | 119 + .../src/java/org_bitcoin_Secp256k1Context.c | 15 + .../src/java/org_bitcoin_Secp256k1Context.h | 22 + .../src/modules/ecdh/Makefile.am.include | 8 + .../libsecp256k1/src/modules/ecdh/main_impl.h | 54 + .../src/modules/ecdh/tests_impl.h | 105 + .../src/modules/recovery/Makefile.am.include | 8 + .../src/modules/recovery/main_impl.h | 193 + .../src/modules/recovery/tests_impl.h | 393 ++ .../internal/secp256k1/libsecp256k1/src/num.h | 74 + .../secp256k1/libsecp256k1/src/num_gmp.h | 20 + .../secp256k1/libsecp256k1/src/num_gmp_impl.h | 288 ++ .../secp256k1/libsecp256k1/src/num_impl.h | 24 + .../secp256k1/libsecp256k1/src/scalar.h | 106 + .../secp256k1/libsecp256k1/src/scalar_4x64.h | 19 + .../libsecp256k1/src/scalar_4x64_impl.h | 949 ++++ .../secp256k1/libsecp256k1/src/scalar_8x32.h | 19 + .../libsecp256k1/src/scalar_8x32_impl.h | 721 +++ .../secp256k1/libsecp256k1/src/scalar_impl.h | 370 ++ .../secp256k1/libsecp256k1/src/scalar_low.h | 15 + .../libsecp256k1/src/scalar_low_impl.h | 114 + .../secp256k1/libsecp256k1/src/secp256k1.c | 559 ++ .../secp256k1/libsecp256k1/src/testrand.h | 38 + .../libsecp256k1/src/testrand_impl.h | 110 + .../secp256k1/libsecp256k1/src/tests.c | 4525 +++++++++++++++++ .../libsecp256k1/src/tests_exhaustive.c | 470 ++ .../secp256k1/libsecp256k1/src/util.h | 113 + .../secp256k1/internal/secp256k1/panic_cb.go | 21 + .../secp256k1/internal/secp256k1/secp256.go | 167 + crypto/secp256k1/secp256k1_cgo.go | 3 +- 97 files changed, 21882 insertions(+), 23 deletions(-) create mode 100644 crypto/secp256k1/internal/secp256k1/.gitignore create mode 100644 crypto/secp256k1/internal/secp256k1/LICENSE create mode 100644 crypto/secp256k1/internal/secp256k1/README.md create mode 100644 crypto/secp256k1/internal/secp256k1/curve.go create mode 100644 crypto/secp256k1/internal/secp256k1/ext.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/.gitignore create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/COPYING create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/README.md create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/TODO create mode 100755 crypto/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/configure.ac create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/obj/.gitignore create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include create mode 100755 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h create mode 100755 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c create mode 100644 crypto/secp256k1/internal/secp256k1/libsecp256k1/src/util.h create mode 100644 crypto/secp256k1/internal/secp256k1/panic_cb.go create mode 100644 crypto/secp256k1/internal/secp256k1/secp256.go diff --git a/Gopkg.lock b/Gopkg.lock index f385e2559..146c9420d 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,14 +34,6 @@ revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" -[[projects]] - digest = "1:b42be5a3601f833e0b9f2d6625d887ec1309764bfcac3d518f3db425dcd4ec5c" - name = "github.com/ethereum/go-ethereum" - packages = ["crypto/secp256k1"] - pruneopts = "T" - revision = "9dc5d1a915ac0e0bd8429d6ac41df50eec91de5f" - version = "v1.8.21" - [[projects]] digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70" name = "github.com/fortytw2/leaktest" @@ -506,7 +498,6 @@ "github.com/btcsuite/btcd/btcec", "github.com/btcsuite/btcutil/base58", "github.com/btcsuite/btcutil/bech32", - "github.com/ethereum/go-ethereum/crypto/secp256k1", "github.com/fortytw2/leaktest", "github.com/go-kit/kit/log", "github.com/go-kit/kit/log/level", diff --git a/Gopkg.toml b/Gopkg.toml index cf905ee75..c334ab71d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -75,18 +75,6 @@ name = "github.com/prometheus/client_golang" version = "^0.9.1" -# we use the secp256k1 implementation: -[[constraint]] - name = "github.com/ethereum/go-ethereum" - version = "^v1.8.21" - - # Prevent dep from pruning build scripts and codegen templates - # note: this leaves the whole go-ethereum package in vendor - # can be removed when https://github.com/golang/dep/issues/1847 is resolved - [[prune.project]] - name = "github.com/ethereum/go-ethereum" - unused-packages = false - ################################### ## Some repos dont have releases. ## Pin to revision diff --git a/crypto/secp256k1/internal/secp256k1/.gitignore b/crypto/secp256k1/internal/secp256k1/.gitignore new file mode 100644 index 000000000..802b6744a --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +*~ diff --git a/crypto/secp256k1/internal/secp256k1/LICENSE b/crypto/secp256k1/internal/secp256k1/LICENSE new file mode 100644 index 000000000..f9090e142 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/LICENSE @@ -0,0 +1,31 @@ +Copyright (c) 2010 The Go Authors. All rights reserved. +Copyright (c) 2011 ThePiachu. All rights reserved. +Copyright (c) 2015 Jeffrey Wilcke. All rights reserved. +Copyright (c) 2015 Felix Lange. All rights reserved. +Copyright (c) 2015 Gustav Simonsson. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of the copyright holder. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/crypto/secp256k1/internal/secp256k1/README.md b/crypto/secp256k1/internal/secp256k1/README.md new file mode 100644 index 000000000..d899ca270 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/README.md @@ -0,0 +1,3 @@ +This package is copied from https://github.com/ethereum/go-ethereum/tree/729bf365b5f17325be9107b63b233da54100eec6/crypto/secp256k1 + +Unlike the rest of go-ethereum it is MIT licensed so compatible with our Apache2.0 license. We opt to copy in here rather than depend on go-ethereum to avoid issues with vendoring of the GPL parts of that repository by downstream. diff --git a/crypto/secp256k1/internal/secp256k1/curve.go b/crypto/secp256k1/internal/secp256k1/curve.go new file mode 100644 index 000000000..5409ee1d2 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/curve.go @@ -0,0 +1,325 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// * The name of ThePiachu may not be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +package secp256k1 + +import ( + "crypto/elliptic" + "math/big" + "unsafe" +) + +/* +#include "libsecp256k1/include/secp256k1.h" +extern int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, const unsigned char *point, const unsigned char *scalar); +*/ +import "C" + +const ( + // number of bits in a big.Word + wordBits = 32 << (uint64(^big.Word(0)) >> 63) + // number of bytes in a big.Word + wordBytes = wordBits / 8 +) + +// readBits encodes the absolute value of bigint as big-endian bytes. Callers +// must ensure that buf has enough space. If buf is too short the result will +// be incomplete. +func readBits(bigint *big.Int, buf []byte) { + i := len(buf) + for _, d := range bigint.Bits() { + for j := 0; j < wordBytes && i > 0; j++ { + i-- + buf[i] = byte(d) + d >>= 8 + } + } +} + +// This code is from https://github.com/ThePiachu/GoBit and implements +// several Koblitz elliptic curves over prime fields. +// +// The curve methods, internally, on Jacobian coordinates. For a given +// (x, y) position on the curve, the Jacobian coordinates are (x1, y1, +// z1) where x = x1/z1² and y = y1/z1³. The greatest speedups come +// when the whole calculation can be performed within the transform +// (as in ScalarMult and ScalarBaseMult). But even for Add and Double, +// it's faster to apply and reverse the transform than to operate in +// affine coordinates. + +// A BitCurve represents a Koblitz Curve with a=0. +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +type BitCurve struct { + P *big.Int // the order of the underlying field + N *big.Int // the order of the base point + B *big.Int // the constant of the BitCurve equation + Gx, Gy *big.Int // (x,y) of the base point + BitSize int // the size of the underlying field +} + +func (BitCurve *BitCurve) Params() *elliptic.CurveParams { + return &elliptic.CurveParams{ + P: BitCurve.P, + N: BitCurve.N, + B: BitCurve.B, + Gx: BitCurve.Gx, + Gy: BitCurve.Gy, + BitSize: BitCurve.BitSize, + } +} + +// IsOnCurve returns true if the given (x,y) lies on the BitCurve. +func (BitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool { + // y² = x³ + b + y2 := new(big.Int).Mul(y, y) //y² + y2.Mod(y2, BitCurve.P) //y²%P + + x3 := new(big.Int).Mul(x, x) //x² + x3.Mul(x3, x) //x³ + + x3.Add(x3, BitCurve.B) //x³+B + x3.Mod(x3, BitCurve.P) //(x³+B)%P + + return x3.Cmp(y2) == 0 +} + +//TODO: double check if the function is okay +// affineFromJacobian reverses the Jacobian transform. See the comment at the +// top of the file. +func (BitCurve *BitCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + zinv := new(big.Int).ModInverse(z, BitCurve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, BitCurve.P) + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, BitCurve.P) + return +} + +// Add returns the sum of (x1,y1) and (x2,y2) +func (BitCurve *BitCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + z := new(big.Int).SetInt64(1) + return BitCurve.affineFromJacobian(BitCurve.addJacobian(x1, y1, z, x2, y2, z)) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (BitCurve *BitCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, BitCurve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, BitCurve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, BitCurve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, BitCurve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, BitCurve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, BitCurve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, BitCurve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, BitCurve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, BitCurve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, BitCurve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, BitCurve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, BitCurve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, BitCurve.P) + + return x3, y3, z3 +} + +// Double returns 2*(x,y) +func (BitCurve *BitCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + z1 := new(big.Int).SetInt64(1) + return BitCurve.affineFromJacobian(BitCurve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (BitCurve *BitCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + a := new(big.Int).Mul(x, x) //X1² + b := new(big.Int).Mul(y, y) //Y1² + c := new(big.Int).Mul(b, b) //B² + + d := new(big.Int).Add(x, b) //X1+B + d.Mul(d, d) //(X1+B)² + d.Sub(d, a) //(X1+B)²-A + d.Sub(d, c) //(X1+B)²-A-C + d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C) + + e := new(big.Int).Mul(big.NewInt(3), a) //3*A + f := new(big.Int).Mul(e, e) //E² + + x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D + x3.Sub(f, x3) //F-2*D + x3.Mod(x3, BitCurve.P) + + y3 := new(big.Int).Sub(d, x3) //D-X3 + y3.Mul(e, y3) //E*(D-X3) + y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C + y3.Mod(y3, BitCurve.P) + + z3 := new(big.Int).Mul(y, z) //Y1*Z1 + z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 + z3.Mod(z3, BitCurve.P) + + return x3, y3, z3 +} + +func (BitCurve *BitCurve) ScalarMult(Bx, By *big.Int, scalar []byte) (*big.Int, *big.Int) { + // Ensure scalar is exactly 32 bytes. We pad always, even if + // scalar is 32 bytes long, to avoid a timing side channel. + if len(scalar) > 32 { + panic("can't handle scalars > 256 bits") + } + // NOTE: potential timing issue + padded := make([]byte, 32) + copy(padded[32-len(scalar):], scalar) + scalar = padded + + // Do the multiplication in C, updating point. + point := make([]byte, 64) + readBits(Bx, point[:32]) + readBits(By, point[32:]) + + pointPtr := (*C.uchar)(unsafe.Pointer(&point[0])) + scalarPtr := (*C.uchar)(unsafe.Pointer(&scalar[0])) + res := C.secp256k1_ext_scalar_mul(context, pointPtr, scalarPtr) + + // Unpack the result and clear temporaries. + x := new(big.Int).SetBytes(point[:32]) + y := new(big.Int).SetBytes(point[32:]) + for i := range point { + point[i] = 0 + } + for i := range padded { + scalar[i] = 0 + } + if res != 1 { + return nil, nil + } + return x, y +} + +// ScalarBaseMult returns k*G, where G is the base point of the group and k is +// an integer in big-endian form. +func (BitCurve *BitCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return BitCurve.ScalarMult(BitCurve.Gx, BitCurve.Gy, k) +} + +// Marshal converts a point into the form specified in section 4.3.6 of ANSI +// X9.62. +func (BitCurve *BitCurve) Marshal(x, y *big.Int) []byte { + byteLen := (BitCurve.BitSize + 7) >> 3 + ret := make([]byte, 1+2*byteLen) + ret[0] = 4 // uncompressed point flag + readBits(x, ret[1:1+byteLen]) + readBits(y, ret[1+byteLen:]) + return ret +} + +// Unmarshal converts a point, serialised by Marshal, into an x, y pair. On +// error, x = nil. +func (BitCurve *BitCurve) Unmarshal(data []byte) (x, y *big.Int) { + byteLen := (BitCurve.BitSize + 7) >> 3 + if len(data) != 1+2*byteLen { + return + } + if data[0] != 4 { // uncompressed form + return + } + x = new(big.Int).SetBytes(data[1 : 1+byteLen]) + y = new(big.Int).SetBytes(data[1+byteLen:]) + return +} + +var theCurve = new(BitCurve) + +func init() { + // See SEC 2 section 2.7.1 + // curve parameters taken from: + // http://www.secg.org/sec2-v2.pdf + theCurve.P, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 0) + theCurve.N, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 0) + theCurve.B, _ = new(big.Int).SetString("0x0000000000000000000000000000000000000000000000000000000000000007", 0) + theCurve.Gx, _ = new(big.Int).SetString("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", 0) + theCurve.Gy, _ = new(big.Int).SetString("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", 0) + theCurve.BitSize = 256 +} + +// S256 returns a BitCurve which implements secp256k1. +func S256() *BitCurve { + return theCurve +} diff --git a/crypto/secp256k1/internal/secp256k1/ext.h b/crypto/secp256k1/internal/secp256k1/ext.h new file mode 100644 index 000000000..e422fe4b4 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/ext.h @@ -0,0 +1,130 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// secp256k1_context_create_sign_verify creates a context for signing and signature verification. +static secp256k1_context* secp256k1_context_create_sign_verify() { + return secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); +} + +// secp256k1_ext_ecdsa_recover recovers the public key of an encoded compact signature. +// +// Returns: 1: recovery was successful +// 0: recovery was not successful +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: pubkey_out: the serialized 65-byte public key of the signer (cannot be NULL) +// In: sigdata: pointer to a 65-byte signature with the recovery id at the end (cannot be NULL) +// msgdata: pointer to a 32-byte message (cannot be NULL) +static int secp256k1_ext_ecdsa_recover( + const secp256k1_context* ctx, + unsigned char *pubkey_out, + const unsigned char *sigdata, + const unsigned char *msgdata +) { + secp256k1_ecdsa_recoverable_signature sig; + secp256k1_pubkey pubkey; + + if (!secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &sig, sigdata, (int)sigdata[64])) { + return 0; + } + if (!secp256k1_ecdsa_recover(ctx, &pubkey, &sig, msgdata)) { + return 0; + } + size_t outputlen = 65; + return secp256k1_ec_pubkey_serialize(ctx, pubkey_out, &outputlen, &pubkey, SECP256K1_EC_UNCOMPRESSED); +} + +// secp256k1_ext_ecdsa_verify verifies an encoded compact signature. +// +// Returns: 1: signature is valid +// 0: signature is invalid +// Args: ctx: pointer to a context object (cannot be NULL) +// In: sigdata: pointer to a 64-byte signature (cannot be NULL) +// msgdata: pointer to a 32-byte message (cannot be NULL) +// pubkeydata: pointer to public key data (cannot be NULL) +// pubkeylen: length of pubkeydata +static int secp256k1_ext_ecdsa_verify( + const secp256k1_context* ctx, + const unsigned char *sigdata, + const unsigned char *msgdata, + const unsigned char *pubkeydata, + size_t pubkeylen +) { + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pubkey; + + if (!secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sigdata)) { + return 0; + } + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) { + return 0; + } + return secp256k1_ecdsa_verify(ctx, &sig, msgdata, &pubkey); +} + +// secp256k1_ext_reencode_pubkey decodes then encodes a public key. It can be used to +// convert between public key formats. The input/output formats are chosen depending on the +// length of the input/output buffers. +// +// Returns: 1: conversion successful +// 0: conversion unsuccessful +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: out: output buffer that will contain the reencoded key (cannot be NULL) +// In: outlen: length of out (33 for compressed keys, 65 for uncompressed keys) +// pubkeydata: the input public key (cannot be NULL) +// pubkeylen: length of pubkeydata +static int secp256k1_ext_reencode_pubkey( + const secp256k1_context* ctx, + unsigned char *out, + size_t outlen, + const unsigned char *pubkeydata, + size_t pubkeylen +) { + secp256k1_pubkey pubkey; + + if (!secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeydata, pubkeylen)) { + return 0; + } + unsigned int flag = (outlen == 33) ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED; + return secp256k1_ec_pubkey_serialize(ctx, out, &outlen, &pubkey, flag); +} + +// secp256k1_ext_scalar_mul multiplies a point by a scalar in constant time. +// +// Returns: 1: multiplication was successful +// 0: scalar was invalid (zero or overflow) +// Args: ctx: pointer to a context object (cannot be NULL) +// Out: point: the multiplied point (usually secret) +// In: point: pointer to a 64-byte public point, +// encoded as two 256bit big-endian numbers. +// scalar: a 32-byte scalar with which to multiply the point +int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, unsigned char *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_fe feX, feY; + secp256k1_gej res; + secp256k1_ge ge; + secp256k1_scalar s; + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + (void)ctx; + + secp256k1_fe_set_b32(&feX, point); + secp256k1_fe_set_b32(&feY, point+32); + secp256k1_ge_set_xy(&ge, &feX, &feY); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + secp256k1_ecmult_const(&res, &ge, &s); + secp256k1_ge_set_gej(&ge, &res); + /* Note: can't use secp256k1_pubkey_save here because it is not constant time. */ + secp256k1_fe_normalize(&ge.x); + secp256k1_fe_normalize(&ge.y); + secp256k1_fe_get_b32(point, &ge.x); + secp256k1_fe_get_b32(point+32, &ge.y); + ret = 1; + } + secp256k1_scalar_clear(&s); + return ret; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/.gitignore b/crypto/secp256k1/internal/secp256k1/libsecp256k1/.gitignore new file mode 100644 index 000000000..87fea161b --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/.gitignore @@ -0,0 +1,49 @@ +bench_inv +bench_ecdh +bench_sign +bench_verify +bench_schnorr_verify +bench_recover +bench_internal +tests +exhaustive_tests +gen_context +*.exe +*.so +*.a +!.gitignore + +Makefile +configure +.libs/ +Makefile.in +aclocal.m4 +autom4te.cache/ +config.log +config.status +*.tar.gz +*.la +libtool +.deps/ +.dirstamp +*.lo +*.o +*~ +src/libsecp256k1-config.h +src/libsecp256k1-config.h.in +src/ecmult_static_context.h +build-aux/config.guess +build-aux/config.sub +build-aux/depcomp +build-aux/install-sh +build-aux/ltmain.sh +build-aux/m4/libtool.m4 +build-aux/m4/lt~obsolete.m4 +build-aux/m4/ltoptions.m4 +build-aux/m4/ltsugar.m4 +build-aux/m4/ltversion.m4 +build-aux/missing +build-aux/compile +build-aux/test-driver +src/stamp-h1 +libsecp256k1.pc diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml b/crypto/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml new file mode 100644 index 000000000..243952924 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/.travis.yml @@ -0,0 +1,69 @@ +language: c +sudo: false +addons: + apt: + packages: libgmp-dev +compiler: + - clang + - gcc +cache: + directories: + - src/java/guava/ +env: + global: + - FIELD=auto BIGNUM=auto SCALAR=auto ENDOMORPHISM=no STATICPRECOMPUTATION=yes ASM=no BUILD=check EXTRAFLAGS= HOST= ECDH=no RECOVERY=no EXPERIMENTAL=no + - GUAVA_URL=https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar GUAVA_JAR=src/java/guava/guava-18.0.jar + matrix: + - SCALAR=32bit RECOVERY=yes + - SCALAR=32bit FIELD=32bit ECDH=yes EXPERIMENTAL=yes + - SCALAR=64bit + - FIELD=64bit RECOVERY=yes + - FIELD=64bit ENDOMORPHISM=yes + - FIELD=64bit ENDOMORPHISM=yes ECDH=yes EXPERIMENTAL=yes + - FIELD=64bit ASM=x86_64 + - FIELD=64bit ENDOMORPHISM=yes ASM=x86_64 + - FIELD=32bit ENDOMORPHISM=yes + - BIGNUM=no + - BIGNUM=no ENDOMORPHISM=yes RECOVERY=yes EXPERIMENTAL=yes + - BIGNUM=no STATICPRECOMPUTATION=no + - BUILD=distcheck + - EXTRAFLAGS=CPPFLAGS=-DDETERMINISTIC + - EXTRAFLAGS=CFLAGS=-O0 + - BUILD=check-java ECDH=yes EXPERIMENTAL=yes +matrix: + fast_finish: true + include: + - compiler: clang + env: HOST=i686-linux-gnu ENDOMORPHISM=yes + addons: + apt: + packages: + - gcc-multilib + - libgmp-dev:i386 + - compiler: clang + env: HOST=i686-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - compiler: gcc + env: HOST=i686-linux-gnu ENDOMORPHISM=yes + addons: + apt: + packages: + - gcc-multilib + - compiler: gcc + env: HOST=i686-linux-gnu + addons: + apt: + packages: + - gcc-multilib + - libgmp-dev:i386 +before_install: mkdir -p `dirname $GUAVA_JAR` +install: if [ ! -f $GUAVA_JAR ]; then wget $GUAVA_URL -O $GUAVA_JAR; fi +before_script: ./autogen.sh +script: + - if [ -n "$HOST" ]; then export USE_HOST="--host=$HOST"; fi + - if [ "x$HOST" = "xi686-linux-gnu" ]; then export CC="$CC -m32"; fi + - ./configure --enable-experimental=$EXPERIMENTAL --enable-endomorphism=$ENDOMORPHISM --with-field=$FIELD --with-bignum=$BIGNUM --with-scalar=$SCALAR --enable-ecmult-static-precomputation=$STATICPRECOMPUTATION --enable-module-ecdh=$ECDH --enable-module-recovery=$RECOVERY $EXTRAFLAGS $USE_HOST && make -j2 $BUILD +os: linux diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/COPYING b/crypto/secp256k1/internal/secp256k1/libsecp256k1/COPYING new file mode 100644 index 000000000..4522a5990 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/COPYING @@ -0,0 +1,19 @@ +Copyright (c) 2013 Pieter Wuille + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am b/crypto/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am new file mode 100644 index 000000000..c071fbe27 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/Makefile.am @@ -0,0 +1,177 @@ +ACLOCAL_AMFLAGS = -I build-aux/m4 + +lib_LTLIBRARIES = libsecp256k1.la +if USE_JNI +JNI_LIB = libsecp256k1_jni.la +noinst_LTLIBRARIES = $(JNI_LIB) +else +JNI_LIB = +endif +include_HEADERS = include/secp256k1.h +noinst_HEADERS = +noinst_HEADERS += src/scalar.h +noinst_HEADERS += src/scalar_4x64.h +noinst_HEADERS += src/scalar_8x32.h +noinst_HEADERS += src/scalar_low.h +noinst_HEADERS += src/scalar_impl.h +noinst_HEADERS += src/scalar_4x64_impl.h +noinst_HEADERS += src/scalar_8x32_impl.h +noinst_HEADERS += src/scalar_low_impl.h +noinst_HEADERS += src/group.h +noinst_HEADERS += src/group_impl.h +noinst_HEADERS += src/num_gmp.h +noinst_HEADERS += src/num_gmp_impl.h +noinst_HEADERS += src/ecdsa.h +noinst_HEADERS += src/ecdsa_impl.h +noinst_HEADERS += src/eckey.h +noinst_HEADERS += src/eckey_impl.h +noinst_HEADERS += src/ecmult.h +noinst_HEADERS += src/ecmult_impl.h +noinst_HEADERS += src/ecmult_const.h +noinst_HEADERS += src/ecmult_const_impl.h +noinst_HEADERS += src/ecmult_gen.h +noinst_HEADERS += src/ecmult_gen_impl.h +noinst_HEADERS += src/num.h +noinst_HEADERS += src/num_impl.h +noinst_HEADERS += src/field_10x26.h +noinst_HEADERS += src/field_10x26_impl.h +noinst_HEADERS += src/field_5x52.h +noinst_HEADERS += src/field_5x52_impl.h +noinst_HEADERS += src/field_5x52_int128_impl.h +noinst_HEADERS += src/field_5x52_asm_impl.h +noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h +noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h +noinst_HEADERS += src/util.h +noinst_HEADERS += src/testrand.h +noinst_HEADERS += src/testrand_impl.h +noinst_HEADERS += src/hash.h +noinst_HEADERS += src/hash_impl.h +noinst_HEADERS += src/field.h +noinst_HEADERS += src/field_impl.h +noinst_HEADERS += src/bench.h +noinst_HEADERS += contrib/lax_der_parsing.h +noinst_HEADERS += contrib/lax_der_parsing.c +noinst_HEADERS += contrib/lax_der_privatekey_parsing.h +noinst_HEADERS += contrib/lax_der_privatekey_parsing.c + +if USE_EXTERNAL_ASM +COMMON_LIB = libsecp256k1_common.la +noinst_LTLIBRARIES = $(COMMON_LIB) +else +COMMON_LIB = +endif + +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = libsecp256k1.pc + +if USE_EXTERNAL_ASM +if USE_ASM_ARM +libsecp256k1_common_la_SOURCES = src/asm/field_10x26_arm.s +endif +endif + +libsecp256k1_la_SOURCES = src/secp256k1.c +libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB) + +libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c +libsecp256k1_jni_la_CPPFLAGS = -DSECP256K1_BUILD $(JNI_INCLUDES) + +noinst_PROGRAMS = +if USE_BENCHMARK +noinst_PROGRAMS += bench_verify bench_sign bench_internal +bench_verify_SOURCES = src/bench_verify.c +bench_verify_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_sign_SOURCES = src/bench_sign.c +bench_sign_LDADD = libsecp256k1.la $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +bench_internal_SOURCES = src/bench_internal.c +bench_internal_LDADD = $(SECP_LIBS) $(COMMON_LIB) +bench_internal_CPPFLAGS = -DSECP256K1_BUILD $(SECP_INCLUDES) +endif + +TESTS = +if USE_TESTS +noinst_PROGRAMS += tests +tests_SOURCES = src/tests.c +tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src -I$(top_srcdir)/include $(SECP_INCLUDES) $(SECP_TEST_INCLUDES) +if !ENABLE_COVERAGE +tests_CPPFLAGS += -DVERIFY +endif +tests_LDADD = $(SECP_LIBS) $(SECP_TEST_LIBS) $(COMMON_LIB) +tests_LDFLAGS = -static +TESTS += tests +endif + +if USE_EXHAUSTIVE_TESTS +noinst_PROGRAMS += exhaustive_tests +exhaustive_tests_SOURCES = src/tests_exhaustive.c +exhaustive_tests_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/src $(SECP_INCLUDES) +if !ENABLE_COVERAGE +exhaustive_tests_CPPFLAGS += -DVERIFY +endif +exhaustive_tests_LDADD = $(SECP_LIBS) +exhaustive_tests_LDFLAGS = -static +TESTS += exhaustive_tests +endif + +JAVAROOT=src/java +JAVAORG=org/bitcoin +JAVA_GUAVA=$(srcdir)/$(JAVAROOT)/guava/guava-18.0.jar +CLASSPATH_ENV=CLASSPATH=$(JAVA_GUAVA) +JAVA_FILES= \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Test.java \ + $(JAVAROOT)/$(JAVAORG)/NativeSecp256k1Util.java \ + $(JAVAROOT)/$(JAVAORG)/Secp256k1Context.java + +if USE_JNI + +$(JAVA_GUAVA): + @echo Guava is missing. Fetch it via: \ + wget https://search.maven.org/remotecontent?filepath=com/google/guava/guava/18.0/guava-18.0.jar -O $(@) + @false + +.stamp-java: $(JAVA_FILES) + @echo Compiling $^ + $(AM_V_at)$(CLASSPATH_ENV) javac $^ + @touch $@ + +if USE_TESTS + +check-java: libsecp256k1.la $(JAVA_GUAVA) .stamp-java + $(AM_V_at)java -Djava.library.path="./:./src:./src/.libs:.libs/" -cp "$(JAVA_GUAVA):$(JAVAROOT)" $(JAVAORG)/NativeSecp256k1Test + +endif +endif + +if USE_ECMULT_STATIC_PRECOMPUTATION +CPPFLAGS_FOR_BUILD +=-I$(top_srcdir) +CFLAGS_FOR_BUILD += -Wall -Wextra -Wno-unused-function + +gen_context_OBJECTS = gen_context.o +gen_context_BIN = gen_context$(BUILD_EXEEXT) +gen_%.o: src/gen_%.c + $(CC_FOR_BUILD) $(CPPFLAGS_FOR_BUILD) $(CFLAGS_FOR_BUILD) -c $< -o $@ + +$(gen_context_BIN): $(gen_context_OBJECTS) + $(CC_FOR_BUILD) $^ -o $@ + +$(libsecp256k1_la_OBJECTS): src/ecmult_static_context.h +$(tests_OBJECTS): src/ecmult_static_context.h +$(bench_internal_OBJECTS): src/ecmult_static_context.h + +src/ecmult_static_context.h: $(gen_context_BIN) + ./$(gen_context_BIN) + +CLEANFILES = $(gen_context_BIN) src/ecmult_static_context.h $(JAVAROOT)/$(JAVAORG)/*.class .stamp-java +endif + +EXTRA_DIST = autogen.sh src/gen_context.c src/basic-config.h $(JAVA_FILES) + +if ENABLE_MODULE_ECDH +include src/modules/ecdh/Makefile.am.include +endif + +if ENABLE_MODULE_RECOVERY +include src/modules/recovery/Makefile.am.include +endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/README.md b/crypto/secp256k1/internal/secp256k1/libsecp256k1/README.md new file mode 100644 index 000000000..8cd344ea8 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/README.md @@ -0,0 +1,61 @@ +libsecp256k1 +============ + +[![Build Status](https://travis-ci.org/bitcoin-core/secp256k1.svg?branch=master)](https://travis-ci.org/bitcoin-core/secp256k1) + +Optimized C library for EC operations on curve secp256k1. + +This library is a work in progress and is being used to research best practices. Use at your own risk. + +Features: +* secp256k1 ECDSA signing/verification and key generation. +* Adding/multiplying private/public keys. +* Serialization/parsing of private keys, public keys, signatures. +* Constant time, constant memory access signing and pubkey generation. +* Derandomized DSA (via RFC6979 or with a caller provided function.) +* Very efficient implementation. + +Implementation details +---------------------- + +* General + * No runtime heap allocation. + * Extensive testing infrastructure. + * Structured to facilitate review and analysis. + * Intended to be portable to any system with a C89 compiler and uint64_t support. + * Expose only higher level interfaces to minimize the API surface and improve application security. ("Be difficult to use insecurely.") +* Field operations + * Optimized implementation of arithmetic modulo the curve's field size (2^256 - 0x1000003D1). + * Using 5 52-bit limbs (including hand-optimized assembly for x86_64, by Diederik Huys). + * Using 10 26-bit limbs. + * Field inverses and square roots using a sliding window over blocks of 1s (by Peter Dettman). +* Scalar operations + * Optimized implementation without data-dependent branches of arithmetic modulo the curve's order. + * Using 4 64-bit limbs (relying on __int128 support in the compiler). + * Using 8 32-bit limbs. +* Group operations + * Point addition formula specifically simplified for the curve equation (y^2 = x^3 + 7). + * Use addition between points in Jacobian and affine coordinates where possible. + * Use a unified addition/doubling formula where necessary to avoid data-dependent branches. + * Point/x comparison without a field inversion by comparison in the Jacobian coordinate space. +* Point multiplication for verification (a*P + b*G). + * Use wNAF notation for point multiplicands. + * Use a much larger window for multiples of G, using precomputed multiples. + * Use Shamir's trick to do the multiplication with the public key and the generator simultaneously. + * Optionally (off by default) use secp256k1's efficiently-computable endomorphism to split the P multiplicand into 2 half-sized ones. +* Point multiplication for signing + * Use a precomputed table of multiples of powers of 16 multiplied with the generator, so general multiplication becomes a series of additions. + * Access the table with branch-free conditional moves so memory access is uniform. + * No data-dependent branches + * The precomputed tables add and eventually subtract points for which no known scalar (private key) is known, preventing even an attacker with control over the private key used to control the data internally. + +Build steps +----------- + +libsecp256k1 is built using autotools: + + $ ./autogen.sh + $ ./configure + $ make + $ ./tests + $ sudo make install # optional diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/TODO b/crypto/secp256k1/internal/secp256k1/libsecp256k1/TODO new file mode 100644 index 000000000..a300e1c5e --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/TODO @@ -0,0 +1,3 @@ +* Unit tests for fieldelem/groupelem, including ones intended to + trigger fieldelem's boundary cases. +* Complete constant-time operations for signing/keygen diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh b/crypto/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh new file mode 100755 index 000000000..65286b935 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/autogen.sh @@ -0,0 +1,3 @@ +#!/bin/sh +set -e +autoreconf -if --warnings=all diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 b/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 new file mode 100644 index 000000000..1fc362761 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_jni_include_dir.m4 @@ -0,0 +1,140 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_jni_include_dir.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_JNI_INCLUDE_DIR +# +# DESCRIPTION +# +# AX_JNI_INCLUDE_DIR finds include directories needed for compiling +# programs using the JNI interface. +# +# JNI include directories are usually in the Java distribution. This is +# deduced from the value of $JAVA_HOME, $JAVAC, or the path to "javac", in +# that order. When this macro completes, a list of directories is left in +# the variable JNI_INCLUDE_DIRS. +# +# Example usage follows: +# +# AX_JNI_INCLUDE_DIR +# +# for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS +# do +# CPPFLAGS="$CPPFLAGS -I$JNI_INCLUDE_DIR" +# done +# +# If you want to force a specific compiler: +# +# - at the configure.in level, set JAVAC=yourcompiler before calling +# AX_JNI_INCLUDE_DIR +# +# - at the configure level, setenv JAVAC +# +# Note: This macro can work with the autoconf M4 macros for Java programs. +# This particular macro is not part of the original set of macros. +# +# LICENSE +# +# Copyright (c) 2008 Don Anderson +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 10 + +AU_ALIAS([AC_JNI_INCLUDE_DIR], [AX_JNI_INCLUDE_DIR]) +AC_DEFUN([AX_JNI_INCLUDE_DIR],[ + +JNI_INCLUDE_DIRS="" + +if test "x$JAVA_HOME" != x; then + _JTOPDIR="$JAVA_HOME" +else + if test "x$JAVAC" = x; then + JAVAC=javac + fi + AC_PATH_PROG([_ACJNI_JAVAC], [$JAVAC], [no]) + if test "x$_ACJNI_JAVAC" = xno; then + AC_MSG_WARN([cannot find JDK; try setting \$JAVAC or \$JAVA_HOME]) + fi + _ACJNI_FOLLOW_SYMLINKS("$_ACJNI_JAVAC") + _JTOPDIR=`echo "$_ACJNI_FOLLOWED" | sed -e 's://*:/:g' -e 's:/[[^/]]*$::'` +fi + +case "$host_os" in + darwin*) _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + _JINC="$_JTOPDIR/Headers";; + *) _JINC="$_JTOPDIR/include";; +esac +_AS_ECHO_LOG([_JTOPDIR=$_JTOPDIR]) +_AS_ECHO_LOG([_JINC=$_JINC]) + +# On Mac OS X 10.6.4, jni.h is a symlink: +# /System/Library/Frameworks/JavaVM.framework/Versions/Current/Headers/jni.h +# -> ../../CurrentJDK/Headers/jni.h. + +AC_CACHE_CHECK(jni headers, ac_cv_jni_header_path, +[ +if test -f "$_JINC/jni.h"; then + ac_cv_jni_header_path="$_JINC" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" +else + _JTOPDIR=`echo "$_JTOPDIR" | sed -e 's:/[[^/]]*$::'` + if test -f "$_JTOPDIR/include/jni.h"; then + ac_cv_jni_header_path="$_JTOPDIR/include" + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $ac_cv_jni_header_path" + else + ac_cv_jni_header_path=none + fi +fi +]) + + + +# get the likely subdirectories for system specific java includes +case "$host_os" in +bsdi*) _JNI_INC_SUBDIRS="bsdos";; +darwin*) _JNI_INC_SUBDIRS="darwin";; +freebsd*) _JNI_INC_SUBDIRS="freebsd";; +linux*) _JNI_INC_SUBDIRS="linux genunix";; +osf*) _JNI_INC_SUBDIRS="alpha";; +solaris*) _JNI_INC_SUBDIRS="solaris";; +mingw*) _JNI_INC_SUBDIRS="win32";; +cygwin*) _JNI_INC_SUBDIRS="win32";; +*) _JNI_INC_SUBDIRS="genunix";; +esac + +if test "x$ac_cv_jni_header_path" != "xnone"; then + # add any subdirectories that are present + for JINCSUBDIR in $_JNI_INC_SUBDIRS + do + if test -d "$_JTOPDIR/include/$JINCSUBDIR"; then + JNI_INCLUDE_DIRS="$JNI_INCLUDE_DIRS $_JTOPDIR/include/$JINCSUBDIR" + fi + done +fi +]) + +# _ACJNI_FOLLOW_SYMLINKS +# Follows symbolic links on , +# finally setting variable _ACJNI_FOLLOWED +# ---------------------------------------- +AC_DEFUN([_ACJNI_FOLLOW_SYMLINKS],[ +# find the include directory relative to the javac executable +_cur="$1" +while ls -ld "$_cur" 2>/dev/null | grep " -> " >/dev/null; do + AC_MSG_CHECKING([symlink for $_cur]) + _slink=`ls -ld "$_cur" | sed 's/.* -> //'` + case "$_slink" in + /*) _cur="$_slink";; + # 'X' avoids triggering unwanted echo options. + *) _cur=`echo "X$_cur" | sed -e 's/^X//' -e 's:[[^/]]*$::'`"$_slink";; + esac + AC_MSG_RESULT([$_cur]) +done +_ACJNI_FOLLOWED="$_cur" +])# _ACJNI diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 b/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 new file mode 100644 index 000000000..77fd346a7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/ax_prog_cc_for_build.m4 @@ -0,0 +1,125 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_prog_cc_for_build.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_CC_FOR_BUILD +# +# DESCRIPTION +# +# This macro searches for a C compiler that generates native executables, +# that is a C compiler that surely is not a cross-compiler. This can be +# useful if you have to generate source code at compile-time like for +# example GCC does. +# +# The macro sets the CC_FOR_BUILD and CPP_FOR_BUILD macros to anything +# needed to compile or link (CC_FOR_BUILD) and preprocess (CPP_FOR_BUILD). +# The value of these variables can be overridden by the user by specifying +# a compiler with an environment variable (like you do for standard CC). +# +# It also sets BUILD_EXEEXT and BUILD_OBJEXT to the executable and object +# file extensions for the build platform, and GCC_FOR_BUILD to `yes' if +# the compiler we found is GCC. All these variables but GCC_FOR_BUILD are +# substituted in the Makefile. +# +# LICENSE +# +# Copyright (c) 2008 Paolo Bonzini +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 8 + +AU_ALIAS([AC_PROG_CC_FOR_BUILD], [AX_PROG_CC_FOR_BUILD]) +AC_DEFUN([AX_PROG_CC_FOR_BUILD], [dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_PROG_CPP])dnl +AC_REQUIRE([AC_EXEEXT])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl + +dnl Use the standard macros, but make them use other variable names +dnl +pushdef([ac_cv_prog_CPP], ac_cv_build_prog_CPP)dnl +pushdef([ac_cv_prog_gcc], ac_cv_build_prog_gcc)dnl +pushdef([ac_cv_prog_cc_works], ac_cv_build_prog_cc_works)dnl +pushdef([ac_cv_prog_cc_cross], ac_cv_build_prog_cc_cross)dnl +pushdef([ac_cv_prog_cc_g], ac_cv_build_prog_cc_g)dnl +pushdef([ac_cv_exeext], ac_cv_build_exeext)dnl +pushdef([ac_cv_objext], ac_cv_build_objext)dnl +pushdef([ac_exeext], ac_build_exeext)dnl +pushdef([ac_objext], ac_build_objext)dnl +pushdef([CC], CC_FOR_BUILD)dnl +pushdef([CPP], CPP_FOR_BUILD)dnl +pushdef([CFLAGS], CFLAGS_FOR_BUILD)dnl +pushdef([CPPFLAGS], CPPFLAGS_FOR_BUILD)dnl +pushdef([LDFLAGS], LDFLAGS_FOR_BUILD)dnl +pushdef([host], build)dnl +pushdef([host_alias], build_alias)dnl +pushdef([host_cpu], build_cpu)dnl +pushdef([host_vendor], build_vendor)dnl +pushdef([host_os], build_os)dnl +pushdef([ac_cv_host], ac_cv_build)dnl +pushdef([ac_cv_host_alias], ac_cv_build_alias)dnl +pushdef([ac_cv_host_cpu], ac_cv_build_cpu)dnl +pushdef([ac_cv_host_vendor], ac_cv_build_vendor)dnl +pushdef([ac_cv_host_os], ac_cv_build_os)dnl +pushdef([ac_cpp], ac_build_cpp)dnl +pushdef([ac_compile], ac_build_compile)dnl +pushdef([ac_link], ac_build_link)dnl + +save_cross_compiling=$cross_compiling +save_ac_tool_prefix=$ac_tool_prefix +cross_compiling=no +ac_tool_prefix= + +AC_PROG_CC +AC_PROG_CPP +AC_EXEEXT + +ac_tool_prefix=$save_ac_tool_prefix +cross_compiling=$save_cross_compiling + +dnl Restore the old definitions +dnl +popdef([ac_link])dnl +popdef([ac_compile])dnl +popdef([ac_cpp])dnl +popdef([ac_cv_host_os])dnl +popdef([ac_cv_host_vendor])dnl +popdef([ac_cv_host_cpu])dnl +popdef([ac_cv_host_alias])dnl +popdef([ac_cv_host])dnl +popdef([host_os])dnl +popdef([host_vendor])dnl +popdef([host_cpu])dnl +popdef([host_alias])dnl +popdef([host])dnl +popdef([LDFLAGS])dnl +popdef([CPPFLAGS])dnl +popdef([CFLAGS])dnl +popdef([CPP])dnl +popdef([CC])dnl +popdef([ac_objext])dnl +popdef([ac_exeext])dnl +popdef([ac_cv_objext])dnl +popdef([ac_cv_exeext])dnl +popdef([ac_cv_prog_cc_g])dnl +popdef([ac_cv_prog_cc_cross])dnl +popdef([ac_cv_prog_cc_works])dnl +popdef([ac_cv_prog_gcc])dnl +popdef([ac_cv_prog_CPP])dnl + +dnl Finally, set Makefile variables +dnl +BUILD_EXEEXT=$ac_build_exeext +BUILD_OBJEXT=$ac_build_objext +AC_SUBST(BUILD_EXEEXT)dnl +AC_SUBST(BUILD_OBJEXT)dnl +AC_SUBST([CFLAGS_FOR_BUILD])dnl +AC_SUBST([CPPFLAGS_FOR_BUILD])dnl +AC_SUBST([LDFLAGS_FOR_BUILD])dnl +]) diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 b/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 new file mode 100644 index 000000000..b74acb8c1 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/build-aux/m4/bitcoin_secp.m4 @@ -0,0 +1,69 @@ +dnl libsecp25k1 helper checks +AC_DEFUN([SECP_INT128_CHECK],[ +has_int128=$ac_cv_type___int128 +]) + +dnl escape "$0x" below using the m4 quadrigaph @S|@, and escape it again with a \ for the shell. +AC_DEFUN([SECP_64BIT_ASM_CHECK],[ +AC_MSG_CHECKING(for x86_64 assembly availability) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include ]],[[ + uint64_t a = 11, tmp; + __asm__ __volatile__("movq \@S|@0x100000000,%1; mulq %%rsi" : "+a"(a) : "S"(tmp) : "cc", "%rdx"); + ]])],[has_64bit_asm=yes],[has_64bit_asm=no]) +AC_MSG_RESULT([$has_64bit_asm]) +]) + +dnl +AC_DEFUN([SECP_OPENSSL_CHECK],[ + has_libcrypto=no + m4_ifdef([PKG_CHECK_MODULES],[ + PKG_CHECK_MODULES([CRYPTO], [libcrypto], [has_libcrypto=yes],[has_libcrypto=no]) + if test x"$has_libcrypto" = x"yes"; then + TEMP_LIBS="$LIBS" + LIBS="$LIBS $CRYPTO_LIBS" + AC_CHECK_LIB(crypto, main,[AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed])],[has_libcrypto=no]) + LIBS="$TEMP_LIBS" + fi + ]) + if test x$has_libcrypto = xno; then + AC_CHECK_HEADER(openssl/crypto.h,[ + AC_CHECK_LIB(crypto, main,[ + has_libcrypto=yes + CRYPTO_LIBS=-lcrypto + AC_DEFINE(HAVE_LIBCRYPTO,1,[Define this symbol if libcrypto is installed]) + ]) + ]) + LIBS= + fi +if test x"$has_libcrypto" = x"yes" && test x"$has_openssl_ec" = x; then + AC_MSG_CHECKING(for EC functions in libcrypto) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #include + #include ]],[[ + EC_KEY *eckey = EC_KEY_new_by_curve_name(NID_secp256k1); + ECDSA_sign(0, NULL, 0, NULL, NULL, eckey); + ECDSA_verify(0, NULL, 0, NULL, 0, eckey); + EC_KEY_free(eckey); + ECDSA_SIG *sig_openssl; + sig_openssl = ECDSA_SIG_new(); + (void)sig_openssl->r; + ECDSA_SIG_free(sig_openssl); + ]])],[has_openssl_ec=yes],[has_openssl_ec=no]) + AC_MSG_RESULT([$has_openssl_ec]) +fi +]) + +dnl +AC_DEFUN([SECP_GMP_CHECK],[ +if test x"$has_gmp" != x"yes"; then + CPPFLAGS_TEMP="$CPPFLAGS" + CPPFLAGS="$GMP_CPPFLAGS $CPPFLAGS" + LIBS_TEMP="$LIBS" + LIBS="$GMP_LIBS $LIBS" + AC_CHECK_HEADER(gmp.h,[AC_CHECK_LIB(gmp, __gmpz_init,[has_gmp=yes; GMP_LIBS="$GMP_LIBS -lgmp"; AC_DEFINE(HAVE_LIBGMP,1,[Define this symbol if libgmp is installed])])]) + CPPFLAGS="$CPPFLAGS_TEMP" + LIBS="$LIBS_TEMP" +fi +]) diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/configure.ac b/crypto/secp256k1/internal/secp256k1/libsecp256k1/configure.ac new file mode 100644 index 000000000..e5fcbcb4e --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/configure.ac @@ -0,0 +1,493 @@ +AC_PREREQ([2.60]) +AC_INIT([libsecp256k1],[0.1]) +AC_CONFIG_AUX_DIR([build-aux]) +AC_CONFIG_MACRO_DIR([build-aux/m4]) +AC_CANONICAL_HOST +AH_TOP([#ifndef LIBSECP256K1_CONFIG_H]) +AH_TOP([#define LIBSECP256K1_CONFIG_H]) +AH_BOTTOM([#endif /*LIBSECP256K1_CONFIG_H*/]) +AM_INIT_AUTOMAKE([foreign subdir-objects]) +LT_INIT + +dnl make the compilation flags quiet unless V=1 is used +m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) + +PKG_PROG_PKG_CONFIG + +AC_PATH_TOOL(AR, ar) +AC_PATH_TOOL(RANLIB, ranlib) +AC_PATH_TOOL(STRIP, strip) +AX_PROG_CC_FOR_BUILD + +if test "x$CFLAGS" = "x"; then + CFLAGS="-g" +fi + +AM_PROG_CC_C_O + +AC_PROG_CC_C89 +if test x"$ac_cv_prog_cc_c89" = x"no"; then + AC_MSG_ERROR([c89 compiler support required]) +fi +AM_PROG_AS + +case $host_os in + *darwin*) + if test x$cross_compiling != xyes; then + AC_PATH_PROG([BREW],brew,) + if test x$BREW != x; then + dnl These Homebrew packages may be keg-only, meaning that they won't be found + dnl in expected paths because they may conflict with system files. Ask + dnl Homebrew where each one is located, then adjust paths accordingly. + + openssl_prefix=`$BREW --prefix openssl 2>/dev/null` + gmp_prefix=`$BREW --prefix gmp 2>/dev/null` + if test x$openssl_prefix != x; then + PKG_CONFIG_PATH="$openssl_prefix/lib/pkgconfig:$PKG_CONFIG_PATH" + export PKG_CONFIG_PATH + fi + if test x$gmp_prefix != x; then + GMP_CPPFLAGS="-I$gmp_prefix/include" + GMP_LIBS="-L$gmp_prefix/lib" + fi + else + AC_PATH_PROG([PORT],port,) + dnl if homebrew isn't installed and macports is, add the macports default paths + dnl as a last resort. + if test x$PORT != x; then + CPPFLAGS="$CPPFLAGS -isystem /opt/local/include" + LDFLAGS="$LDFLAGS -L/opt/local/lib" + fi + fi + fi + ;; +esac + +CFLAGS="$CFLAGS -W" + +warn_CFLAGS="-std=c89 -pedantic -Wall -Wextra -Wcast-align -Wnested-externs -Wshadow -Wstrict-prototypes -Wno-unused-function -Wno-long-long -Wno-overlength-strings" +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS $warn_CFLAGS" +AC_MSG_CHECKING([if ${CC} supports ${warn_CFLAGS}]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" + ]) + +saved_CFLAGS="$CFLAGS" +CFLAGS="$CFLAGS -fvisibility=hidden" +AC_MSG_CHECKING([if ${CC} supports -fvisibility=hidden]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[char foo;]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" + ]) + +AC_ARG_ENABLE(benchmark, + AS_HELP_STRING([--enable-benchmark],[compile benchmark (default is no)]), + [use_benchmark=$enableval], + [use_benchmark=no]) + +AC_ARG_ENABLE(coverage, + AS_HELP_STRING([--enable-coverage],[enable compiler flags to support kcov coverage analysis]), + [enable_coverage=$enableval], + [enable_coverage=no]) + +AC_ARG_ENABLE(tests, + AS_HELP_STRING([--enable-tests],[compile tests (default is yes)]), + [use_tests=$enableval], + [use_tests=yes]) + +AC_ARG_ENABLE(openssl_tests, + AS_HELP_STRING([--enable-openssl-tests],[enable OpenSSL tests, if OpenSSL is available (default is auto)]), + [enable_openssl_tests=$enableval], + [enable_openssl_tests=auto]) + +AC_ARG_ENABLE(experimental, + AS_HELP_STRING([--enable-experimental],[allow experimental configure options (default is no)]), + [use_experimental=$enableval], + [use_experimental=no]) + +AC_ARG_ENABLE(exhaustive_tests, + AS_HELP_STRING([--enable-exhaustive-tests],[compile exhaustive tests (default is yes)]), + [use_exhaustive_tests=$enableval], + [use_exhaustive_tests=yes]) + +AC_ARG_ENABLE(endomorphism, + AS_HELP_STRING([--enable-endomorphism],[enable endomorphism (default is no)]), + [use_endomorphism=$enableval], + [use_endomorphism=no]) + +AC_ARG_ENABLE(ecmult_static_precomputation, + AS_HELP_STRING([--enable-ecmult-static-precomputation],[enable precomputed ecmult table for signing (default is yes)]), + [use_ecmult_static_precomputation=$enableval], + [use_ecmult_static_precomputation=auto]) + +AC_ARG_ENABLE(module_ecdh, + AS_HELP_STRING([--enable-module-ecdh],[enable ECDH shared secret computation (experimental)]), + [enable_module_ecdh=$enableval], + [enable_module_ecdh=no]) + +AC_ARG_ENABLE(module_recovery, + AS_HELP_STRING([--enable-module-recovery],[enable ECDSA pubkey recovery module (default is no)]), + [enable_module_recovery=$enableval], + [enable_module_recovery=no]) + +AC_ARG_ENABLE(jni, + AS_HELP_STRING([--enable-jni],[enable libsecp256k1_jni (default is auto)]), + [use_jni=$enableval], + [use_jni=auto]) + +AC_ARG_WITH([field], [AS_HELP_STRING([--with-field=64bit|32bit|auto], +[Specify Field Implementation. Default is auto])],[req_field=$withval], [req_field=auto]) + +AC_ARG_WITH([bignum], [AS_HELP_STRING([--with-bignum=gmp|no|auto], +[Specify Bignum Implementation. Default is auto])],[req_bignum=$withval], [req_bignum=auto]) + +AC_ARG_WITH([scalar], [AS_HELP_STRING([--with-scalar=64bit|32bit|auto], +[Specify scalar implementation. Default is auto])],[req_scalar=$withval], [req_scalar=auto]) + +AC_ARG_WITH([asm], [AS_HELP_STRING([--with-asm=x86_64|arm|no|auto] +[Specify assembly optimizations to use. Default is auto (experimental: arm)])],[req_asm=$withval], [req_asm=auto]) + +AC_CHECK_TYPES([__int128]) + +AC_MSG_CHECKING([for __builtin_expect]) +AC_COMPILE_IFELSE([AC_LANG_SOURCE([[void myfunc() {__builtin_expect(0,0);}]])], + [ AC_MSG_RESULT([yes]);AC_DEFINE(HAVE_BUILTIN_EXPECT,1,[Define this symbol if __builtin_expect is available]) ], + [ AC_MSG_RESULT([no]) + ]) + +if test x"$enable_coverage" = x"yes"; then + AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code]) + CFLAGS="$CFLAGS -O0 --coverage" + LDFLAGS="--coverage" +else + CFLAGS="$CFLAGS -O3" +fi + +if test x"$use_ecmult_static_precomputation" != x"no"; then + save_cross_compiling=$cross_compiling + cross_compiling=no + TEMP_CC="$CC" + CC="$CC_FOR_BUILD" + AC_MSG_CHECKING([native compiler: ${CC_FOR_BUILD}]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([], [return 0])], + [working_native_cc=yes], + [working_native_cc=no],[dnl]) + CC="$TEMP_CC" + cross_compiling=$save_cross_compiling + + if test x"$working_native_cc" = x"no"; then + set_precomp=no + if test x"$use_ecmult_static_precomputation" = x"yes"; then + AC_MSG_ERROR([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + else + AC_MSG_RESULT([${CC_FOR_BUILD} does not produce working binaries. Please set CC_FOR_BUILD]) + fi + else + AC_MSG_RESULT([ok]) + set_precomp=yes + fi +else + set_precomp=no +fi + +if test x"$req_asm" = x"auto"; then + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" = x"yes"; then + set_asm=x86_64 + fi + if test x"$set_asm" = x; then + set_asm=no + fi +else + set_asm=$req_asm + case $set_asm in + x86_64) + SECP_64BIT_ASM_CHECK + if test x"$has_64bit_asm" != x"yes"; then + AC_MSG_ERROR([x86_64 assembly optimization requested but not available]) + fi + ;; + arm) + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid assembly optimization selection]) + ;; + esac +fi + +if test x"$req_field" = x"auto"; then + if test x"set_asm" = x"x86_64"; then + set_field=64bit + fi + if test x"$set_field" = x; then + SECP_INT128_CHECK + if test x"$has_int128" = x"yes"; then + set_field=64bit + fi + fi + if test x"$set_field" = x; then + set_field=32bit + fi +else + set_field=$req_field + case $set_field in + 64bit) + if test x"$set_asm" != x"x86_64"; then + SECP_INT128_CHECK + if test x"$has_int128" != x"yes"; then + AC_MSG_ERROR([64bit field explicitly requested but neither __int128 support or x86_64 assembly available]) + fi + fi + ;; + 32bit) + ;; + *) + AC_MSG_ERROR([invalid field implementation selection]) + ;; + esac +fi + +if test x"$req_scalar" = x"auto"; then + SECP_INT128_CHECK + if test x"$has_int128" = x"yes"; then + set_scalar=64bit + fi + if test x"$set_scalar" = x; then + set_scalar=32bit + fi +else + set_scalar=$req_scalar + case $set_scalar in + 64bit) + SECP_INT128_CHECK + if test x"$has_int128" != x"yes"; then + AC_MSG_ERROR([64bit scalar explicitly requested but __int128 support not available]) + fi + ;; + 32bit) + ;; + *) + AC_MSG_ERROR([invalid scalar implementation selected]) + ;; + esac +fi + +if test x"$req_bignum" = x"auto"; then + SECP_GMP_CHECK + if test x"$has_gmp" = x"yes"; then + set_bignum=gmp + fi + + if test x"$set_bignum" = x; then + set_bignum=no + fi +else + set_bignum=$req_bignum + case $set_bignum in + gmp) + SECP_GMP_CHECK + if test x"$has_gmp" != x"yes"; then + AC_MSG_ERROR([gmp bignum explicitly requested but libgmp not available]) + fi + ;; + no) + ;; + *) + AC_MSG_ERROR([invalid bignum implementation selection]) + ;; + esac +fi + +# select assembly optimization +use_external_asm=no + +case $set_asm in +x86_64) + AC_DEFINE(USE_ASM_X86_64, 1, [Define this symbol to enable x86_64 assembly optimizations]) + ;; +arm) + use_external_asm=yes + ;; +no) + ;; +*) + AC_MSG_ERROR([invalid assembly optimizations]) + ;; +esac + +# select field implementation +case $set_field in +64bit) + AC_DEFINE(USE_FIELD_5X52, 1, [Define this symbol to use the FIELD_5X52 implementation]) + ;; +32bit) + AC_DEFINE(USE_FIELD_10X26, 1, [Define this symbol to use the FIELD_10X26 implementation]) + ;; +*) + AC_MSG_ERROR([invalid field implementation]) + ;; +esac + +# select bignum implementation +case $set_bignum in +gmp) + AC_DEFINE(HAVE_LIBGMP, 1, [Define this symbol if libgmp is installed]) + AC_DEFINE(USE_NUM_GMP, 1, [Define this symbol to use the gmp implementation for num]) + AC_DEFINE(USE_FIELD_INV_NUM, 1, [Define this symbol to use the num-based field inverse implementation]) + AC_DEFINE(USE_SCALAR_INV_NUM, 1, [Define this symbol to use the num-based scalar inverse implementation]) + ;; +no) + AC_DEFINE(USE_NUM_NONE, 1, [Define this symbol to use no num implementation]) + AC_DEFINE(USE_FIELD_INV_BUILTIN, 1, [Define this symbol to use the native field inverse implementation]) + AC_DEFINE(USE_SCALAR_INV_BUILTIN, 1, [Define this symbol to use the native scalar inverse implementation]) + ;; +*) + AC_MSG_ERROR([invalid bignum implementation]) + ;; +esac + +#select scalar implementation +case $set_scalar in +64bit) + AC_DEFINE(USE_SCALAR_4X64, 1, [Define this symbol to use the 4x64 scalar implementation]) + ;; +32bit) + AC_DEFINE(USE_SCALAR_8X32, 1, [Define this symbol to use the 8x32 scalar implementation]) + ;; +*) + AC_MSG_ERROR([invalid scalar implementation]) + ;; +esac + +if test x"$use_tests" = x"yes"; then + SECP_OPENSSL_CHECK + if test x"$has_openssl_ec" = x"yes"; then + if test x"$enable_openssl_tests" != x"no"; then + AC_DEFINE(ENABLE_OPENSSL_TESTS, 1, [Define this symbol if OpenSSL EC functions are available]) + SECP_TEST_INCLUDES="$SSL_CFLAGS $CRYPTO_CFLAGS" + SECP_TEST_LIBS="$CRYPTO_LIBS" + + case $host in + *mingw*) + SECP_TEST_LIBS="$SECP_TEST_LIBS -lgdi32" + ;; + esac + fi + else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but OpenSSL with EC support is not available]) + fi + fi +else + if test x"$enable_openssl_tests" = x"yes"; then + AC_MSG_ERROR([OpenSSL tests requested but tests are not enabled]) + fi +fi + +if test x"$use_jni" != x"no"; then + AX_JNI_INCLUDE_DIR + have_jni_dependencies=yes + if test x"$enable_module_ecdh" = x"no"; then + have_jni_dependencies=no + fi + if test "x$JNI_INCLUDE_DIRS" = "x"; then + have_jni_dependencies=no + fi + if test "x$have_jni_dependencies" = "xno"; then + if test x"$use_jni" = x"yes"; then + AC_MSG_ERROR([jni support explicitly requested but headers/dependencies were not found. Enable ECDH and try again.]) + fi + AC_MSG_WARN([jni headers/dependencies not found. jni support disabled]) + use_jni=no + else + use_jni=yes + for JNI_INCLUDE_DIR in $JNI_INCLUDE_DIRS; do + JNI_INCLUDES="$JNI_INCLUDES -I$JNI_INCLUDE_DIR" + done + fi +fi + +if test x"$set_bignum" = x"gmp"; then + SECP_LIBS="$SECP_LIBS $GMP_LIBS" + SECP_INCLUDES="$SECP_INCLUDES $GMP_CPPFLAGS" +fi + +if test x"$use_endomorphism" = x"yes"; then + AC_DEFINE(USE_ENDOMORPHISM, 1, [Define this symbol to use endomorphism optimization]) +fi + +if test x"$set_precomp" = x"yes"; then + AC_DEFINE(USE_ECMULT_STATIC_PRECOMPUTATION, 1, [Define this symbol to use a statically generated ecmult table]) +fi + +if test x"$enable_module_ecdh" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_ECDH, 1, [Define this symbol to enable the ECDH module]) +fi + +if test x"$enable_module_recovery" = x"yes"; then + AC_DEFINE(ENABLE_MODULE_RECOVERY, 1, [Define this symbol to enable the ECDSA pubkey recovery module]) +fi + +AC_C_BIGENDIAN() + +if test x"$use_external_asm" = x"yes"; then + AC_DEFINE(USE_EXTERNAL_ASM, 1, [Define this symbol if an external (non-inline) assembly implementation is used]) +fi + +AC_MSG_NOTICE([Using static precomputation: $set_precomp]) +AC_MSG_NOTICE([Using assembly optimizations: $set_asm]) +AC_MSG_NOTICE([Using field implementation: $set_field]) +AC_MSG_NOTICE([Using bignum implementation: $set_bignum]) +AC_MSG_NOTICE([Using scalar implementation: $set_scalar]) +AC_MSG_NOTICE([Using endomorphism optimizations: $use_endomorphism]) +AC_MSG_NOTICE([Building for coverage analysis: $enable_coverage]) +AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) +AC_MSG_NOTICE([Building ECDSA pubkey recovery module: $enable_module_recovery]) +AC_MSG_NOTICE([Using jni: $use_jni]) + +if test x"$enable_experimental" = x"yes"; then + AC_MSG_NOTICE([******]) + AC_MSG_NOTICE([WARNING: experimental build]) + AC_MSG_NOTICE([Experimental features do not have stable APIs or properties, and may not be safe for production use.]) + AC_MSG_NOTICE([Building ECDH module: $enable_module_ecdh]) + AC_MSG_NOTICE([******]) +else + if test x"$enable_module_ecdh" = x"yes"; then + AC_MSG_ERROR([ECDH module is experimental. Use --enable-experimental to allow.]) + fi + if test x"$set_asm" = x"arm"; then + AC_MSG_ERROR([ARM assembly optimization is experimental. Use --enable-experimental to allow.]) + fi +fi + +AC_CONFIG_HEADERS([src/libsecp256k1-config.h]) +AC_CONFIG_FILES([Makefile libsecp256k1.pc]) +AC_SUBST(JNI_INCLUDES) +AC_SUBST(SECP_INCLUDES) +AC_SUBST(SECP_LIBS) +AC_SUBST(SECP_TEST_LIBS) +AC_SUBST(SECP_TEST_INCLUDES) +AM_CONDITIONAL([ENABLE_COVERAGE], [test x"$enable_coverage" = x"yes"]) +AM_CONDITIONAL([USE_TESTS], [test x"$use_tests" != x"no"]) +AM_CONDITIONAL([USE_EXHAUSTIVE_TESTS], [test x"$use_exhaustive_tests" != x"no"]) +AM_CONDITIONAL([USE_BENCHMARK], [test x"$use_benchmark" = x"yes"]) +AM_CONDITIONAL([USE_ECMULT_STATIC_PRECOMPUTATION], [test x"$set_precomp" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_ECDH], [test x"$enable_module_ecdh" = x"yes"]) +AM_CONDITIONAL([ENABLE_MODULE_RECOVERY], [test x"$enable_module_recovery" = x"yes"]) +AM_CONDITIONAL([USE_JNI], [test x"$use_jni" == x"yes"]) +AM_CONDITIONAL([USE_EXTERNAL_ASM], [test x"$use_external_asm" = x"yes"]) +AM_CONDITIONAL([USE_ASM_ARM], [test x"$set_asm" = x"arm"]) + +dnl make sure nothing new is exported so that we don't break the cache +PKGCONFIG_PATH_TEMP="$PKG_CONFIG_PATH" +unset PKG_CONFIG_PATH +PKG_CONFIG_PATH="$PKGCONFIG_PATH_TEMP" + +AC_OUTPUT diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c new file mode 100644 index 000000000..5b141a994 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.c @@ -0,0 +1,150 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_parsing.h" + +int ecdsa_signature_parse_der_lax(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + size_t rpos, rlen, spos, slen; + size_t pos = 0; + size_t lenbyte; + unsigned char tmpsig[64] = {0}; + int overflow = 0; + + /* Hack to initialize sig with a correctly-parsed but invalid signature. */ + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + + /* Sequence tag byte */ + if (pos == inputlen || input[pos] != 0x30) { + return 0; + } + pos++; + + /* Sequence length bytes */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + pos += lenbyte; + } + + /* Integer tag byte for R */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for R */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + rlen = 0; + while (lenbyte > 0) { + rlen = (rlen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + rlen = lenbyte; + } + if (rlen > inputlen - pos) { + return 0; + } + rpos = pos; + pos += rlen; + + /* Integer tag byte for S */ + if (pos == inputlen || input[pos] != 0x02) { + return 0; + } + pos++; + + /* Integer length for S */ + if (pos == inputlen) { + return 0; + } + lenbyte = input[pos++]; + if (lenbyte & 0x80) { + lenbyte -= 0x80; + if (pos + lenbyte > inputlen) { + return 0; + } + while (lenbyte > 0 && input[pos] == 0) { + pos++; + lenbyte--; + } + if (lenbyte >= sizeof(size_t)) { + return 0; + } + slen = 0; + while (lenbyte > 0) { + slen = (slen << 8) + input[pos]; + pos++; + lenbyte--; + } + } else { + slen = lenbyte; + } + if (slen > inputlen - pos) { + return 0; + } + spos = pos; + pos += slen; + + /* Ignore leading zeroes in R */ + while (rlen > 0 && input[rpos] == 0) { + rlen--; + rpos++; + } + /* Copy R value */ + if (rlen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 32 - rlen, input + rpos, rlen); + } + + /* Ignore leading zeroes in S */ + while (slen > 0 && input[spos] == 0) { + slen--; + spos++; + } + /* Copy S value */ + if (slen > 32) { + overflow = 1; + } else { + memcpy(tmpsig + 64 - slen, input + spos, slen); + } + + if (!overflow) { + overflow = !secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + if (overflow) { + memset(tmpsig, 0, 64); + secp256k1_ecdsa_signature_parse_compact(ctx, sig, tmpsig); + } + return 1; +} + diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h new file mode 100644 index 000000000..6d27871a7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_parsing.h @@ -0,0 +1,91 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file defines a function that parses DER with various errors and + * violations. This is not a part of the library itself, because the allowed + * violations are chosen arbitrarily and do not follow or establish any + * standard. + * + * In many places it matters that different implementations do not only accept + * the same set of valid signatures, but also reject the same set of signatures. + * The only means to accomplish that is by strictly obeying a standard, and not + * accepting anything else. + * + * Nonetheless, sometimes there is a need for compatibility with systems that + * use signatures which do not strictly obey DER. The snippet below shows how + * certain violations are easily supported. You may need to adapt it. + * + * Do not use this for new systems. Use well-defined DER or compact signatures + * instead if you have the choice (see secp256k1_ecdsa_signature_parse_der and + * secp256k1_ecdsa_signature_parse_compact). + * + * The supported violations are: + * - All numbers are parsed as nonnegative integers, even though X.609-0207 + * section 8.3.3 specifies that integers are always encoded as two's + * complement. + * - Integers can have length 0, even though section 8.3.1 says they can't. + * - Integers with overly long padding are accepted, violation section + * 8.3.2. + * - 127-byte long length descriptors are accepted, even though section + * 8.1.3.5.c says that they are not. + * - Trailing garbage data inside or after the signature is ignored. + * - The length descriptor of the sequence is ignored. + * + * Compared to for example OpenSSL, many violations are NOT supported: + * - Using overly long tag descriptors for the sequence or integers inside, + * violating section 8.1.2.2. + * - Encoding primitive integers as constructed values, violating section + * 8.3.1. + */ + +#ifndef _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ +#define _SECP256K1_CONTRIB_LAX_DER_PARSING_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** Parse a signature in "lax DER" format + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * + * This function will accept any valid DER encoded signature, even if the + * encoded numbers are out of range. In addition, it will accept signatures + * which violate the DER spec in various ways. Its purpose is to allow + * validation of the Bitcoin blockchain, which includes non-DER signatures + * from before the network rules were updated to enforce DER. Note that + * the set of supported violations is a strict subset of what OpenSSL will + * accept. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +int ecdsa_signature_parse_der_lax( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c new file mode 100644 index 000000000..c2e63b4b8 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.c @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "lax_der_privatekey_parsing.h" + +int ec_privkey_import_der(const secp256k1_context* ctx, unsigned char *out32, const unsigned char *privkey, size_t privkeylen) { + const unsigned char *end = privkey + privkeylen; + int lenb = 0; + int len = 0; + memset(out32, 0, 32); + /* sequence header */ + if (end < privkey+1 || *privkey != 0x30) { + return 0; + } + privkey++; + /* sequence length constructor */ + if (end < privkey+1 || !(*privkey & 0x80)) { + return 0; + } + lenb = *privkey & ~0x80; privkey++; + if (lenb < 1 || lenb > 2) { + return 0; + } + if (end < privkey+lenb) { + return 0; + } + /* sequence length */ + len = privkey[lenb-1] | (lenb > 1 ? privkey[lenb-2] << 8 : 0); + privkey += lenb; + if (end < privkey+len) { + return 0; + } + /* sequence element 0: version number (=1) */ + if (end < privkey+3 || privkey[0] != 0x02 || privkey[1] != 0x01 || privkey[2] != 0x01) { + return 0; + } + privkey += 3; + /* sequence element 1: octet string, up to 32 bytes */ + if (end < privkey+2 || privkey[0] != 0x04 || privkey[1] > 0x20 || end < privkey+2+privkey[1]) { + return 0; + } + memcpy(out32 + 32 - privkey[1], privkey + 2, privkey[1]); + if (!secp256k1_ec_seckey_verify(ctx, out32)) { + memset(out32, 0, 32); + return 0; + } + return 1; +} + +int ec_privkey_export_der(const secp256k1_context *ctx, unsigned char *privkey, size_t *privkeylen, const unsigned char *key32, int compressed) { + secp256k1_pubkey pubkey; + size_t pubkeylen = 0; + if (!secp256k1_ec_pubkey_create(ctx, &pubkey, key32)) { + *privkeylen = 0; + return 0; + } + if (compressed) { + static const unsigned char begin[] = { + 0x30,0x81,0xD3,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0x85,0x30,0x81,0x82,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x21,0x02,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87, + 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8, + 0x17,0x98,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, + 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x24,0x03,0x22,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 33; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } else { + static const unsigned char begin[] = { + 0x30,0x82,0x01,0x13,0x02,0x01,0x01,0x04,0x20 + }; + static const unsigned char middle[] = { + 0xA0,0x81,0xA5,0x30,0x81,0xA2,0x02,0x01,0x01,0x30,0x2C,0x06,0x07,0x2A,0x86,0x48, + 0xCE,0x3D,0x01,0x01,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F,0x30,0x06,0x04,0x01,0x00,0x04,0x01,0x07,0x04, + 0x41,0x04,0x79,0xBE,0x66,0x7E,0xF9,0xDC,0xBB,0xAC,0x55,0xA0,0x62,0x95,0xCE,0x87, + 0x0B,0x07,0x02,0x9B,0xFC,0xDB,0x2D,0xCE,0x28,0xD9,0x59,0xF2,0x81,0x5B,0x16,0xF8, + 0x17,0x98,0x48,0x3A,0xDA,0x77,0x26,0xA3,0xC4,0x65,0x5D,0xA4,0xFB,0xFC,0x0E,0x11, + 0x08,0xA8,0xFD,0x17,0xB4,0x48,0xA6,0x85,0x54,0x19,0x9C,0x47,0xD0,0x8F,0xFB,0x10, + 0xD4,0xB8,0x02,0x21,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFE,0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B,0xBF,0xD2,0x5E, + 0x8C,0xD0,0x36,0x41,0x41,0x02,0x01,0x01,0xA1,0x44,0x03,0x42,0x00 + }; + unsigned char *ptr = privkey; + memcpy(ptr, begin, sizeof(begin)); ptr += sizeof(begin); + memcpy(ptr, key32, 32); ptr += 32; + memcpy(ptr, middle, sizeof(middle)); ptr += sizeof(middle); + pubkeylen = 65; + secp256k1_ec_pubkey_serialize(ctx, ptr, &pubkeylen, &pubkey, SECP256K1_EC_UNCOMPRESSED); + ptr += pubkeylen; + *privkeylen = ptr - privkey; + } + return 1; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h new file mode 100644 index 000000000..2fd088f8a --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/contrib/lax_der_privatekey_parsing.h @@ -0,0 +1,90 @@ +/********************************************************************** + * Copyright (c) 2014, 2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/**** + * Please do not link this file directly. It is not part of the libsecp256k1 + * project and does not promise any stability in its API, functionality or + * presence. Projects which use this code should instead copy this header + * and its accompanying .c file directly into their codebase. + ****/ + +/* This file contains code snippets that parse DER private keys with + * various errors and violations. This is not a part of the library + * itself, because the allowed violations are chosen arbitrarily and + * do not follow or establish any standard. + * + * It also contains code to serialize private keys in a compatible + * manner. + * + * These functions are meant for compatibility with applications + * that require BER encoded keys. When working with secp256k1-specific + * code, the simple 32-byte private keys normally used by the + * library are sufficient. + */ + +#ifndef _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ +#define _SECP256K1_CONTRIB_BER_PRIVATEKEY_H_ + +#include + +# ifdef __cplusplus +extern "C" { +# endif + +/** Export a private key in DER format. + * + * Returns: 1 if the private key was valid. + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: privkey: pointer to an array for storing the private key in BER. + * Should have space for 279 bytes, and cannot be NULL. + * privkeylen: Pointer to an int where the length of the private key in + * privkey will be stored. + * In: seckey: pointer to a 32-byte secret key to export. + * compressed: 1 if the key should be exported in + * compressed format, 0 otherwise + * + * This function is purely meant for compatibility with applications that + * require BER encoded keys. When working with secp256k1-specific code, the + * simple 32-byte private keys are sufficient. + * + * Note that this function does not guarantee correct DER output. It is + * guaranteed to be parsable by secp256k1_ec_privkey_import_der + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_export_der( + const secp256k1_context* ctx, + unsigned char *privkey, + size_t *privkeylen, + const unsigned char *seckey, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Import a private key in DER format. + * Returns: 1 if a private key was extracted. + * Args: ctx: pointer to a context object (cannot be NULL). + * Out: seckey: pointer to a 32-byte array for storing the private key. + * (cannot be NULL). + * In: privkey: pointer to a private key in DER format (cannot be NULL). + * privkeylen: length of the DER private key pointed to be privkey. + * + * This function will accept more than just strict DER, and even allow some BER + * violations. The public key stored inside the DER-encoded private key is not + * verified for correctness, nor are the curve parameters. Use this function + * only if you know in advance it is supposed to contain a secp256k1 private + * key. + */ +SECP256K1_WARN_UNUSED_RESULT int ec_privkey_import_der( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *privkey, + size_t privkeylen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h new file mode 100644 index 000000000..f268e309d --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1.h @@ -0,0 +1,577 @@ +#ifndef _SECP256K1_ +# define _SECP256K1_ + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/* These rules specify the order of arguments in API calls: + * + * 1. Context pointers go first, followed by output arguments, combined + * output/input arguments, and finally input-only arguments. + * 2. Array lengths always immediately the follow the argument whose length + * they describe, even if this violates rule 1. + * 3. Within the OUT/OUTIN/IN groups, pointers to data that is typically generated + * later go first. This means: signatures, public nonces, private nonces, + * messages, public keys, secret keys, tweaks. + * 4. Arguments that are not data pointers go last, from more complex to less + * complex: function pointers, algorithm names, messages, void pointers, + * counts, flags, booleans. + * 5. Opaque data pointers follow the function pointer they are to be passed to. + */ + +/** Opaque data structure that holds context information (precomputed tables etc.). + * + * The purpose of context structures is to cache large precomputed data tables + * that are expensive to construct, and also to maintain the randomization data + * for blinding. + * + * Do not create a new context object for each operation, as construction is + * far slower than all other API calls (~100 times slower than an ECDSA + * verification). + * + * A constructed context can safely be used from multiple threads + * simultaneously, but API call that take a non-const pointer to a context + * need exclusive access to it. In particular this is the case for + * secp256k1_context_destroy and secp256k1_context_randomize. + * + * Regarding randomization, either do it once at creation time (in which case + * you do not need any locking for the other calls), or use a read-write lock. + */ +typedef struct secp256k1_context_struct secp256k1_context; + +/** Opaque data structure that holds a parsed and valid public key. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pubkey; + +/** Opaque data structured that holds a parsed ECDSA signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_serialize_* functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_ecdsa_signature; + +/** A pointer to a function to deterministically generate a nonce. + * + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). + * data: Arbitrary data pointer that is passed through. + * attempt: how many iterations we have tried to find a nonce. + * This will almost always be 0, but different attempt values + * are required to result in a different nonce. + * + * Except for test cases, this function should compute some cryptographic hash of + * the message, the algorithm, the key and the attempt. + */ +typedef int (*secp256k1_nonce_function)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *algo16, + void *data, + unsigned int attempt +); + +# if !defined(SECP256K1_GNUC_PREREQ) +# if defined(__GNUC__)&&defined(__GNUC_MINOR__) +# define SECP256K1_GNUC_PREREQ(_maj,_min) \ + ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) +# else +# define SECP256K1_GNUC_PREREQ(_maj,_min) 0 +# endif +# endif + +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(2,7) +# define SECP256K1_INLINE __inline__ +# elif (defined(_MSC_VER)) +# define SECP256K1_INLINE __inline +# else +# define SECP256K1_INLINE +# endif +# else +# define SECP256K1_INLINE inline +# endif + +#ifndef SECP256K1_API +# if defined(_WIN32) +# ifdef SECP256K1_BUILD +# define SECP256K1_API __declspec(dllexport) +# else +# define SECP256K1_API +# endif +# elif defined(__GNUC__) && defined(SECP256K1_BUILD) +# define SECP256K1_API __attribute__ ((visibility ("default"))) +# else +# define SECP256K1_API +# endif +#endif + +/**Warning attributes + * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out + * some paranoid null checks. */ +# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) +# else +# define SECP256K1_WARN_UNUSED_RESULT +# endif +# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) +# else +# define SECP256K1_ARG_NONNULL(_x) +# endif + +/** All flags' lower 8 bits indicate what they're for. Do not use directly. */ +#define SECP256K1_FLAGS_TYPE_MASK ((1 << 8) - 1) +#define SECP256K1_FLAGS_TYPE_CONTEXT (1 << 0) +#define SECP256K1_FLAGS_TYPE_COMPRESSION (1 << 1) +/** The higher bits contain the actual data. Do not use directly. */ +#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8) +#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9) +#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8) + +/** Flags to pass to secp256k1_context_create. */ +#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) +#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN) +#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT) + +/** Flag to pass to secp256k1_ec_pubkey_serialize and secp256k1_ec_privkey_export. */ +#define SECP256K1_EC_COMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION | SECP256K1_FLAGS_BIT_COMPRESSION) +#define SECP256K1_EC_UNCOMPRESSED (SECP256K1_FLAGS_TYPE_COMPRESSION) + +/** Create a secp256k1 context object. + * + * Returns: a newly created context object. + * In: flags: which parts of the context to initialize. + */ +SECP256K1_API secp256k1_context* secp256k1_context_create( + unsigned int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Copies a secp256k1 context object. + * + * Returns: a newly created context object. + * Args: ctx: an existing context to copy (cannot be NULL) + */ +SECP256K1_API secp256k1_context* secp256k1_context_clone( + const secp256k1_context* ctx +) SECP256K1_ARG_NONNULL(1) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object. + * + * The context pointer may not be used afterwards. + * Args: ctx: an existing context to destroy (cannot be NULL) + */ +SECP256K1_API void secp256k1_context_destroy( + secp256k1_context* ctx +); + +/** Set a callback function to be called when an illegal argument is passed to + * an API call. It will only trigger for violations that are mentioned + * explicitly in the header. + * + * The philosophy is that these shouldn't be dealt with through a + * specific return value, as calling code should not have branches to deal with + * the case that this code itself is broken. + * + * On the other hand, during debug stage, one would want to be informed about + * such mistakes, and the default (crashing) may be inadvisable. + * When this callback is triggered, the API function called is guaranteed not + * to cause a crash, though its return value and output arguments are + * undefined. + * + * Args: ctx: an existing context object (cannot be NULL) + * In: fun: a pointer to a function to call when an illegal argument is + * passed to the API, taking a message and an opaque pointer + * (NULL restores a default handler that calls abort). + * data: the opaque pointer to pass to fun above. + */ +SECP256K1_API void secp256k1_context_set_illegal_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) SECP256K1_ARG_NONNULL(1); + +/** Set a callback function to be called when an internal consistency check + * fails. The default is crashing. + * + * This can only trigger in case of a hardware failure, miscompilation, + * memory corruption, serious bug in the library, or other error would can + * otherwise result in undefined behaviour. It will not trigger due to mere + * incorrect usage of the API (see secp256k1_context_set_illegal_callback + * for that). After this callback returns, anything may happen, including + * crashing. + * + * Args: ctx: an existing context object (cannot be NULL) + * In: fun: a pointer to a function to call when an internal error occurs, + * taking a message and an opaque pointer (NULL restores a default + * handler that calls abort). + * data: the opaque pointer to pass to fun above. + */ +SECP256K1_API void secp256k1_context_set_error_callback( + secp256k1_context* ctx, + void (*fun)(const char* message, void* data), + const void* data +) SECP256K1_ARG_NONNULL(1); + +/** Parse a variable-length public key into the pubkey object. + * + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * Args: ctx: a secp256k1 context object. + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is undefined. + * In: input: pointer to a serialized public key + * inputlen: length of the array pointed to by input + * + * This function supports parsing compressed (33 bytes, header byte 0x02 or + * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header + * byte 0x06 or 0x07) format public keys. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( + const secp256k1_context* ctx, + secp256k1_pubkey* pubkey, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a pubkey object into a serialized byte sequence. + * + * Returns: 1 always. + * Args: ctx: a secp256k1 context object. + * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if + * compressed==1) byte array to place the serialized key + * in. + * In/Out: outputlen: a pointer to an integer which is initially set to the + * size of output, and is overwritten with the written + * size. + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key. + * flags: SECP256K1_EC_COMPRESSED if serialization should be in + * compressed format, otherwise SECP256K1_EC_UNCOMPRESSED. + */ +SECP256K1_API int secp256k1_ec_pubkey_serialize( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_pubkey* pubkey, + unsigned int flags +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Parse an ECDSA signature in compact (64 bytes) format. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input64: a pointer to the 64-byte array to parse + * + * The signature must consist of a 32-byte big endian R value, followed by a + * 32-byte big endian S value. If R or S fall outside of [0..order-1], the + * encoding is invalid. R and S with value 0 are allowed in the encoding. + * + * After the call, sig will always be initialized. If parsing failed or R or + * S are zero, the resulting sig value is guaranteed to fail validation for any + * message and public key. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_compact( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a DER ECDSA signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * + * This function will accept any valid DER encoded signature, even if the + * encoded numbers are out of range. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +SECP256K1_API int secp256k1_ecdsa_signature_parse_der( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const unsigned char *input, + size_t inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in DER format. + * + * Returns: 1 if enough space was available to serialize, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: output: a pointer to an array to store the DER serialization + * In/Out: outputlen: a pointer to a length integer. Initially, this integer + * should be set to the length of output. After the call + * it will be set to the length of the serialization (even + * if 0 was returned). + * In: sig: a pointer to an initialized signature object + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_der( + const secp256k1_context* ctx, + unsigned char *output, + size_t *outputlen, + const secp256k1_ecdsa_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Serialize an ECDSA signature in compact (64 byte) format. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to a 64-byte array to store the compact serialization + * In: sig: a pointer to an initialized signature object + * + * See secp256k1_ecdsa_signature_parse_compact for details about the encoding. + */ +SECP256K1_API int secp256k1_ecdsa_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + const secp256k1_ecdsa_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify an ECDSA signature. + * + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to an initialized public key to verify with (cannot be NULL) + * + * To avoid accepting malleable signatures, only ECDSA signatures in lower-S + * form are accepted. + * + * If you need to accept ECDSA signatures from sources that do not obey this + * rule, apply secp256k1_ecdsa_signature_normalize to the signature prior to + * validation, but be aware that doing so results in malleable signatures. + * + * For details, see the comments for that function. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( + const secp256k1_context* ctx, + const secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Convert a signature to a normalized lower-S form. + * + * Returns: 1 if sigin was not normalized, 0 if it already was. + * Args: ctx: a secp256k1 context object + * Out: sigout: a pointer to a signature to fill with the normalized form, + * or copy if the input was already normalized. (can be NULL if + * you're only interested in whether the input was already + * normalized). + * In: sigin: a pointer to a signature to check/normalize (cannot be NULL, + * can be identical to sigout) + * + * With ECDSA a third-party can forge a second distinct signature of the same + * message, given a single initial signature, but without knowing the key. This + * is done by negating the S value modulo the order of the curve, 'flipping' + * the sign of the random point R which is not included in the signature. + * + * Forgery of the same message isn't universally problematic, but in systems + * where message malleability or uniqueness of signatures is important this can + * cause issues. This forgery can be blocked by all verifiers forcing signers + * to use a normalized form. + * + * The lower-S form reduces the size of signatures slightly on average when + * variable length encodings (such as DER) are used and is cheap to verify, + * making it a good choice. Security of always using lower-S is assured because + * anyone can trivially modify a signature after the fact to enforce this + * property anyway. + * + * The lower S value is always between 0x1 and + * 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + * inclusive. + * + * No other forms of ECDSA malleability are known and none seem likely, but + * there is no formal proof that ECDSA, even with this additional restriction, + * is free of other malleability. Commonly used serialization schemes will also + * accept various non-unique encodings, so care should be taken when this + * property is required for an application. + * + * The secp256k1_ecdsa_sign function will by default create signatures in the + * lower-S form, and secp256k1_ecdsa_verify will not accept others. In case + * signatures come from a system that cannot enforce this property, + * secp256k1_ecdsa_signature_normalize must be called before verification. + */ +SECP256K1_API int secp256k1_ecdsa_signature_normalize( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sigout, + const secp256k1_ecdsa_signature *sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3); + +/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. + */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_rfc6979; + +/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ +SECP256K1_API extern const secp256k1_nonce_function secp256k1_nonce_function_default; + +/** Create an ECDSA signature. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * + * The created signature is always in lower-S form. See + * secp256k1_ecdsa_signature_normalize for more details. + */ +SECP256K1_API int secp256k1_ecdsa_sign( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify an ECDSA secret key. + * + * Returns: 1: secret key is valid + * 0: secret key is invalid + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seckey: pointer to a 32-byte secret key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( + const secp256k1_context* ctx, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Compute the public key for a secret key. + * + * Returns: 1: secret was valid, public key stores + * 0: secret was invalid, try again + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: pubkey: pointer to the created public key (cannot be NULL) + * In: seckey: pointer to a 32-byte private key (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by adding tweak to it. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or if the resulting private key + * would be invalid (only when the tweak is the complement of the + * private key). 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL). + * In/Out: seckey: pointer to a 32-byte private key. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by adding tweak times the generator to it. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or if the resulting public key + * would be invalid (only when the tweak is the complement of the + * corresponding private key). 1 otherwise. + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL). + * In/Out: pubkey: pointer to a public key object. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by multiplying it by a tweak. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. + * Args: ctx: pointer to a context object (cannot be NULL). + * In/Out: seckey: pointer to a 32-byte private key. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by multiplying it by a tweak value. + * Returns: 0 if the tweak was out of range (chance of around 1 in 2^128 for + * uniformly random 32-byte arrays, or equal to zero. 1 otherwise. + * Args: ctx: pointer to a context object initialized for validation + * (cannot be NULL). + * In/Out: pubkey: pointer to a public key obkect. + * In: tweak: pointer to a 32-byte tweak. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates the context randomization. + * Returns: 1: randomization successfully updated + * 0: error + * Args: ctx: pointer to a context object (cannot be NULL) + * In: seed32: pointer to a 32-byte random seed (NULL resets to initial state) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + secp256k1_context* ctx, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1); + +/** Add a number of public keys together. + * Returns: 1: the sum of the public keys is valid. + * 0: the sum of the public keys is not valid. + * Args: ctx: pointer to a context object + * Out: out: pointer to a public key object for placing the resulting public key + * (cannot be NULL) + * In: ins: pointer to array of pointers to public keys (cannot be NULL) + * n: the number of public keys to add together (must be at least 1) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_pubkey *out, + const secp256k1_pubkey * const * ins, + size_t n +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h new file mode 100644 index 000000000..4b84d7a96 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_ecdh.h @@ -0,0 +1,31 @@ +#ifndef _SECP256K1_ECDH_ +# define _SECP256K1_ECDH_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Compute an EC Diffie-Hellman secret in constant time + * Returns: 1: exponentiation was successful + * 0: scalar was invalid (zero or overflow) + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: result: a 32-byte array which will be populated by an ECDH + * secret computed from the point and scalar + * In: pubkey: a pointer to a secp256k1_pubkey containing an + * initialized public key + * privkey: a 32-byte scalar with which to multiply the point + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( + const secp256k1_context* ctx, + unsigned char *result, + const secp256k1_pubkey *pubkey, + const unsigned char *privkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h new file mode 100644 index 000000000..055379725 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/include/secp256k1_recovery.h @@ -0,0 +1,110 @@ +#ifndef _SECP256K1_RECOVERY_ +# define _SECP256K1_RECOVERY_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Opaque data structured that holds a parsed ECDSA signature, + * supporting pubkey recovery. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 65 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage or transmission, use + * the secp256k1_ecdsa_signature_serialize_* and + * secp256k1_ecdsa_signature_parse_* functions. + * + * Furthermore, it is guaranteed that identical signatures (including their + * recoverability) will have identical representation, so they can be + * memcmp'ed. + */ +typedef struct { + unsigned char data[65]; +} secp256k1_ecdsa_recoverable_signature; + +/** Parse a compact ECDSA signature (64 bytes + recovery id). + * + * Returns: 1 when the signature could be parsed, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: sig: a pointer to a signature object + * In: input64: a pointer to a 64-byte compact signature + * recid: the recovery id (0, 1, 2 or 3) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_parse_compact( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature* sig, + const unsigned char *input64, + int recid +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Convert a recoverable signature into a normal signature. + * + * Returns: 1 + * Out: sig: a pointer to a normal signature (cannot be NULL). + * In: sigin: a pointer to a recoverable signature (cannot be NULL). + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_convert( + const secp256k1_context* ctx, + secp256k1_ecdsa_signature* sig, + const secp256k1_ecdsa_recoverable_signature* sigin +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in compact format (64 bytes + recovery id). + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: output64: a pointer to a 64-byte array of the compact signature (cannot be NULL) + * recid: a pointer to an integer to hold the recovery id (can be NULL). + * In: sig: a pointer to an initialized signature object (cannot be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_recoverable_signature_serialize_compact( + const secp256k1_context* ctx, + unsigned char *output64, + int *recid, + const secp256k1_ecdsa_recoverable_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Create a recoverable ECDSA signature. + * + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_ecdsa_sign_recoverable( + const secp256k1_context* ctx, + secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Recover an ECDSA public key from a signature. + * + * Returns: 1: public key successfully recovered (which guarantees a correct signature). + * 0: otherwise. + * Args: ctx: pointer to a context object, initialized for verification (cannot be NULL) + * Out: pubkey: pointer to the recovered public key (cannot be NULL) + * In: sig: pointer to initialized signature that supports pubkey recovery (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( + const secp256k1_context* ctx, + secp256k1_pubkey *pubkey, + const secp256k1_ecdsa_recoverable_signature *sig, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in b/crypto/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in new file mode 100644 index 000000000..a0d006f11 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/libsecp256k1.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: libsecp256k1 +Description: Optimized C library for EC operations on curve secp256k1 +URL: https://github.com/bitcoin-core/secp256k1 +Version: @PACKAGE_VERSION@ +Cflags: -I${includedir} +Libs.private: @SECP_LIBS@ +Libs: -L${libdir} -lsecp256k1 + diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/obj/.gitignore b/crypto/secp256k1/internal/secp256k1/libsecp256k1/obj/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage b/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage new file mode 100644 index 000000000..ab580c5b2 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/group_prover.sage @@ -0,0 +1,322 @@ +# This code supports verifying group implementations which have branches +# or conditional statements (like cmovs), by allowing each execution path +# to independently set assumptions on input or intermediary variables. +# +# The general approach is: +# * A constraint is a tuple of two sets of of symbolic expressions: +# the first of which are required to evaluate to zero, the second of which +# are required to evaluate to nonzero. +# - A constraint is said to be conflicting if any of its nonzero expressions +# is in the ideal with basis the zero expressions (in other words: when the +# zero expressions imply that one of the nonzero expressions are zero). +# * There is a list of laws that describe the intended behaviour, including +# laws for addition and doubling. Each law is called with the symbolic point +# coordinates as arguments, and returns: +# - A constraint describing the assumptions under which it is applicable, +# called "assumeLaw" +# - A constraint describing the requirements of the law, called "require" +# * Implementations are transliterated into functions that operate as well on +# algebraic input points, and are called once per combination of branches +# exectured. Each execution returns: +# - A constraint describing the assumptions this implementation requires +# (such as Z1=1), called "assumeFormula" +# - A constraint describing the assumptions this specific branch requires, +# but which is by construction guaranteed to cover the entire space by +# merging the results from all branches, called "assumeBranch" +# - The result of the computation +# * All combinations of laws with implementation branches are tried, and: +# - If the combination of assumeLaw, assumeFormula, and assumeBranch results +# in a conflict, it means this law does not apply to this branch, and it is +# skipped. +# - For others, we try to prove the require constraints hold, assuming the +# information in assumeLaw + assumeFormula + assumeBranch, and if this does +# not succeed, we fail. +# + To prove an expression is zero, we check whether it belongs to the +# ideal with the assumed zero expressions as basis. This test is exact. +# + To prove an expression is nonzero, we check whether each of its +# factors is contained in the set of nonzero assumptions' factors. +# This test is not exact, so various combinations of original and +# reduced expressions' factors are tried. +# - If we succeed, we print out the assumptions from assumeFormula that +# weren't implied by assumeLaw already. Those from assumeBranch are skipped, +# as we assume that all constraints in it are complementary with each other. +# +# Based on the sage verification scripts used in the Explicit-Formulas Database +# by Tanja Lange and others, see http://hyperelliptic.org/EFD + +class fastfrac: + """Fractions over rings.""" + + def __init__(self,R,top,bot=1): + """Construct a fractional, given a ring, a numerator, and denominator.""" + self.R = R + if parent(top) == ZZ or parent(top) == R: + self.top = R(top) + self.bot = R(bot) + elif top.__class__ == fastfrac: + self.top = top.top + self.bot = top.bot * bot + else: + self.top = R(numerator(top)) + self.bot = R(denominator(top)) * bot + + def iszero(self,I): + """Return whether this fraction is zero given an ideal.""" + return self.top in I and self.bot not in I + + def reduce(self,assumeZero): + zero = self.R.ideal(map(numerator, assumeZero)) + return fastfrac(self.R, zero.reduce(self.top)) / fastfrac(self.R, zero.reduce(self.bot)) + + def __add__(self,other): + """Add two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top + self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot + self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __sub__(self,other): + """Subtract two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top - self.bot * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot - self.bot * other.top,self.bot * other.bot) + return NotImplemented + + def __neg__(self): + """Return the negation of a fraction.""" + return fastfrac(self.R,-self.top,self.bot) + + def __mul__(self,other): + """Multiply two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top * other,self.bot) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.top,self.bot * other.bot) + return NotImplemented + + def __rmul__(self,other): + """Multiply something else with a fraction.""" + return self.__mul__(other) + + def __div__(self,other): + """Divide two fractions.""" + if parent(other) == ZZ: + return fastfrac(self.R,self.top,self.bot * other) + if other.__class__ == fastfrac: + return fastfrac(self.R,self.top * other.bot,self.bot * other.top) + return NotImplemented + + def __pow__(self,other): + """Compute a power of a fraction.""" + if parent(other) == ZZ: + if other < 0: + # Negative powers require flipping top and bottom + return fastfrac(self.R,self.bot ^ (-other),self.top ^ (-other)) + else: + return fastfrac(self.R,self.top ^ other,self.bot ^ other) + return NotImplemented + + def __str__(self): + return "fastfrac((" + str(self.top) + ") / (" + str(self.bot) + "))" + def __repr__(self): + return "%s" % self + + def numerator(self): + return self.top + +class constraints: + """A set of constraints, consisting of zero and nonzero expressions. + + Constraints can either be used to express knowledge or a requirement. + + Both the fields zero and nonzero are maps from expressions to description + strings. The expressions that are the keys in zero are required to be zero, + and the expressions that are the keys in nonzero are required to be nonzero. + + Note that (a != 0) and (b != 0) is the same as (a*b != 0), so all keys in + nonzero could be multiplied into a single key. This is often much less + efficient to work with though, so we keep them separate inside the + constraints. This allows higher-level code to do fast checks on the individual + nonzero elements, or combine them if needed for stronger checks. + + We can't multiply the different zero elements, as it would suffice for one of + the factors to be zero, instead of all of them. Instead, the zero elements are + typically combined into an ideal first. + """ + + def __init__(self, **kwargs): + if 'zero' in kwargs: + self.zero = dict(kwargs['zero']) + else: + self.zero = dict() + if 'nonzero' in kwargs: + self.nonzero = dict(kwargs['nonzero']) + else: + self.nonzero = dict() + + def negate(self): + return constraints(zero=self.nonzero, nonzero=self.zero) + + def __add__(self, other): + zero = self.zero.copy() + zero.update(other.zero) + nonzero = self.nonzero.copy() + nonzero.update(other.nonzero) + return constraints(zero=zero, nonzero=nonzero) + + def __str__(self): + return "constraints(zero=%s,nonzero=%s)" % (self.zero, self.nonzero) + + def __repr__(self): + return "%s" % self + + +def conflicts(R, con): + """Check whether any of the passed non-zero assumptions is implied by the zero assumptions""" + zero = R.ideal(map(numerator, con.zero)) + if 1 in zero: + return True + # First a cheap check whether any of the individual nonzero terms conflict on + # their own. + for nonzero in con.nonzero: + if nonzero.iszero(zero): + return True + # It can be the case that entries in the nonzero set do not individually + # conflict with the zero set, but their combination does. For example, knowing + # that either x or y is zero is equivalent to having x*y in the zero set. + # Having x or y individually in the nonzero set is not a conflict, but both + # simultaneously is, so that is the right thing to check for. + if reduce(lambda a,b: a * b, con.nonzero, fastfrac(R, 1)).iszero(zero): + return True + return False + + +def get_nonzero_set(R, assume): + """Calculate a simple set of nonzero expressions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = set() + for nz in map(numerator, assume.nonzero): + for (f,n) in nz.factor(): + nonzero.add(f) + rnz = zero.reduce(nz) + for (f,n) in rnz.factor(): + nonzero.add(f) + return nonzero + + +def prove_nonzero(R, exprs, assume): + """Check whether an expression is provably nonzero, given assumptions""" + zero = R.ideal(map(numerator, assume.zero)) + nonzero = get_nonzero_set(R, assume) + expl = set() + ok = True + for expr in exprs: + if numerator(expr) in zero: + return (False, [exprs[expr]]) + allexprs = reduce(lambda a,b: numerator(a)*numerator(b), exprs, 1) + for (f, n) in allexprs.factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for (f, n) in zero.reduce(numerator(allexprs)).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in numerator(expr).factor(): + if f not in nonzero: + ok = False + if ok: + return (True, None) + ok = True + for expr in exprs: + for (f,n) in zero.reduce(numerator(expr)).factor(): + if f not in nonzero: + expl.add(exprs[expr]) + if expl: + return (False, list(expl)) + else: + return (True, None) + + +def prove_zero(R, exprs, assume): + """Check whether all of the passed expressions are provably zero, given assumptions""" + r, e = prove_nonzero(R, dict(map(lambda x: (fastfrac(R, x.bot, 1), exprs[x]), exprs)), assume) + if not r: + return (False, map(lambda x: "Possibly zero denominator: %s" % x, e)) + zero = R.ideal(map(numerator, assume.zero)) + nonzero = prod(x for x in assume.nonzero) + expl = [] + for expr in exprs: + if not expr.iszero(zero): + expl.append(exprs[expr]) + if not expl: + return (True, None) + return (False, expl) + + +def describe_extra(R, assume, assumeExtra): + """Describe what assumptions are added, given existing assumptions""" + zerox = assume.zero.copy() + zerox.update(assumeExtra.zero) + zero = R.ideal(map(numerator, assume.zero)) + zeroextra = R.ideal(map(numerator, zerox)) + nonzero = get_nonzero_set(R, assume) + ret = set() + # Iterate over the extra zero expressions + for base in assumeExtra.zero: + if base not in zero: + add = [] + for (f, n) in numerator(base).factor(): + if f not in nonzero: + add += ["%s" % f] + if add: + ret.add((" * ".join(add)) + " = 0 [%s]" % assumeExtra.zero[base]) + # Iterate over the extra nonzero expressions + for nz in assumeExtra.nonzero: + nzr = zeroextra.reduce(numerator(nz)) + if nzr not in zeroextra: + for (f,n) in nzr.factor(): + if zeroextra.reduce(f) not in nonzero: + ret.add("%s != 0" % zeroextra.reduce(f)) + return ", ".join(x for x in ret) + + +def check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require): + """Check a set of zero and nonzero requirements, given a set of zero and nonzero assumptions""" + assume = assumeLaw + assumeAssert + assumeBranch + + if conflicts(R, assume): + # This formula does not apply + return None + + describe = describe_extra(R, assumeLaw + assumeBranch, assumeAssert) + + ok, msg = prove_zero(R, require.zero, assume) + if not ok: + return "FAIL, %s fails (assuming %s)" % (str(msg), describe) + + res, expl = prove_nonzero(R, require.nonzero, assume) + if not res: + return "FAIL, %s fails (assuming %s)" % (str(expl), describe) + + if describe != "": + return "OK (assuming %s)" % describe + else: + return "OK" + + +def concrete_verify(c): + for k in c.zero: + if k != 0: + return (False, c.zero[k]) + for k in c.nonzero: + if k == 0: + return (False, c.nonzero[k]) + return (True, None) diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage b/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage new file mode 100644 index 000000000..a97e732f7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/secp256k1.sage @@ -0,0 +1,306 @@ +# Test libsecp256k1' group operation implementations using prover.sage + +import sys + +load("group_prover.sage") +load("weierstrass_prover.sage") + +def formula_secp256k1_gej_double_var(a): + """libsecp256k1's secp256k1_gej_double_var, used by various addition functions""" + rz = a.Z * a.Y + rz = rz * 2 + t1 = a.X^2 + t1 = t1 * 3 + t2 = t1^2 + t3 = a.Y^2 + t3 = t3 * 2 + t4 = t3^2 + t4 = t4 * 2 + t3 = t3 * a.X + rx = t3 + rx = rx * 4 + rx = -rx + rx = rx + t2 + t2 = -t2 + t3 = t3 * 6 + t3 = t3 + t2 + ry = t1 * t3 + t2 = -t4 + ry = ry + t2 + return jacobianpoint(rx, ry, rz) + +def formula_secp256k1_gej_add_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_var""" + if branch == 0: + return (constraints(), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z22 = b.Z^2 + z12 = a.Z^2 + u1 = a.X * z22 + u2 = b.X * z12 + s1 = a.Y * z22 + s1 = s1 * b.Z + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={h : 'h=0', i : 'i=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}), r) + if branch == 3: + return (constraints(), constraints(zero={h : 'h=0', a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h2 * h + h = h * b.Z + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge_var, which assume bz==1""" + if branch == 0: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(nonzero={a.Infinity : 'a_infinite'}), b) + if branch == 1: + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite'}, nonzero={b.Infinity : 'b_infinite'}), a) + z12 = a.Z^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * a.Z + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if (branch == 2): + r = formula_secp256k1_gej_double_var(a) + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if (branch == 3): + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(zero={b.Z - 1 : 'b.z=1'}), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_zinv_var(branch, a, b): + """libsecp256k1's secp256k1_gej_add_zinv_var""" + bzinv = b.Z^(-1) + if branch == 0: + return (constraints(), constraints(nonzero={b.Infinity : 'b_infinite'}), a) + if branch == 1: + bzinv2 = bzinv^2 + bzinv3 = bzinv2 * bzinv + rx = b.X * bzinv2 + ry = b.Y * bzinv3 + rz = 1 + return (constraints(), constraints(zero={b.Infinity : 'b_finite'}, nonzero={a.Infinity : 'a_infinite'}), jacobianpoint(rx, ry, rz)) + azz = a.Z * bzinv + z12 = azz^2 + u1 = a.X + u2 = b.X * z12 + s1 = a.Y + s2 = b.Y * z12 + s2 = s2 * azz + h = -u1 + h = h + u2 + i = -s1 + i = i + s2 + if branch == 2: + r = formula_secp256k1_gej_double_var(a) + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0', i : 'i=0'}), r) + if branch == 3: + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite', h : 'h=0'}, nonzero={i : 'i!=0'}), point_at_infinity()) + i2 = i^2 + h2 = h^2 + h3 = h * h2 + rz = a.Z + rz = rz * h + t = u1 * h2 + rx = t + rx = rx * 2 + rx = rx + h3 + rx = -rx + rx = rx + i2 + ry = -rx + ry = ry + t + ry = ry * i + h3 = h3 * s1 + h3 = -h3 + ry = ry + h3 + return (constraints(), constraints(zero={a.Infinity : 'a_finite', b.Infinity : 'b_finite'}, nonzero={h : 'h!=0'}), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge(branch, a, b): + """libsecp256k1's secp256k1_gej_add_ge""" + zeroes = {} + nonzeroes = {} + a_infinity = False + if (branch & 4) != 0: + nonzeroes.update({a.Infinity : 'a_infinite'}) + a_infinity = True + else: + zeroes.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + rr = t^2 + m_alt = -u2 + tt = u1 * m_alt + rr = rr + tt + degenerate = (branch & 3) == 3 + if (branch & 1) != 0: + zeroes.update({m : 'm_zero'}) + else: + nonzeroes.update({m : 'm_nonzero'}) + if (branch & 2) != 0: + zeroes.update({rr : 'rr_zero'}) + else: + nonzeroes.update({rr : 'rr_nonzero'}) + rr_alt = s1 + rr_alt = rr_alt * 2 + m_alt = m_alt + u1 + if not degenerate: + rr_alt = rr + m_alt = m + n = m_alt^2 + q = n * t + n = n^2 + if degenerate: + n = m + t = rr_alt^2 + rz = a.Z * m_alt + infinity = False + if (branch & 8) != 0: + if not a_infinity: + infinity = True + zeroes.update({rz : 'r.z=0'}) + else: + nonzeroes.update({rz : 'r.z!=0'}) + rz = rz * 2 + q = -q + t = t + q + rx = t + t = t * 2 + t = t + q + t = t * rr_alt + t = t + n + ry = -t + rx = rx * 4 + ry = ry * 4 + if a_infinity: + rx = b.X + ry = b.Y + rz = 1 + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zeroes, nonzero=nonzeroes), jacobianpoint(rx, ry, rz)) + +def formula_secp256k1_gej_add_ge_old(branch, a, b): + """libsecp256k1's old secp256k1_gej_add_ge, which fails when ay+by=0 but ax!=bx""" + a_infinity = (branch & 1) != 0 + zero = {} + nonzero = {} + if a_infinity: + nonzero.update({a.Infinity : 'a_infinite'}) + else: + zero.update({a.Infinity : 'a_finite'}) + zz = a.Z^2 + u1 = a.X + u2 = b.X * zz + s1 = a.Y + s2 = b.Y * zz + s2 = s2 * a.Z + z = a.Z + t = u1 + t = t + u2 + m = s1 + m = m + s2 + n = m^2 + q = n * t + n = n^2 + rr = t^2 + t = u1 * u2 + t = -t + rr = rr + t + t = rr^2 + rz = m * z + infinity = False + if (branch & 2) != 0: + if not a_infinity: + infinity = True + else: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(nonzero={z : 'conflict_a'}, zero={z : 'conflict_b'}), point_at_infinity()) + zero.update({rz : 'r.z=0'}) + else: + nonzero.update({rz : 'r.z!=0'}) + rz = rz * (0 if a_infinity else 2) + rx = t + q = -q + rx = rx + q + q = q * 3 + t = t * 2 + t = t + q + t = t * rr + t = t + n + ry = -t + rx = rx * (0 if a_infinity else 4) + ry = ry * (0 if a_infinity else 4) + t = b.X + t = t * (1 if a_infinity else 0) + rx = rx + t + t = b.Y + t = t * (1 if a_infinity else 0) + ry = ry + t + t = (1 if a_infinity else 0) + rz = rz + t + if infinity: + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), point_at_infinity()) + return (constraints(zero={b.Z - 1 : 'b.z=1', b.Infinity : 'b_finite'}), constraints(zero=zero, nonzero=nonzero), jacobianpoint(rx, ry, rz)) + +if __name__ == "__main__": + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge) + check_symbolic_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old) + + if len(sys.argv) >= 2 and sys.argv[1] == "--exhaustive": + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_var", 0, 7, 5, formula_secp256k1_gej_add_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_var", 0, 7, 5, formula_secp256k1_gej_add_ge_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_zinv_var", 0, 7, 5, formula_secp256k1_gej_add_zinv_var, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge", 0, 7, 16, formula_secp256k1_gej_add_ge, 43) + check_exhaustive_jacobian_weierstrass("secp256k1_gej_add_ge_old [should fail]", 0, 7, 4, formula_secp256k1_gej_add_ge_old, 43) diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage b/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage new file mode 100644 index 000000000..03ef2ec90 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/sage/weierstrass_prover.sage @@ -0,0 +1,264 @@ +# Prover implementation for Weierstrass curves of the form +# y^2 = x^3 + A * x + B, specifically with a = 0 and b = 7, with group laws +# operating on affine and Jacobian coordinates, including the point at infinity +# represented by a 4th variable in coordinates. + +load("group_prover.sage") + + +class affinepoint: + def __init__(self, x, y, infinity=0): + self.x = x + self.y = y + self.infinity = infinity + def __str__(self): + return "affinepoint(x=%s,y=%s,inf=%s)" % (self.x, self.y, self.infinity) + + +class jacobianpoint: + def __init__(self, x, y, z, infinity=0): + self.X = x + self.Y = y + self.Z = z + self.Infinity = infinity + def __str__(self): + return "jacobianpoint(X=%s,Y=%s,Z=%s,inf=%s)" % (self.X, self.Y, self.Z, self.Infinity) + + +def point_at_infinity(): + return jacobianpoint(1, 1, 1, 1) + + +def negate(p): + if p.__class__ == affinepoint: + return affinepoint(p.x, -p.y) + if p.__class__ == jacobianpoint: + return jacobianpoint(p.X, -p.Y, p.Z) + assert(False) + + +def on_weierstrass_curve(A, B, p): + """Return a set of zero-expressions for an affine point to be on the curve""" + return constraints(zero={p.x^3 + A*p.x + B - p.y^2: 'on_curve'}) + + +def tangential_to_weierstrass_curve(A, B, p12, p3): + """Return a set of zero-expressions for ((x12,y12),(x3,y3)) to be a line that is tangential to the curve at (x12,y12)""" + return constraints(zero={ + (p12.y - p3.y) * (p12.y * 2) - (p12.x^2 * 3 + A) * (p12.x - p3.x): 'tangential_to_curve' + }) + + +def colinear(p1, p2, p3): + """Return a set of zero-expressions for ((x1,y1),(x2,y2),(x3,y3)) to be collinear""" + return constraints(zero={ + (p1.y - p2.y) * (p1.x - p3.x) - (p1.y - p3.y) * (p1.x - p2.x): 'colinear_1', + (p2.y - p3.y) * (p2.x - p1.x) - (p2.y - p1.y) * (p2.x - p3.x): 'colinear_2', + (p3.y - p1.y) * (p3.x - p2.x) - (p3.y - p2.y) * (p3.x - p1.x): 'colinear_3' + }) + + +def good_affine_point(p): + return constraints(nonzero={p.x : 'nonzero_x', p.y : 'nonzero_y'}) + + +def good_jacobian_point(p): + return constraints(nonzero={p.X : 'nonzero_X', p.Y : 'nonzero_Y', p.Z^6 : 'nonzero_Z'}) + + +def good_point(p): + return constraints(nonzero={p.Z^6 : 'nonzero_X'}) + + +def finite(p, *affine_fns): + con = good_point(p) + constraints(zero={p.Infinity : 'finite_point'}) + if p.Z != 0: + return con + reduce(lambda a, b: a + b, (f(affinepoint(p.X / p.Z^2, p.Y / p.Z^3)) for f in affine_fns), con) + else: + return con + +def infinite(p): + return constraints(nonzero={p.Infinity : 'infinite_point'}) + + +def law_jacobian_weierstrass_add(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian add, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(nonzero={pa.x - pb.x : 'different_x'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + colinear(pa, pb, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_double(A, B, pa, pb, pA, pB, pC): + """Check whether the passed set of coordinates is a valid Jacobian doubling, given assumptions""" + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y - pb.y : 'equal_y'})) + require = (finite(pC, lambda pc: on_weierstrass_curve(A, B, pc) + + tangential_to_weierstrass_curve(A, B, pa, negate(pc)))) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_opposites(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + on_weierstrass_curve(A, B, pb) + + finite(pA) + + finite(pB) + + constraints(zero={pa.x - pb.x : 'equal_x', pa.y + pb.y : 'opposite_y'})) + require = infinite(pC) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_a(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pb) + + infinite(pA) + + finite(pB)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pb.x : 'c.x=b.x', pc.y - pb.y : 'c.y=b.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_b(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + on_weierstrass_curve(A, B, pa) + + infinite(pB) + + finite(pA)) + require = finite(pC, lambda pc: constraints(zero={pc.x - pa.x : 'c.x=a.x', pc.y - pa.y : 'c.y=a.y'})) + return (assumeLaw, require) + + +def law_jacobian_weierstrass_add_infinite_ab(A, B, pa, pb, pA, pB, pC): + assumeLaw = (good_affine_point(pa) + + good_affine_point(pb) + + good_jacobian_point(pA) + + good_jacobian_point(pB) + + infinite(pA) + + infinite(pB)) + require = infinite(pC) + return (assumeLaw, require) + + +laws_jacobian_weierstrass = { + 'add': law_jacobian_weierstrass_add, + 'double': law_jacobian_weierstrass_double, + 'add_opposite': law_jacobian_weierstrass_add_opposites, + 'add_infinite_a': law_jacobian_weierstrass_add_infinite_a, + 'add_infinite_b': law_jacobian_weierstrass_add_infinite_b, + 'add_infinite_ab': law_jacobian_weierstrass_add_infinite_ab +} + + +def check_exhaustive_jacobian_weierstrass(name, A, B, branches, formula, p): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve, by executing and validating the result for every possible addition in a prime field""" + F = Integers(p) + print "Formula %s on Z%i:" % (name, p) + points = [] + for x in xrange(0, p): + for y in xrange(0, p): + point = affinepoint(F(x), F(y)) + r, e = concrete_verify(on_weierstrass_curve(A, B, point)) + if r: + points.append(point) + + for za in xrange(1, p): + for zb in xrange(1, p): + for pa in points: + for pb in points: + for ia in xrange(2): + for ib in xrange(2): + pA = jacobianpoint(pa.x * F(za)^2, pa.y * F(za)^3, F(za), ia) + pB = jacobianpoint(pb.x * F(zb)^2, pb.y * F(zb)^3, F(zb), ib) + for branch in xrange(0, branches): + assumeAssert, assumeBranch, pC = formula(branch, pA, pB) + pC.X = F(pC.X) + pC.Y = F(pC.Y) + pC.Z = F(pC.Z) + pC.Infinity = F(pC.Infinity) + r, e = concrete_verify(assumeAssert + assumeBranch) + if r: + match = False + for key in laws_jacobian_weierstrass: + assumeLaw, require = laws_jacobian_weierstrass[key](A, B, pa, pb, pA, pB, pC) + r, e = concrete_verify(assumeLaw) + if r: + if match: + print " multiple branches for (%s,%s,%s,%s) + (%s,%s,%s,%s)" % (pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity) + else: + match = True + r, e = concrete_verify(require) + if not r: + print " failure in branch %i for (%s,%s,%s,%s) + (%s,%s,%s,%s) = (%s,%s,%s,%s): %s" % (branch, pA.X, pA.Y, pA.Z, pA.Infinity, pB.X, pB.Y, pB.Z, pB.Infinity, pC.X, pC.Y, pC.Z, pC.Infinity, e) + print + + +def check_symbolic_function(R, assumeAssert, assumeBranch, f, A, B, pa, pb, pA, pB, pC): + assumeLaw, require = f(A, B, pa, pb, pA, pB, pC) + return check_symbolic(R, assumeLaw, assumeAssert, assumeBranch, require) + +def check_symbolic_jacobian_weierstrass(name, A, B, branches, formula): + """Verify an implementation of addition of Jacobian points on a Weierstrass curve symbolically""" + R. = PolynomialRing(QQ,8,order='invlex') + lift = lambda x: fastfrac(R,x) + ax = lift(ax) + ay = lift(ay) + Az = lift(Az) + bx = lift(bx) + by = lift(by) + Bz = lift(Bz) + Ai = lift(Ai) + Bi = lift(Bi) + + pa = affinepoint(ax, ay, Ai) + pb = affinepoint(bx, by, Bi) + pA = jacobianpoint(ax * Az^2, ay * Az^3, Az, Ai) + pB = jacobianpoint(bx * Bz^2, by * Bz^3, Bz, Bi) + + res = {} + + for key in laws_jacobian_weierstrass: + res[key] = [] + + print ("Formula " + name + ":") + count = 0 + for branch in xrange(branches): + assumeFormula, assumeBranch, pC = formula(branch, pA, pB) + pC.X = lift(pC.X) + pC.Y = lift(pC.Y) + pC.Z = lift(pC.Z) + pC.Infinity = lift(pC.Infinity) + + for key in laws_jacobian_weierstrass: + res[key].append((check_symbolic_function(R, assumeFormula, assumeBranch, laws_jacobian_weierstrass[key], A, B, pa, pb, pA, pB, pC), branch)) + + for key in res: + print " %s:" % key + val = res[key] + for x in val: + if x[0] is not None: + print " branch %i: %s" % (x[1], x[0]) + + print diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s new file mode 100644 index 000000000..5df561f2f --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/asm/field_10x26_arm.s @@ -0,0 +1,919 @@ +@ vim: set tabstop=8 softtabstop=8 shiftwidth=8 noexpandtab syntax=armasm: +/********************************************************************** + * Copyright (c) 2014 Wladimir J. van der Laan * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +/* +ARM implementation of field_10x26 inner loops. + +Note: + +- To avoid unnecessary loads and make use of available registers, two + 'passes' have every time been interleaved, with the odd passes accumulating c' and d' + which will be added to c and d respectively in the the even passes + +*/ + + .syntax unified + .arch armv7-a + @ eabi attributes - see readelf -A + .eabi_attribute 8, 1 @ Tag_ARM_ISA_use = yes + .eabi_attribute 9, 0 @ Tag_Thumb_ISA_use = no + .eabi_attribute 10, 0 @ Tag_FP_arch = none + .eabi_attribute 24, 1 @ Tag_ABI_align_needed = 8-byte + .eabi_attribute 25, 1 @ Tag_ABI_align_preserved = 8-byte, except leaf SP + .eabi_attribute 30, 2 @ Tag_ABI_optimization_goals = Agressive Speed + .eabi_attribute 34, 1 @ Tag_CPU_unaligned_access = v6 + .text + + @ Field constants + .set field_R0, 0x3d10 + .set field_R1, 0x400 + .set field_not_M, 0xfc000000 @ ~M = ~0x3ffffff + + .align 2 + .global secp256k1_fe_mul_inner + .type secp256k1_fe_mul_inner, %function + @ Arguments: + @ r0 r Restrict: can overlap with a, not with b + @ r1 a + @ r2 b + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_mul_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r7,r8 scratch + r1 a (pointer) + r2 b (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + + /* A - interleaved with B */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #9*4] @ b[9] + ldr r0, [r1, #1*4] @ a[1] + umull r5, r6, r7, r8 @ d = a[0] * b[9] + ldr r14, [r2, #8*4] @ b[8] + umull r9, r10, r0, r8 @ d' = a[1] * b[9] + ldr r7, [r1, #2*4] @ a[2] + umlal r5, r6, r0, r14 @ d += a[1] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r14 @ d' += a[2] * b[8] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r8 @ d += a[2] * b[7] + ldr r14, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r8 @ d' += a[3] * b[7] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r14 @ d += a[3] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r14 @ d' += a[4] * b[6] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r8 @ d += a[4] * b[5] + ldr r14, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r8 @ d' += a[5] * b[5] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r14 @ d += a[5] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r14 @ d' += a[6] * b[4] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[3] + ldr r14, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r8 @ d' += a[7] * b[3] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[7] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r9, r10, r7, r14 @ d' += a[8] * b[2] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r8 @ d += a[8] * b[1] + ldr r14, [r2, #0*4] @ b[0] + umlal r9, r10, r0, r8 @ d' += a[9] * b[1] + ldr r7, [r1, #0*4] @ a[0] + umlal r5, r6, r0, r14 @ d += a[9] * b[0] + @ r7,r14 used in B + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 4*9] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + umull r3, r4, r7, r14 @ c = a[0] * b[0] + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C - interleaved with D */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #2*4] @ b[2] + ldr r14, [r2, #1*4] @ b[1] + umull r11, r12, r7, r8 @ c' = a[0] * b[2] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[1] * b[1] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[2] * b[0] + ldr r0, [r1, #3*4] @ a[3] + umlal r5, r6, r7, r14 @ d += a[2] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[3] * b[9] + ldr r7, [r1, #4*4] @ a[4] + umlal r5, r6, r0, r8 @ d += a[3] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[4] * b[8] + ldr r0, [r1, #5*4] @ a[5] + umlal r5, r6, r7, r14 @ d += a[4] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[5] * b[7] + ldr r7, [r1, #6*4] @ a[6] + umlal r5, r6, r0, r8 @ d += a[5] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r9, r10, r7, r8 @ d' += a[6] * b[6] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r9, r10, r0, r14 @ d' += a[7] * b[5] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r9, r10, r7, r8 @ d' += a[8] * b[4] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r9, r10, r0, r14 @ d' += a[9] * b[3] + umlal r5, r6, r0, r8 @ d += a[9] * b[2] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E - interleaved with F */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #4*4] @ b[4] + umull r11, r12, r7, r8 @ c' = a[0] * b[4] + ldr r8, [r2, #3*4] @ b[3] + umlal r3, r4, r7, r8 @ c += a[0] * b[3] + ldr r7, [r1, #1*4] @ a[1] + umlal r11, r12, r7, r8 @ c' += a[1] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r3, r4, r7, r8 @ c += a[1] * b[2] + ldr r7, [r1, #2*4] @ a[2] + umlal r11, r12, r7, r8 @ c' += a[2] * b[2] + ldr r8, [r2, #1*4] @ b[1] + umlal r3, r4, r7, r8 @ c += a[2] * b[1] + ldr r7, [r1, #3*4] @ a[3] + umlal r11, r12, r7, r8 @ c' += a[3] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r3, r4, r7, r8 @ c += a[3] * b[0] + ldr r7, [r1, #4*4] @ a[4] + umlal r11, r12, r7, r8 @ c' += a[4] * b[0] + ldr r8, [r2, #9*4] @ b[9] + umlal r5, r6, r7, r8 @ d += a[4] * b[9] + ldr r7, [r1, #5*4] @ a[5] + umull r9, r10, r7, r8 @ d' = a[5] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umlal r5, r6, r7, r8 @ d += a[5] * b[8] + ldr r7, [r1, #6*4] @ a[6] + umlal r9, r10, r7, r8 @ d' += a[6] * b[8] + ldr r8, [r2, #7*4] @ b[7] + umlal r5, r6, r7, r8 @ d += a[6] * b[7] + ldr r7, [r1, #7*4] @ a[7] + umlal r9, r10, r7, r8 @ d' += a[7] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r5, r6, r7, r8 @ d += a[7] * b[6] + ldr r7, [r1, #8*4] @ a[8] + umlal r9, r10, r7, r8 @ d' += a[8] * b[6] + ldr r8, [r2, #5*4] @ b[5] + umlal r5, r6, r7, r8 @ d += a[8] * b[5] + ldr r7, [r1, #9*4] @ a[9] + umlal r9, r10, r7, r8 @ d' += a[9] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r5, r6, r7, r8 @ d += a[9] * b[4] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G - interleaved with H */ + ldr r7, [r1, #0*4] @ a[0] + ldr r8, [r2, #6*4] @ b[6] + ldr r14, [r2, #5*4] @ b[5] + umull r11, r12, r7, r8 @ c' = a[0] * b[6] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[1] * b[5] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[2] * b[4] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[3] * b[3] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[4] * b[2] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[5] * b[1] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[6] * b[0] + ldr r0, [r1, #7*4] @ a[7] + umlal r5, r6, r7, r14 @ d += a[6] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[7] * b[9] + ldr r7, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r8 @ d += a[7] * b[8] + ldr r14, [r2, #7*4] @ b[7] + umlal r9, r10, r7, r8 @ d' += a[8] * b[8] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r9, r10, r0, r14 @ d' += a[9] * b[7] + umlal r5, r6, r0, r8 @ d += a[9] * b[6] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I - interleaved with J */ + ldr r8, [r2, #8*4] @ b[8] + ldr r7, [r1, #0*4] @ a[0] + ldr r14, [r2, #7*4] @ b[7] + umull r11, r12, r7, r8 @ c' = a[0] * b[8] + ldr r0, [r1, #1*4] @ a[1] + umlal r3, r4, r7, r14 @ c += a[0] * b[7] + ldr r8, [r2, #6*4] @ b[6] + umlal r11, r12, r0, r14 @ c' += a[1] * b[7] + ldr r7, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r8 @ c += a[1] * b[6] + ldr r14, [r2, #5*4] @ b[5] + umlal r11, r12, r7, r8 @ c' += a[2] * b[6] + ldr r0, [r1, #3*4] @ a[3] + umlal r3, r4, r7, r14 @ c += a[2] * b[5] + ldr r8, [r2, #4*4] @ b[4] + umlal r11, r12, r0, r14 @ c' += a[3] * b[5] + ldr r7, [r1, #4*4] @ a[4] + umlal r3, r4, r0, r8 @ c += a[3] * b[4] + ldr r14, [r2, #3*4] @ b[3] + umlal r11, r12, r7, r8 @ c' += a[4] * b[4] + ldr r0, [r1, #5*4] @ a[5] + umlal r3, r4, r7, r14 @ c += a[4] * b[3] + ldr r8, [r2, #2*4] @ b[2] + umlal r11, r12, r0, r14 @ c' += a[5] * b[3] + ldr r7, [r1, #6*4] @ a[6] + umlal r3, r4, r0, r8 @ c += a[5] * b[2] + ldr r14, [r2, #1*4] @ b[1] + umlal r11, r12, r7, r8 @ c' += a[6] * b[2] + ldr r0, [r1, #7*4] @ a[7] + umlal r3, r4, r7, r14 @ c += a[6] * b[1] + ldr r8, [r2, #0*4] @ b[0] + umlal r11, r12, r0, r14 @ c' += a[7] * b[1] + ldr r7, [r1, #8*4] @ a[8] + umlal r3, r4, r0, r8 @ c += a[7] * b[0] + ldr r14, [r2, #9*4] @ b[9] + umlal r11, r12, r7, r8 @ c' += a[8] * b[0] + ldr r0, [r1, #9*4] @ a[9] + umlal r5, r6, r7, r14 @ d += a[8] * b[9] + ldr r8, [r2, #8*4] @ b[8] + umull r9, r10, r0, r14 @ d' = a[9] * b[9] + umlal r5, r6, r0, r8 @ d += a[9] * b[8] + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_mul_inner, .-secp256k1_fe_mul_inner + + .align 2 + .global secp256k1_fe_sqr_inner + .type secp256k1_fe_sqr_inner, %function + @ Arguments: + @ r0 r Can overlap with a + @ r1 a + @ Stack (total 4+10*4 = 44) + @ sp + #0 saved 'r' pointer + @ sp + #4 + 4*X t0,t1,t2,t3,t4,t5,t6,t7,u8,t9 +secp256k1_fe_sqr_inner: + stmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, r14} + sub sp, sp, #48 @ frame=44 + alignment + str r0, [sp, #0] @ save result address, we need it only at the end + /****************************************** + * Main computation code. + ****************************************** + + Allocation: + r0,r14,r2,r7,r8 scratch + r1 a (pointer) + r3:r4 c + r5:r6 d + r11:r12 c' + r9:r10 d' + + Note: do not write to r[] here, it may overlap with a[] + */ + /* A interleaved with B */ + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r7, [r1, #0*4] @ a[0] + mov r0, r0, asl #1 + ldr r14, [r1, #9*4] @ a[9] + umull r3, r4, r7, r7 @ c = a[0] * a[0] + ldr r8, [r1, #8*4] @ a[8] + mov r7, r7, asl #1 + umull r5, r6, r7, r14 @ d = a[0]*2 * a[9] + ldr r7, [r1, #2*4] @ a[2]*2 + umull r9, r10, r0, r14 @ d' = a[1]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[1]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #3*4] @ a[3]*2 + umlal r9, r10, r7, r8 @ d' += a[2]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[7] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umlal r9, r10, r0, r14 @ d' += a[3]*2 * a[7] + ldr r14, [r1, #5*4] @ a[5] + mov r7, r7, asl #1 + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[6] + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[6] + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[5] + umlal r9, r10, r14, r14 @ d' += a[5] * a[5] + + bic r0, r5, field_not_M @ t9 = d & M + str r0, [sp, #4 + 9*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + /* B */ + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u0 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u0 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t0 = c & M + str r14, [sp, #4 + 0*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u0 * R1 + umlal r3, r4, r0, r14 + + /* C interleaved with D */ + ldr r0, [r1, #0*4] @ a[0]*2 + ldr r14, [r1, #1*4] @ a[1] + mov r0, r0, asl #1 + ldr r8, [r1, #2*4] @ a[2] + umlal r3, r4, r0, r14 @ c += a[0]*2 * a[1] + mov r7, r8, asl #1 @ a[2]*2 + umull r11, r12, r14, r14 @ c' = a[1] * a[1] + ldr r14, [r1, #9*4] @ a[9] + umlal r11, r12, r0, r8 @ c' += a[0]*2 * a[2] + ldr r0, [r1, #3*4] @ a[3]*2 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r7, r14 @ d += a[2]*2 * a[9] + mov r0, r0, asl #1 + ldr r7, [r1, #4*4] @ a[4]*2 + umull r9, r10, r0, r14 @ d' = a[3]*2 * a[9] + ldr r14, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r8 @ d += a[3]*2 * a[8] + mov r7, r7, asl #1 + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r9, r10, r7, r8 @ d' += a[4]*2 * a[8] + ldr r8, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umlal r5, r6, r7, r14 @ d += a[4]*2 * a[7] + umlal r9, r10, r0, r14 @ d' += a[5]*2 * a[7] + umlal r5, r6, r0, r8 @ d += a[5]*2 * a[6] + umlal r9, r10, r8, r8 @ d' += a[6] * a[6] + + bic r0, r5, field_not_M @ u1 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u1 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t1 = c & M + str r14, [sp, #4 + 1*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u1 * R1 + umlal r3, r4, r0, r14 + + /* D */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u2 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u2 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t2 = c & M + str r14, [sp, #4 + 2*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u2 * R1 + umlal r3, r4, r0, r14 + + /* E interleaved with F */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + ldr r14, [r1, #2*4] @ a[2] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + ldr r2, [r1, #4*4] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[3] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[4] + mov r2, r2, asl #1 @ a[4]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[3] + ldr r8, [r1, #9*4] @ a[9] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[2] + ldr r0, [r1, #5*4] @ a[5]*2 + umlal r11, r12, r14, r14 @ c' += a[2] * a[2] + ldr r14, [r1, #8*4] @ a[8] + mov r0, r0, asl #1 + umlal r5, r6, r2, r8 @ d += a[4]*2 * a[9] + ldr r7, [r1, #6*4] @ a[6]*2 + umull r9, r10, r0, r8 @ d' = a[5]*2 * a[9] + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + umlal r5, r6, r0, r14 @ d += a[5]*2 * a[8] + umlal r9, r10, r7, r14 @ d' += a[6]*2 * a[8] + umlal r5, r6, r7, r8 @ d += a[6]*2 * a[7] + umlal r9, r10, r8, r8 @ d' += a[7] * a[7] + + bic r0, r5, field_not_M @ u3 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u3 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t3 = c & M + str r14, [sp, #4 + 3*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u3 * R1 + umlal r3, r4, r0, r14 + + /* F */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u4 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u4 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t4 = c & M + str r14, [sp, #4 + 4*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u4 * R1 + umlal r3, r4, r0, r14 + + /* G interleaved with H */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #5*4] @ a[5] + ldr r2, [r1, #6*4] @ a[6] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[5] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[6] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[5] + mov r7, r7, asl #1 + ldr r8, [r1, #3*4] @ a[3] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[4] + mov r0, r2, asl #1 @ a[6]*2 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[4] + ldr r14, [r1, #9*4] @ a[9] + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[3] + ldr r7, [r1, #7*4] @ a[7]*2 + umlal r11, r12, r8, r8 @ c' += a[3] * a[3] + mov r7, r7, asl #1 + ldr r8, [r1, #8*4] @ a[8] + umlal r5, r6, r0, r14 @ d += a[6]*2 * a[9] + umull r9, r10, r7, r14 @ d' = a[7]*2 * a[9] + umlal r5, r6, r7, r8 @ d += a[7]*2 * a[8] + umlal r9, r10, r8, r8 @ d' += a[8] * a[8] + + bic r0, r5, field_not_M @ u5 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u5 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t5 = c & M + str r14, [sp, #4 + 5*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u5 * R1 + umlal r3, r4, r0, r14 + + /* H */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + adds r5, r5, r9 @ d += d' + adc r6, r6, r10 + + bic r0, r5, field_not_M @ u6 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u6 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t6 = c & M + str r14, [sp, #4 + 6*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u6 * R1 + umlal r3, r4, r0, r14 + + /* I interleaved with J */ + ldr r7, [r1, #0*4] @ a[0]*2 + ldr r0, [r1, #1*4] @ a[1]*2 + mov r7, r7, asl #1 + ldr r8, [r1, #7*4] @ a[7] + ldr r2, [r1, #8*4] @ a[8] + umlal r3, r4, r7, r8 @ c += a[0]*2 * a[7] + ldr r14, [r1, #6*4] @ a[6] + mov r0, r0, asl #1 + umull r11, r12, r7, r2 @ c' = a[0]*2 * a[8] + ldr r7, [r1, #2*4] @ a[2]*2 + umlal r11, r12, r0, r8 @ c' += a[1]*2 * a[7] + ldr r8, [r1, #5*4] @ a[5] + umlal r3, r4, r0, r14 @ c += a[1]*2 * a[6] + ldr r0, [r1, #3*4] @ a[3]*2 + mov r7, r7, asl #1 + umlal r11, r12, r7, r14 @ c' += a[2]*2 * a[6] + ldr r14, [r1, #4*4] @ a[4] + mov r0, r0, asl #1 + umlal r3, r4, r7, r8 @ c += a[2]*2 * a[5] + mov r2, r2, asl #1 @ a[8]*2 + umlal r11, r12, r0, r8 @ c' += a[3]*2 * a[5] + umlal r3, r4, r0, r14 @ c += a[3]*2 * a[4] + umlal r11, r12, r14, r14 @ c' += a[4] * a[4] + ldr r8, [r1, #9*4] @ a[9] + umlal r5, r6, r2, r8 @ d += a[8]*2 * a[9] + @ r8 will be used in J + + bic r0, r5, field_not_M @ u7 = d & M + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u7 * R0 + umlal r3, r4, r0, r14 + bic r14, r3, field_not_M @ t7 = c & M + str r14, [sp, #4 + 7*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u7 * R1 + umlal r3, r4, r0, r14 + + /* J */ + adds r3, r3, r11 @ c += c' + adc r4, r4, r12 + umlal r5, r6, r8, r8 @ d += a[9] * a[9] + + bic r0, r5, field_not_M @ u8 = d & M + str r0, [sp, #4 + 8*4] + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + movw r14, field_R0 @ c += u8 * R0 + umlal r3, r4, r0, r14 + + /****************************************** + * compute and write back result + ****************************************** + Allocation: + r0 r + r3:r4 c + r5:r6 d + r7 t0 + r8 t1 + r9 t2 + r11 u8 + r12 t9 + r1,r2,r10,r14 scratch + + Note: do not read from a[] after here, it may overlap with r[] + */ + ldr r0, [sp, #0] + add r1, sp, #4 + 3*4 @ r[3..7] = t3..7, r11=u8, r12=t9 + ldmia r1, {r2,r7,r8,r9,r10,r11,r12} + add r1, r0, #3*4 + stmia r1, {r2,r7,r8,r9,r10} + + bic r2, r3, field_not_M @ r[8] = c & M + str r2, [r0, #8*4] + mov r3, r3, lsr #26 @ c >>= 26 + orr r3, r3, r4, asl #6 + mov r4, r4, lsr #26 + mov r14, field_R1 @ c += u8 * R1 + umlal r3, r4, r11, r14 + movw r14, field_R0 @ c += d * R0 + umlal r3, r4, r5, r14 + adds r3, r3, r12 @ c += t9 + adc r4, r4, #0 + + add r1, sp, #4 + 0*4 @ r7,r8,r9 = t0,t1,t2 + ldmia r1, {r7,r8,r9} + + ubfx r2, r3, #0, #22 @ r[9] = c & (M >> 4) + str r2, [r0, #9*4] + mov r3, r3, lsr #22 @ c >>= 22 + orr r3, r3, r4, asl #10 + mov r4, r4, lsr #22 + movw r14, field_R1 << 4 @ c += d * (R1 << 4) + umlal r3, r4, r5, r14 + + movw r14, field_R0 >> 4 @ d = c * (R0 >> 4) + t0 (64x64 multiply+add) + umull r5, r6, r3, r14 @ d = c.lo * (R0 >> 4) + adds r5, r5, r7 @ d.lo += t0 + mla r6, r14, r4, r6 @ d.hi += c.hi * (R0 >> 4) + adc r6, r6, 0 @ d.hi += carry + + bic r2, r5, field_not_M @ r[0] = d & M + str r2, [r0, #0*4] + + mov r5, r5, lsr #26 @ d >>= 26 + orr r5, r5, r6, asl #6 + mov r6, r6, lsr #26 + + movw r14, field_R1 >> 4 @ d += c * (R1 >> 4) + t1 (64x64 multiply+add) + umull r1, r2, r3, r14 @ tmp = c.lo * (R1 >> 4) + adds r5, r5, r8 @ d.lo += t1 + adc r6, r6, #0 @ d.hi += carry + adds r5, r5, r1 @ d.lo += tmp.lo + mla r2, r14, r4, r2 @ tmp.hi += c.hi * (R1 >> 4) + adc r6, r6, r2 @ d.hi += carry + tmp.hi + + bic r2, r5, field_not_M @ r[1] = d & M + str r2, [r0, #1*4] + mov r5, r5, lsr #26 @ d >>= 26 (ignore hi) + orr r5, r5, r6, asl #6 + + add r5, r5, r9 @ d += t2 + str r5, [r0, #2*4] @ r[2] = d + + add sp, sp, #48 + ldmfd sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc} + .size secp256k1_fe_sqr_inner, .-secp256k1_fe_sqr_inner + diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h new file mode 100644 index 000000000..c4c16eb7c --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/basic-config.h @@ -0,0 +1,32 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BASIC_CONFIG_ +#define _SECP256K1_BASIC_CONFIG_ + +#ifdef USE_BASIC_CONFIG + +#undef USE_ASM_X86_64 +#undef USE_ENDOMORPHISM +#undef USE_FIELD_10X26 +#undef USE_FIELD_5X52 +#undef USE_FIELD_INV_BUILTIN +#undef USE_FIELD_INV_NUM +#undef USE_NUM_GMP +#undef USE_NUM_NONE +#undef USE_SCALAR_4X64 +#undef USE_SCALAR_8X32 +#undef USE_SCALAR_INV_BUILTIN +#undef USE_SCALAR_INV_NUM + +#define USE_NUM_NONE 1 +#define USE_FIELD_INV_BUILTIN 1 +#define USE_SCALAR_INV_BUILTIN 1 +#define USE_FIELD_10X26 1 +#define USE_SCALAR_8X32 1 + +#endif // USE_BASIC_CONFIG +#endif // _SECP256K1_BASIC_CONFIG_ diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h new file mode 100644 index 000000000..3a71b4aaf --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench.h @@ -0,0 +1,66 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_BENCH_H_ +#define _SECP256K1_BENCH_H_ + +#include +#include +#include "sys/time.h" + +static double gettimedouble(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_usec * 0.000001 + tv.tv_sec; +} + +void print_number(double x) { + double y = x; + int c = 0; + if (y < 0.0) { + y = -y; + } + while (y < 100.0) { + y *= 10.0; + c++; + } + printf("%.*f", c, x); +} + +void run_benchmark(char *name, void (*benchmark)(void*), void (*setup)(void*), void (*teardown)(void*), void* data, int count, int iter) { + int i; + double min = HUGE_VAL; + double sum = 0.0; + double max = 0.0; + for (i = 0; i < count; i++) { + double begin, total; + if (setup != NULL) { + setup(data); + } + begin = gettimedouble(); + benchmark(data); + total = gettimedouble() - begin; + if (teardown != NULL) { + teardown(data); + } + if (total < min) { + min = total; + } + if (total > max) { + max = total; + } + sum += total; + } + printf("%s: min ", name); + print_number(min * 1000000.0 / iter); + printf("us / avg "); + print_number((sum / count) * 1000000.0 / iter); + printf("us / max "); + print_number(max * 1000000.0 / iter); + printf("us\n"); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c new file mode 100644 index 000000000..cde5e2dbb --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_ecdh.c @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + secp256k1_pubkey point; + unsigned char scalar[32]; +} bench_ecdh_t; + +static void bench_ecdh_setup(void* arg) { + int i; + bench_ecdh_t *data = (bench_ecdh_t*)arg; + const unsigned char point[] = { + 0x03, + 0x54, 0x94, 0xc1, 0x5d, 0x32, 0x09, 0x97, 0x06, + 0xc2, 0x39, 0x5f, 0x94, 0x34, 0x87, 0x45, 0xfd, + 0x75, 0x7c, 0xe3, 0x0e, 0x4e, 0x8c, 0x90, 0xfb, + 0xa2, 0xba, 0xd1, 0x84, 0xf8, 0x83, 0xc6, 0x9f + }; + + /* create a context with no capabilities */ + data->ctx = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT); + for (i = 0; i < 32; i++) { + data->scalar[i] = i + 1; + } + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &data->point, point, sizeof(point)) == 1); +} + +static void bench_ecdh(void* arg) { + int i; + unsigned char res[32]; + bench_ecdh_t *data = (bench_ecdh_t*)arg; + + for (i = 0; i < 20000; i++) { + CHECK(secp256k1_ecdh(data->ctx, res, &data->point, data->scalar) == 1); + } +} + +int main(void) { + bench_ecdh_t data; + + run_benchmark("ecdh", bench_ecdh, bench_ecdh_setup, NULL, &data, 10, 20000); + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c new file mode 100644 index 000000000..0809f77bd --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_internal.c @@ -0,0 +1,382 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ +#include + +#include "include/secp256k1.h" + +#include "util.h" +#include "hash_impl.h" +#include "num_impl.h" +#include "field_impl.h" +#include "group_impl.h" +#include "scalar_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_impl.h" +#include "bench.h" +#include "secp256k1.c" + +typedef struct { + secp256k1_scalar scalar_x, scalar_y; + secp256k1_fe fe_x, fe_y; + secp256k1_ge ge_x, ge_y; + secp256k1_gej gej_x, gej_y; + unsigned char data[64]; + int wnaf[256]; +} bench_inv_t; + +void bench_setup(void* arg) { + bench_inv_t *data = (bench_inv_t*)arg; + + static const unsigned char init_x[32] = { + 0x02, 0x03, 0x05, 0x07, 0x0b, 0x0d, 0x11, 0x13, + 0x17, 0x1d, 0x1f, 0x25, 0x29, 0x2b, 0x2f, 0x35, + 0x3b, 0x3d, 0x43, 0x47, 0x49, 0x4f, 0x53, 0x59, + 0x61, 0x65, 0x67, 0x6b, 0x6d, 0x71, 0x7f, 0x83 + }; + + static const unsigned char init_y[32] = { + 0x82, 0x83, 0x85, 0x87, 0x8b, 0x8d, 0x81, 0x83, + 0x97, 0xad, 0xaf, 0xb5, 0xb9, 0xbb, 0xbf, 0xc5, + 0xdb, 0xdd, 0xe3, 0xe7, 0xe9, 0xef, 0xf3, 0xf9, + 0x11, 0x15, 0x17, 0x1b, 0x1d, 0xb1, 0xbf, 0xd3 + }; + + secp256k1_scalar_set_b32(&data->scalar_x, init_x, NULL); + secp256k1_scalar_set_b32(&data->scalar_y, init_y, NULL); + secp256k1_fe_set_b32(&data->fe_x, init_x); + secp256k1_fe_set_b32(&data->fe_y, init_y); + CHECK(secp256k1_ge_set_xo_var(&data->ge_x, &data->fe_x, 0)); + CHECK(secp256k1_ge_set_xo_var(&data->ge_y, &data->fe_y, 1)); + secp256k1_gej_set_ge(&data->gej_x, &data->ge_x); + secp256k1_gej_set_ge(&data->gej_y, &data->ge_y); + memcpy(data->data, init_x, 32); + memcpy(data->data + 32, init_y, 32); +} + +void bench_scalar_add(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_scalar_negate(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_scalar_negate(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_sqr(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_sqr(&data->scalar_x, &data->scalar_x); + } +} + +void bench_scalar_mul(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_scalar_mul(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +#ifdef USE_ENDOMORPHISM +void bench_scalar_split(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_scalar l, r; + secp256k1_scalar_split_lambda(&l, &r, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} +#endif + +void bench_scalar_inverse(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000; i++) { + secp256k1_scalar_inverse(&data->scalar_x, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_scalar_inverse_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000; i++) { + secp256k1_scalar_inverse_var(&data->scalar_x, &data->scalar_x); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_field_normalize(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize(&data->fe_x); + } +} + +void bench_field_normalize_weak(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 2000000; i++) { + secp256k1_fe_normalize_weak(&data->fe_x); + } +} + +void bench_field_mul(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_mul(&data->fe_x, &data->fe_x, &data->fe_y); + } +} + +void bench_field_sqr(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_fe_sqr(&data->fe_x, &data->fe_x); + } +} + +void bench_field_inverse(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_inv(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_field_inverse_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_inv_var(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_field_sqrt(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_fe_sqrt(&data->fe_x, &data->fe_x); + secp256k1_fe_add(&data->fe_x, &data->fe_y); + } +} + +void bench_group_double_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_double_var(&data->gej_x, &data->gej_x, NULL); + } +} + +void bench_group_add_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_var(&data->gej_x, &data->gej_x, &data->gej_y, NULL); + } +} + +void bench_group_add_affine(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_ge(&data->gej_x, &data->gej_x, &data->ge_y); + } +} + +void bench_group_add_affine_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 200000; i++) { + secp256k1_gej_add_ge_var(&data->gej_x, &data->gej_x, &data->ge_y, NULL); + } +} + +void bench_group_jacobi_var(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_gej_has_quad_y_var(&data->gej_x); + } +} + +void bench_ecmult_wnaf(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_ecmult_wnaf(data->wnaf, 256, &data->scalar_x, WINDOW_A); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + +void bench_wnaf_const(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_wnaf_const(data->wnaf, data->scalar_x, WINDOW_A); + secp256k1_scalar_add(&data->scalar_x, &data->scalar_x, &data->scalar_y); + } +} + + +void bench_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_sha256_t sha; + + for (i = 0; i < 20000; i++) { + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, data->data, 32); + secp256k1_sha256_finalize(&sha, data->data); + } +} + +void bench_hmac_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_hmac_sha256_t hmac; + + for (i = 0; i < 20000; i++) { + secp256k1_hmac_sha256_initialize(&hmac, data->data, 32); + secp256k1_hmac_sha256_write(&hmac, data->data, 32); + secp256k1_hmac_sha256_finalize(&hmac, data->data); + } +} + +void bench_rfc6979_hmac_sha256(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_rfc6979_hmac_sha256_t rng; + + for (i = 0; i < 20000; i++) { + secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64); + secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32); + } +} + +void bench_context_verify(void* arg) { + int i; + (void)arg; + for (i = 0; i < 20; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_VERIFY)); + } +} + +void bench_context_sign(void* arg) { + int i; + (void)arg; + for (i = 0; i < 200; i++) { + secp256k1_context_destroy(secp256k1_context_create(SECP256K1_CONTEXT_SIGN)); + } +} + +#ifndef USE_NUM_NONE +void bench_num_jacobi(void* arg) { + int i; + bench_inv_t *data = (bench_inv_t*)arg; + secp256k1_num nx, norder; + + secp256k1_scalar_get_num(&nx, &data->scalar_x); + secp256k1_scalar_order_get_num(&norder); + secp256k1_scalar_get_num(&norder, &data->scalar_y); + + for (i = 0; i < 200000; i++) { + secp256k1_num_jacobi(&nx, &norder); + } +} +#endif + +int have_flag(int argc, char** argv, char *flag) { + char** argm = argv + argc; + argv++; + if (argv == argm) { + return 1; + } + while (argv != NULL && argv != argm) { + if (strcmp(*argv, flag) == 0) { + return 1; + } + argv++; + } + return 0; +} + +int main(int argc, char **argv) { + bench_inv_t data; + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "add")) run_benchmark("scalar_add", bench_scalar_add, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "negate")) run_benchmark("scalar_negate", bench_scalar_negate, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "sqr")) run_benchmark("scalar_sqr", bench_scalar_sqr, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "mul")) run_benchmark("scalar_mul", bench_scalar_mul, bench_setup, NULL, &data, 10, 200000); +#ifdef USE_ENDOMORPHISM + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "split")) run_benchmark("scalar_split", bench_scalar_split, bench_setup, NULL, &data, 10, 20000); +#endif + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse", bench_scalar_inverse, bench_setup, NULL, &data, 10, 2000); + if (have_flag(argc, argv, "scalar") || have_flag(argc, argv, "inverse")) run_benchmark("scalar_inverse_var", bench_scalar_inverse_var, bench_setup, NULL, &data, 10, 2000); + + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize", bench_field_normalize, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "normalize")) run_benchmark("field_normalize_weak", bench_field_normalize_weak, bench_setup, NULL, &data, 10, 2000000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqr")) run_benchmark("field_sqr", bench_field_sqr, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "mul")) run_benchmark("field_mul", bench_field_mul, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse", bench_field_inverse, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "inverse")) run_benchmark("field_inverse_var", bench_field_inverse_var, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "field") || have_flag(argc, argv, "sqrt")) run_benchmark("field_sqrt", bench_field_sqrt, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "double")) run_benchmark("group_double_var", bench_group_double_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_var", bench_group_add_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine", bench_group_add_affine, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "add")) run_benchmark("group_add_affine_var", bench_group_add_affine_var, bench_setup, NULL, &data, 10, 200000); + if (have_flag(argc, argv, "group") || have_flag(argc, argv, "jacobi")) run_benchmark("group_jacobi_var", bench_group_jacobi_var, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("wnaf_const", bench_wnaf_const, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "ecmult") || have_flag(argc, argv, "wnaf")) run_benchmark("ecmult_wnaf", bench_ecmult_wnaf, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "sha256")) run_benchmark("hash_sha256", bench_sha256, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "hmac")) run_benchmark("hash_hmac_sha256", bench_hmac_sha256, bench_setup, NULL, &data, 10, 20000); + if (have_flag(argc, argv, "hash") || have_flag(argc, argv, "rng6979")) run_benchmark("hash_rfc6979_hmac_sha256", bench_rfc6979_hmac_sha256, bench_setup, NULL, &data, 10, 20000); + + if (have_flag(argc, argv, "context") || have_flag(argc, argv, "verify")) run_benchmark("context_verify", bench_context_verify, bench_setup, NULL, &data, 10, 20); + if (have_flag(argc, argv, "context") || have_flag(argc, argv, "sign")) run_benchmark("context_sign", bench_context_sign, bench_setup, NULL, &data, 10, 200); + +#ifndef USE_NUM_NONE + if (have_flag(argc, argv, "num") || have_flag(argc, argv, "jacobi")) run_benchmark("num_jacobi", bench_num_jacobi, bench_setup, NULL, &data, 10, 200000); +#endif + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c new file mode 100644 index 000000000..6489378cc --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_recover.c @@ -0,0 +1,60 @@ +/********************************************************************** + * Copyright (c) 2014-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "include/secp256k1_recovery.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char sig[64]; +} bench_recover_t; + +void bench_recover(void* arg) { + int i; + bench_recover_t *data = (bench_recover_t*)arg; + secp256k1_pubkey pubkey; + unsigned char pubkeyc[33]; + + for (i = 0; i < 20000; i++) { + int j; + size_t pubkeylen = 33; + secp256k1_ecdsa_recoverable_signature sig; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(data->ctx, &sig, data->sig, i % 2)); + CHECK(secp256k1_ecdsa_recover(data->ctx, &pubkey, &sig, data->msg)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, pubkeyc, &pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + for (j = 0; j < 32; j++) { + data->sig[j + 32] = data->msg[j]; /* Move former message to S. */ + data->msg[j] = data->sig[j]; /* Move former R to message. */ + data->sig[j] = pubkeyc[j + 1]; /* Move recovered pubkey X coordinate to R (which must be a valid X coordinate). */ + } + } +} + +void bench_recover_setup(void* arg) { + int i; + bench_recover_t *data = (bench_recover_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (i = 0; i < 64; i++) { + data->sig[i] = 65 + i; + } +} + +int main(void) { + bench_recover_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + + run_benchmark("ecdsa_recover", bench_recover, bench_recover_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c new file mode 100644 index 000000000..5f137dda2 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_schnorr_verify.c @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "include/secp256k1_schnorr.h" +#include "util.h" +#include "bench.h" + +typedef struct { + unsigned char key[32]; + unsigned char sig[64]; + unsigned char pubkey[33]; + size_t pubkeylen; +} benchmark_schnorr_sig_t; + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + benchmark_schnorr_sig_t sigs[64]; + int numsigs; +} benchmark_schnorr_verify_t; + +static void benchmark_schnorr_init(void* arg) { + int i, k; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = 1 + i; + } + for (k = 0; k < data->numsigs; k++) { + secp256k1_pubkey pubkey; + for (i = 0; i < 32; i++) { + data->sigs[k].key[i] = 33 + i + k; + } + secp256k1_schnorr_sign(data->ctx, data->sigs[k].sig, data->msg, data->sigs[k].key, NULL, NULL); + data->sigs[k].pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_create(data->ctx, &pubkey, data->sigs[k].key)); + CHECK(secp256k1_ec_pubkey_serialize(data->ctx, data->sigs[k].pubkey, &data->sigs[k].pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED)); + } +} + +static void benchmark_schnorr_verify(void* arg) { + int i; + benchmark_schnorr_verify_t* data = (benchmark_schnorr_verify_t*)arg; + + for (i = 0; i < 20000 / data->numsigs; i++) { + secp256k1_pubkey pubkey; + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->sigs[0].pubkey, data->sigs[0].pubkeylen)); + CHECK(secp256k1_schnorr_verify(data->ctx, data->sigs[0].sig, data->msg, &pubkey) == ((i & 0xFF) == 0)); + data->sigs[0].sig[(i >> 8) % 64] ^= (i & 0xFF); + } +} + + + +int main(void) { + benchmark_schnorr_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + data.numsigs = 1; + run_benchmark("schnorr_verify", benchmark_schnorr_verify, benchmark_schnorr_init, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c new file mode 100644 index 000000000..ed7224d75 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_sign.c @@ -0,0 +1,56 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +typedef struct { + secp256k1_context* ctx; + unsigned char msg[32]; + unsigned char key[32]; +} bench_sign_t; + +static void bench_sign_setup(void* arg) { + int i; + bench_sign_t *data = (bench_sign_t*)arg; + + for (i = 0; i < 32; i++) { + data->msg[i] = i + 1; + } + for (i = 0; i < 32; i++) { + data->key[i] = i + 65; + } +} + +static void bench_sign(void* arg) { + int i; + bench_sign_t *data = (bench_sign_t*)arg; + + unsigned char sig[74]; + for (i = 0; i < 20000; i++) { + size_t siglen = 74; + int j; + secp256k1_ecdsa_signature signature; + CHECK(secp256k1_ecdsa_sign(data->ctx, &signature, data->msg, data->key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data->ctx, sig, &siglen, &signature)); + for (j = 0; j < 32; j++) { + data->msg[j] = sig[j]; + data->key[j] = sig[j + 32]; + } + } +} + +int main(void) { + bench_sign_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + + run_benchmark("ecdsa_sign", bench_sign, bench_sign_setup, NULL, &data, 10, 20000); + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c new file mode 100644 index 000000000..418defa0a --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/bench_verify.c @@ -0,0 +1,112 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include +#include + +#include "include/secp256k1.h" +#include "util.h" +#include "bench.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include +#include +#include +#endif + +typedef struct { + secp256k1_context *ctx; + unsigned char msg[32]; + unsigned char key[32]; + unsigned char sig[72]; + size_t siglen; + unsigned char pubkey[33]; + size_t pubkeylen; +#ifdef ENABLE_OPENSSL_TESTS + EC_GROUP* ec_group; +#endif +} benchmark_verify_t; + +static void benchmark_verify(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + CHECK(secp256k1_ec_pubkey_parse(data->ctx, &pubkey, data->pubkey, data->pubkeylen) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(data->ctx, &sig, data->sig, data->siglen) == 1); + CHECK(secp256k1_ecdsa_verify(data->ctx, &sig, data->msg, &pubkey) == (i == 0)); + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} + +#ifdef ENABLE_OPENSSL_TESTS +static void benchmark_verify_openssl(void* arg) { + int i; + benchmark_verify_t* data = (benchmark_verify_t*)arg; + + for (i = 0; i < 20000; i++) { + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + { + EC_KEY *pkey = EC_KEY_new(); + const unsigned char *pubkey = &data->pubkey[0]; + int result; + + CHECK(pkey != NULL); + result = EC_KEY_set_group(pkey, data->ec_group); + CHECK(result); + result = (o2i_ECPublicKey(&pkey, &pubkey, data->pubkeylen)) != NULL; + CHECK(result); + result = ECDSA_verify(0, &data->msg[0], sizeof(data->msg), &data->sig[0], data->siglen, pkey) == (i == 0); + CHECK(result); + EC_KEY_free(pkey); + } + data->sig[data->siglen - 1] ^= (i & 0xFF); + data->sig[data->siglen - 2] ^= ((i >> 8) & 0xFF); + data->sig[data->siglen - 3] ^= ((i >> 16) & 0xFF); + } +} +#endif + +int main(void) { + int i; + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + benchmark_verify_t data; + + data.ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + for (i = 0; i < 32; i++) { + data.msg[i] = 1 + i; + } + for (i = 0; i < 32; i++) { + data.key[i] = 33 + i; + } + data.siglen = 72; + CHECK(secp256k1_ecdsa_sign(data.ctx, &sig, data.msg, data.key, NULL, NULL)); + CHECK(secp256k1_ecdsa_signature_serialize_der(data.ctx, data.sig, &data.siglen, &sig)); + CHECK(secp256k1_ec_pubkey_create(data.ctx, &pubkey, data.key)); + data.pubkeylen = 33; + CHECK(secp256k1_ec_pubkey_serialize(data.ctx, data.pubkey, &data.pubkeylen, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + + run_benchmark("ecdsa_verify", benchmark_verify, NULL, NULL, &data, 10, 20000); +#ifdef ENABLE_OPENSSL_TESTS + data.ec_group = EC_GROUP_new_by_curve_name(NID_secp256k1); + run_benchmark("ecdsa_verify_openssl", benchmark_verify_openssl, NULL, NULL, &data, 10, 20000); + EC_GROUP_free(data.ec_group); +#endif + + secp256k1_context_destroy(data.ctx); + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h new file mode 100644 index 000000000..54ae101b9 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa.h @@ -0,0 +1,21 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECDSA_ +#define _SECP256K1_ECDSA_ + +#include + +#include "scalar.h" +#include "group.h" +#include "ecmult.h" + +static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *r, secp256k1_scalar *s, const unsigned char *sig, size_t size); +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar *r, const secp256k1_scalar *s); +static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar* r, const secp256k1_scalar* s, const secp256k1_ge *pubkey, const secp256k1_scalar *message); +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h new file mode 100644 index 000000000..453bb1188 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecdsa_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + + +#ifndef _SECP256K1_ECDSA_IMPL_H_ +#define _SECP256K1_ECDSA_IMPL_H_ + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult.h" +#include "ecmult_gen.h" +#include "ecdsa.h" + +/** Group order for secp256k1 defined as 'n' in "Standards for Efficient Cryptography" (SEC2) 2.7.1 + * sage: for t in xrange(1023, -1, -1): + * .. p = 2**256 - 2**32 - t + * .. if p.is_prime(): + * .. print '%x'%p + * .. break + * 'fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f' + * sage: a = 0 + * sage: b = 7 + * sage: F = FiniteField (p) + * sage: '%x' % (EllipticCurve ([F (a), F (b)]).order()) + * 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' + */ +static const secp256k1_fe secp256k1_ecdsa_const_order_as_fe = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0xBAAEDCE6UL, 0xAF48A03BUL, 0xBFD25E8CUL, 0xD0364141UL +); + +/** Difference between field and order, values 'p' and 'n' values defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + * sage: p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F + * sage: a = 0 + * sage: b = 7 + * sage: F = FiniteField (p) + * sage: '%x' % (p - EllipticCurve ([F (a), F (b)]).order()) + * '14551231950b75fc4402da1722fc9baee' + */ +static const secp256k1_fe secp256k1_ecdsa_const_p_minus_order = SECP256K1_FE_CONST( + 0, 0, 0, 1, 0x45512319UL, 0x50B75FC4UL, 0x402DA172UL, 0x2FC9BAEEUL +); + +static int secp256k1_der_read_len(const unsigned char **sigp, const unsigned char *sigend) { + int lenleft, b1; + size_t ret = 0; + if (*sigp >= sigend) { + return -1; + } + b1 = *((*sigp)++); + if (b1 == 0xFF) { + /* X.690-0207 8.1.3.5.c the value 0xFF shall not be used. */ + return -1; + } + if ((b1 & 0x80) == 0) { + /* X.690-0207 8.1.3.4 short form length octets */ + return b1; + } + if (b1 == 0x80) { + /* Indefinite length is not allowed in DER. */ + return -1; + } + /* X.690-207 8.1.3.5 long form length octets */ + lenleft = b1 & 0x7F; + if (lenleft > sigend - *sigp) { + return -1; + } + if (**sigp == 0) { + /* Not the shortest possible length encoding. */ + return -1; + } + if ((size_t)lenleft > sizeof(size_t)) { + /* The resulting length would exceed the range of a size_t, so + * certainly longer than the passed array size. + */ + return -1; + } + while (lenleft > 0) { + if ((ret >> ((sizeof(size_t) - 1) * 8)) != 0) { + } + ret = (ret << 8) | **sigp; + if (ret + lenleft > (size_t)(sigend - *sigp)) { + /* Result exceeds the length of the passed array. */ + return -1; + } + (*sigp)++; + lenleft--; + } + if (ret < 128) { + /* Not the shortest possible length encoding. */ + return -1; + } + return ret; +} + +static int secp256k1_der_parse_integer(secp256k1_scalar *r, const unsigned char **sig, const unsigned char *sigend) { + int overflow = 0; + unsigned char ra[32] = {0}; + int rlen; + + if (*sig == sigend || **sig != 0x02) { + /* Not a primitive integer (X.690-0207 8.3.1). */ + return 0; + } + (*sig)++; + rlen = secp256k1_der_read_len(sig, sigend); + if (rlen <= 0 || (*sig) + rlen > sigend) { + /* Exceeds bounds or not at least length 1 (X.690-0207 8.3.1). */ + return 0; + } + if (**sig == 0x00 && rlen > 1 && (((*sig)[1]) & 0x80) == 0x00) { + /* Excessive 0x00 padding. */ + return 0; + } + if (**sig == 0xFF && rlen > 1 && (((*sig)[1]) & 0x80) == 0x80) { + /* Excessive 0xFF padding. */ + return 0; + } + if ((**sig & 0x80) == 0x80) { + /* Negative. */ + overflow = 1; + } + while (rlen > 0 && **sig == 0) { + /* Skip leading zero bytes */ + rlen--; + (*sig)++; + } + if (rlen > 32) { + overflow = 1; + } + if (!overflow) { + memcpy(ra + 32 - rlen, *sig, rlen); + secp256k1_scalar_set_b32(r, ra, &overflow); + } + if (overflow) { + secp256k1_scalar_set_int(r, 0); + } + (*sig) += rlen; + return 1; +} + +static int secp256k1_ecdsa_sig_parse(secp256k1_scalar *rr, secp256k1_scalar *rs, const unsigned char *sig, size_t size) { + const unsigned char *sigend = sig + size; + int rlen; + if (sig == sigend || *(sig++) != 0x30) { + /* The encoding doesn't start with a constructed sequence (X.690-0207 8.9.1). */ + return 0; + } + rlen = secp256k1_der_read_len(&sig, sigend); + if (rlen < 0 || sig + rlen > sigend) { + /* Tuple exceeds bounds */ + return 0; + } + if (sig + rlen != sigend) { + /* Garbage after tuple. */ + return 0; + } + + if (!secp256k1_der_parse_integer(rr, &sig, sigend)) { + return 0; + } + if (!secp256k1_der_parse_integer(rs, &sig, sigend)) { + return 0; + } + + if (sig != sigend) { + /* Trailing garbage inside tuple. */ + return 0; + } + + return 1; +} + +static int secp256k1_ecdsa_sig_serialize(unsigned char *sig, size_t *size, const secp256k1_scalar* ar, const secp256k1_scalar* as) { + unsigned char r[33] = {0}, s[33] = {0}; + unsigned char *rp = r, *sp = s; + size_t lenR = 33, lenS = 33; + secp256k1_scalar_get_b32(&r[1], ar); + secp256k1_scalar_get_b32(&s[1], as); + while (lenR > 1 && rp[0] == 0 && rp[1] < 0x80) { lenR--; rp++; } + while (lenS > 1 && sp[0] == 0 && sp[1] < 0x80) { lenS--; sp++; } + if (*size < 6+lenS+lenR) { + *size = 6 + lenS + lenR; + return 0; + } + *size = 6 + lenS + lenR; + sig[0] = 0x30; + sig[1] = 4 + lenS + lenR; + sig[2] = 0x02; + sig[3] = lenR; + memcpy(sig+4, rp, lenR); + sig[4+lenR] = 0x02; + sig[5+lenR] = lenS; + memcpy(sig+lenR+6, sp, lenS); + return 1; +} + +static int secp256k1_ecdsa_sig_verify(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar *sigs, const secp256k1_ge *pubkey, const secp256k1_scalar *message) { + unsigned char c[32]; + secp256k1_scalar sn, u1, u2; +#if !defined(EXHAUSTIVE_TEST_ORDER) + secp256k1_fe xr; +#endif + secp256k1_gej pubkeyj; + secp256k1_gej pr; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_inverse_var(&sn, sigs); + secp256k1_scalar_mul(&u1, &sn, message); + secp256k1_scalar_mul(&u2, &sn, sigr); + secp256k1_gej_set_ge(&pubkeyj, pubkey); + secp256k1_ecmult(ctx, &pr, &pubkeyj, &u2, &u1); + if (secp256k1_gej_is_infinity(&pr)) { + return 0; + } + +#if defined(EXHAUSTIVE_TEST_ORDER) +{ + secp256k1_scalar computed_r; + secp256k1_ge pr_ge; + secp256k1_ge_set_gej(&pr_ge, &pr); + secp256k1_fe_normalize(&pr_ge.x); + + secp256k1_fe_get_b32(c, &pr_ge.x); + secp256k1_scalar_set_b32(&computed_r, c, NULL); + return secp256k1_scalar_eq(sigr, &computed_r); +} +#else + secp256k1_scalar_get_b32(c, sigr); + secp256k1_fe_set_b32(&xr, c); + + /** We now have the recomputed R point in pr, and its claimed x coordinate (modulo n) + * in xr. Naively, we would extract the x coordinate from pr (requiring a inversion modulo p), + * compute the remainder modulo n, and compare it to xr. However: + * + * xr == X(pr) mod n + * <=> exists h. (xr + h * n < p && xr + h * n == X(pr)) + * [Since 2 * n > p, h can only be 0 or 1] + * <=> (xr == X(pr)) || (xr + n < p && xr + n == X(pr)) + * [In Jacobian coordinates, X(pr) is pr.x / pr.z^2 mod p] + * <=> (xr == pr.x / pr.z^2 mod p) || (xr + n < p && xr + n == pr.x / pr.z^2 mod p) + * [Multiplying both sides of the equations by pr.z^2 mod p] + * <=> (xr * pr.z^2 mod p == pr.x) || (xr + n < p && (xr + n) * pr.z^2 mod p == pr.x) + * + * Thus, we can avoid the inversion, but we have to check both cases separately. + * secp256k1_gej_eq_x implements the (xr * pr.z^2 mod p == pr.x) test. + */ + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* xr * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + if (secp256k1_fe_cmp_var(&xr, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + /* xr + n >= p, so we can skip testing the second case. */ + return 0; + } + secp256k1_fe_add(&xr, &secp256k1_ecdsa_const_order_as_fe); + if (secp256k1_gej_eq_x_var(&xr, &pr)) { + /* (xr + n) * pr.z^2 mod p == pr.x, so the signature is valid. */ + return 1; + } + return 0; +#endif +} + +static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *seckey, const secp256k1_scalar *message, const secp256k1_scalar *nonce, int *recid) { + unsigned char b[32]; + secp256k1_gej rp; + secp256k1_ge r; + secp256k1_scalar n; + int overflow = 0; + + secp256k1_ecmult_gen(ctx, &rp, nonce); + secp256k1_ge_set_gej(&r, &rp); + secp256k1_fe_normalize(&r.x); + secp256k1_fe_normalize(&r.y); + secp256k1_fe_get_b32(b, &r.x); + secp256k1_scalar_set_b32(sigr, b, &overflow); + /* These two conditions should be checked before calling */ + VERIFY_CHECK(!secp256k1_scalar_is_zero(sigr)); + VERIFY_CHECK(overflow == 0); + + if (recid) { + /* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log + * of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria. + */ + *recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0); + } + secp256k1_scalar_mul(&n, sigr, seckey); + secp256k1_scalar_add(&n, &n, message); + secp256k1_scalar_inverse(sigs, nonce); + secp256k1_scalar_mul(sigs, sigs, &n); + secp256k1_scalar_clear(&n); + secp256k1_gej_clear(&rp); + secp256k1_ge_clear(&r); + if (secp256k1_scalar_is_zero(sigs)) { + return 0; + } + if (secp256k1_scalar_is_high(sigs)) { + secp256k1_scalar_negate(sigs, sigs); + if (recid) { + *recid ^= 1; + } + } + return 1; +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h new file mode 100644 index 000000000..42739a3be --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey.h @@ -0,0 +1,25 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECKEY_ +#define _SECP256K1_ECKEY_ + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size); +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed); + +static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak); +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h new file mode 100644 index 000000000..ce38071ac --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/eckey_impl.h @@ -0,0 +1,99 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECKEY_IMPL_H_ +#define _SECP256K1_ECKEY_IMPL_H_ + +#include "eckey.h" + +#include "scalar.h" +#include "field.h" +#include "group.h" +#include "ecmult_gen.h" + +static int secp256k1_eckey_pubkey_parse(secp256k1_ge *elem, const unsigned char *pub, size_t size) { + if (size == 33 && (pub[0] == 0x02 || pub[0] == 0x03)) { + secp256k1_fe x; + return secp256k1_fe_set_b32(&x, pub+1) && secp256k1_ge_set_xo_var(elem, &x, pub[0] == 0x03); + } else if (size == 65 && (pub[0] == 0x04 || pub[0] == 0x06 || pub[0] == 0x07)) { + secp256k1_fe x, y; + if (!secp256k1_fe_set_b32(&x, pub+1) || !secp256k1_fe_set_b32(&y, pub+33)) { + return 0; + } + secp256k1_ge_set_xy(elem, &x, &y); + if ((pub[0] == 0x06 || pub[0] == 0x07) && secp256k1_fe_is_odd(&y) != (pub[0] == 0x07)) { + return 0; + } + return secp256k1_ge_is_valid_var(elem); + } else { + return 0; + } +} + +static int secp256k1_eckey_pubkey_serialize(secp256k1_ge *elem, unsigned char *pub, size_t *size, int compressed) { + if (secp256k1_ge_is_infinity(elem)) { + return 0; + } + secp256k1_fe_normalize_var(&elem->x); + secp256k1_fe_normalize_var(&elem->y); + secp256k1_fe_get_b32(&pub[1], &elem->x); + if (compressed) { + *size = 33; + pub[0] = 0x02 | (secp256k1_fe_is_odd(&elem->y) ? 0x01 : 0x00); + } else { + *size = 65; + pub[0] = 0x04; + secp256k1_fe_get_b32(&pub[33], &elem->y); + } + return 1; +} + +static int secp256k1_eckey_privkey_tweak_add(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + secp256k1_scalar_add(key, key, tweak); + if (secp256k1_scalar_is_zero(key)) { + return 0; + } + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_gej pt; + secp256k1_scalar one; + secp256k1_gej_set_ge(&pt, key); + secp256k1_scalar_set_int(&one, 1); + secp256k1_ecmult(ctx, &pt, &pt, &one, tweak); + + if (secp256k1_gej_is_infinity(&pt)) { + return 0; + } + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) { + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_mul(key, key, tweak); + return 1; +} + +static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) { + secp256k1_scalar zero; + secp256k1_gej pt; + if (secp256k1_scalar_is_zero(tweak)) { + return 0; + } + + secp256k1_scalar_set_int(&zero, 0); + secp256k1_gej_set_ge(&pt, key); + secp256k1_ecmult(ctx, &pt, &pt, tweak, &zero); + secp256k1_ge_set_gej(key, &pt); + return 1; +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h new file mode 100644 index 000000000..20484134f --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult.h @@ -0,0 +1,31 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_ +#define _SECP256K1_ECMULT_ + +#include "num.h" +#include "group.h" + +typedef struct { + /* For accelerating the computation of a*P + b*G: */ + secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ +#endif +} secp256k1_ecmult_context; + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); + +/** Double multiply: R = na*A + ng*G */ +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h new file mode 100644 index 000000000..2b0097655 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_CONST_ +#define _SECP256K1_ECMULT_CONST_ + +#include "scalar.h" +#include "group.h" + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h new file mode 100644 index 000000000..0db314c48 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_const_impl.h @@ -0,0 +1,239 @@ +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_CONST_IMPL_ +#define _SECP256K1_ECMULT_CONST_IMPL_ + +#include "scalar.h" +#include "group.h" +#include "ecmult_const.h" +#include "ecmult_impl.h" + +#ifdef USE_ENDOMORPHISM + #define WNAF_BITS 128 +#else + #define WNAF_BITS 256 +#endif +#define WNAF_SIZE(w) ((WNAF_BITS + (w) - 1) / (w)) + +/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ +#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ + int m; \ + int abs_n = (n) * (((n) > 0) * 2 - 1); \ + int idx_n = abs_n / 2; \ + secp256k1_fe neg_y; \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ + VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ + for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \ + /* This loop is used to avoid secret data in array indices. See + * the comment in ecmult_gen_impl.h for rationale. */ \ + secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ + secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ + } \ + (r)->infinity = 0; \ + secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ + secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ +} while(0) + + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^{wi} * wnaf[i], i=0..return_val) + * with the following guarantees: + * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) + * - each wnaf[i] is nonzero + * - the number of words set is returned; this is always (WNAF_BITS + w - 1) / w + * + * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar + * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) + * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003 + * + * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 + */ +static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w) { + int global_sign; + int skew = 0; + int word = 0; + + /* 1 2 3 */ + int u_last; + int u; + + int flip; + int bit; + secp256k1_scalar neg_s; + int not_neg_one; + /* Note that we cannot handle even numbers by negating them to be odd, as is + * done in other implementations, since if our scalars were specified to have + * width < 256 for performance reasons, their negations would have width 256 + * and we'd lose any performance benefit. Instead, we use a technique from + * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) + * or 2 (for odd) to the number we are encoding, returning a skew value indicating + * this, and having the caller compensate after doing the multiplication. */ + + /* Negative numbers will be negated to keep their bit representation below the maximum width */ + flip = secp256k1_scalar_is_high(&s); + /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ + bit = flip ^ !secp256k1_scalar_is_even(&s); + /* We check for negative one, since adding 2 to it will cause an overflow */ + secp256k1_scalar_negate(&neg_s, &s); + not_neg_one = !secp256k1_scalar_is_one(&neg_s); + secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); + /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects + * that we added two to it and flipped it. In fact for -1 these operations are + * identical. We only flipped, but since skewing is required (in the sense that + * the skew must be 1 or 2, never zero) and flipping is not, we need to change + * our flags to claim that we only skewed. */ + global_sign = secp256k1_scalar_cond_negate(&s, flip); + global_sign *= not_neg_one * 2 - 1; + skew = 1 << bit; + + /* 4 */ + u_last = secp256k1_scalar_shr_int(&s, w); + while (word * w < WNAF_BITS) { + int sign; + int even; + + /* 4.1 4.4 */ + u = secp256k1_scalar_shr_int(&s, w); + /* 4.2 */ + even = ((u & 1) == 0); + sign = 2 * (u_last > 0) - 1; + u += sign * even; + u_last -= sign * even * (1 << w); + + /* 4.3, adapted for global sign change */ + wnaf[word++] = u_last * global_sign; + + u_last = u; + } + wnaf[word] = u * global_sign; + + VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); + VERIFY_CHECK(word == WNAF_SIZE(w)); + return skew; +} + + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; + + int skew_1; + int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; + int skew_lam; + secp256k1_scalar q_1, q_lam; +#endif + + int i; + secp256k1_scalar sc = *scalar; + + /* build wnaf representation for q. */ +#ifdef USE_ENDOMORPHISM + /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); + skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1); + skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1); +#else + skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1); +#endif + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + */ + secp256k1_gej_set_ge(r, a); + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, r); + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_fe_normalize_weak(&pre_a[i].y); + } +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } +#endif + + /* first loop iteration (separated out so we can directly set r, rather + * than having it start at infinity, get doubled several times, then have + * its new value added to it) */ + i = wnaf_1[WNAF_SIZE(WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); + secp256k1_gej_set_ge(r, &tmpa); +#ifdef USE_ENDOMORPHISM + i = wnaf_lam[WNAF_SIZE(WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); + secp256k1_gej_add_ge(r, r, &tmpa); +#endif + /* remaining loop iterations */ + for (i = WNAF_SIZE(WINDOW_A - 1) - 1; i >= 0; i--) { + int n; + int j; + for (j = 0; j < WINDOW_A - 1; ++j) { + secp256k1_gej_double_nonzero(r, r, NULL); + } + + n = wnaf_1[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#ifdef USE_ENDOMORPHISM + n = wnaf_lam[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#endif + } + + secp256k1_fe_mul(&r->z, &r->z, &Z); + + { + /* Correct for wNAF skew */ + secp256k1_ge correction = *a; + secp256k1_ge_storage correction_1_stor; +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage correction_lam_stor; +#endif + secp256k1_ge_storage a2_stor; + secp256k1_gej tmpj; + secp256k1_gej_set_ge(&tmpj, &correction); + secp256k1_gej_double_var(&tmpj, &tmpj, NULL); + secp256k1_ge_set_gej(&correction, &tmpj); + secp256k1_ge_to_storage(&correction_1_stor, a); +#ifdef USE_ENDOMORPHISM + secp256k1_ge_to_storage(&correction_lam_stor, a); +#endif + secp256k1_ge_to_storage(&a2_stor, &correction); + + /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ + secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); +#endif + + /* Apply the correction */ + secp256k1_ge_from_storage(&correction, &correction_1_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + +#ifdef USE_ENDOMORPHISM + secp256k1_ge_from_storage(&correction, &correction_lam_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_ge_mul_lambda(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); +#endif + } +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h new file mode 100644 index 000000000..eb2cc9ead --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen.h @@ -0,0 +1,43 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_GEN_ +#define _SECP256K1_ECMULT_GEN_ + +#include "scalar.h" +#include "group.h" + +typedef struct { + /* For accelerating the computation of a*G: + * To harden against timing attacks, use the following mechanism: + * * Break up the multiplicand into groups of 4 bits, called n_0, n_1, n_2, ..., n_63. + * * Compute sum(n_i * 16^i * G + U_i, i=0..63), where: + * * U_i = U * 2^i (for i=0..62) + * * U_i = U * (1-2^63) (for i=63) + * where U is a point with no known corresponding scalar. Note that sum(U_i, i=0..63) = 0. + * For each i, and each of the 16 possible values of n_i, (n_i * 16^i * G + U_i) is + * precomputed (call it prec(i, n_i)). The formula now becomes sum(prec(i, n_i), i=0..63). + * None of the resulting prec group elements have a known scalar, and neither do any of + * the intermediate sums while computing a*G. + */ + secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ + secp256k1_scalar blind; + secp256k1_gej initial; +} secp256k1_ecmult_gen_context; + +static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, + const secp256k1_ecmult_gen_context* src, const secp256k1_callback* cb); +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context* ctx); +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx); + +/** Multiply with the generator: R = a*G */ +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context* ctx, secp256k1_gej *r, const secp256k1_scalar *a); + +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h new file mode 100644 index 000000000..35f254607 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_gen_impl.h @@ -0,0 +1,210 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_GEN_IMPL_H_ +#define _SECP256K1_ECMULT_GEN_IMPL_H_ + +#include "scalar.h" +#include "group.h" +#include "ecmult_gen.h" +#include "hash_impl.h" +#ifdef USE_ECMULT_STATIC_PRECOMPUTATION +#include "ecmult_static_context.h" +#endif +static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context *ctx) { + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx, const secp256k1_callback* cb) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_ge prec[1024]; + secp256k1_gej gj; + secp256k1_gej nums_gej; + int i, j; +#endif + + if (ctx->prec != NULL) { + return; + } +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + ctx->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*ctx->prec)); + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + /* Construct a group element with no known corresponding scalar (nothing up my sleeve). */ + { + static const unsigned char nums_b32[33] = "The scalar for this x is unknown"; + secp256k1_fe nums_x; + secp256k1_ge nums_ge; + int r; + r = secp256k1_fe_set_b32(&nums_x, nums_b32); + (void)r; + VERIFY_CHECK(r); + r = secp256k1_ge_set_xo_var(&nums_ge, &nums_x, 0); + (void)r; + VERIFY_CHECK(r); + secp256k1_gej_set_ge(&nums_gej, &nums_ge); + /* Add G to make the bits in x uniformly distributed. */ + secp256k1_gej_add_ge_var(&nums_gej, &nums_gej, &secp256k1_ge_const_g, NULL); + } + + /* compute prec. */ + { + secp256k1_gej precj[1024]; /* Jacobian versions of prec. */ + secp256k1_gej gbase; + secp256k1_gej numsbase; + gbase = gj; /* 16^j * G */ + numsbase = nums_gej; /* 2^j * nums. */ + for (j = 0; j < 64; j++) { + /* Set precj[j*16 .. j*16+15] to (numsbase, numsbase + gbase, ..., numsbase + 15*gbase). */ + precj[j*16] = numsbase; + for (i = 1; i < 16; i++) { + secp256k1_gej_add_var(&precj[j*16 + i], &precj[j*16 + i - 1], &gbase, NULL); + } + /* Multiply gbase by 16. */ + for (i = 0; i < 4; i++) { + secp256k1_gej_double_var(&gbase, &gbase, NULL); + } + /* Multiply numbase by 2. */ + secp256k1_gej_double_var(&numsbase, &numsbase, NULL); + if (j == 62) { + /* In the last iteration, numsbase is (1 - 2^j) * nums instead. */ + secp256k1_gej_neg(&numsbase, &numsbase); + secp256k1_gej_add_var(&numsbase, &numsbase, &nums_gej, NULL); + } + } + secp256k1_ge_set_all_gej_var(prec, precj, 1024, cb); + } + for (j = 0; j < 64; j++) { + for (i = 0; i < 16; i++) { + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); + } + } +#else + (void)cb; + ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; +#endif + secp256k1_ecmult_gen_blind(ctx, NULL); +} + +static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context* ctx) { + return ctx->prec != NULL; +} + +static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst, + const secp256k1_ecmult_gen_context *src, const secp256k1_callback* cb) { + if (src->prec == NULL) { + dst->prec = NULL; + } else { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); + memcpy(dst->prec, src->prec, sizeof(*dst->prec)); +#else + (void)cb; + dst->prec = src->prec; +#endif + dst->initial = src->initial; + dst->blind = src->blind; + } +} + +static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + free(ctx->prec); +#endif + secp256k1_scalar_clear(&ctx->blind); + secp256k1_gej_clear(&ctx->initial); + ctx->prec = NULL; +} + +static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp256k1_gej *r, const secp256k1_scalar *gn) { + secp256k1_ge add; + secp256k1_ge_storage adds; + secp256k1_scalar gnb; + int bits; + int i, j; + memset(&adds, 0, sizeof(adds)); + *r = ctx->initial; + /* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */ + secp256k1_scalar_add(&gnb, gn, &ctx->blind); + add.infinity = 0; + for (j = 0; j < 64; j++) { + bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4); + for (i = 0; i < 16; i++) { + /** This uses a conditional move to avoid any secret data in array indexes. + * _Any_ use of secret indexes has been demonstrated to result in timing + * sidechannels, even when the cache-line access patterns are uniform. + * See also: + * "A word of warning", CHES 2013 Rump Session, by Daniel J. Bernstein and Peter Schwabe + * (https://cryptojedi.org/peter/data/chesrump-20130822.pdf) and + * "Cache Attacks and Countermeasures: the Case of AES", RSA 2006, + * by Dag Arne Osvik, Adi Shamir, and Eran Tromer + * (http://www.tau.ac.il/~tromer/papers/cache.pdf) + */ + secp256k1_ge_storage_cmov(&adds, &(*ctx->prec)[j][i], i == bits); + } + secp256k1_ge_from_storage(&add, &adds); + secp256k1_gej_add_ge(r, r, &add); + } + bits = 0; + secp256k1_ge_clear(&add); + secp256k1_scalar_clear(&gnb); +} + +/* Setup blinding values for secp256k1_ecmult_gen. */ +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { + secp256k1_scalar b; + secp256k1_gej gb; + secp256k1_fe s; + unsigned char nonce32[32]; + secp256k1_rfc6979_hmac_sha256_t rng; + int retry; + unsigned char keydata[64] = {0}; + if (seed32 == NULL) { + /* When seed is NULL, reset the initial point and blinding value. */ + secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); + secp256k1_gej_neg(&ctx->initial, &ctx->initial); + secp256k1_scalar_set_int(&ctx->blind, 1); + } + /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ + secp256k1_scalar_get_b32(nonce32, &ctx->blind); + /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, + * and guards against weak or adversarial seeds. This is a simpler and safer interface than + * asking the caller for blinding values directly and expecting them to retry on failure. + */ + memcpy(keydata, nonce32, 32); + if (seed32 != NULL) { + memcpy(keydata + 32, seed32, 32); + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); + memset(keydata, 0, sizeof(keydata)); + /* Retry for out of range results to achieve uniformity. */ + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + retry = !secp256k1_fe_set_b32(&s, nonce32); + retry |= secp256k1_fe_is_zero(&s); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ + /* Randomize the projection to defend against multiplier sidechannels. */ + secp256k1_gej_rescale(&ctx->initial, &s); + secp256k1_fe_clear(&s); + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + secp256k1_scalar_set_b32(&b, nonce32, &retry); + /* A blinding value of 0 works, but would undermine the projection hardening. */ + retry |= secp256k1_scalar_is_zero(&b); + } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + memset(nonce32, 0, 32); + secp256k1_ecmult_gen(ctx, &gb, &b); + secp256k1_scalar_negate(&b, &b); + ctx->blind = b; + ctx->initial = gb; + secp256k1_scalar_clear(&b); + secp256k1_gej_clear(&gb); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h new file mode 100644 index 000000000..4e40104ad --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/ecmult_impl.h @@ -0,0 +1,406 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_ECMULT_IMPL_H_ +#define _SECP256K1_ECMULT_IMPL_H_ + +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" + +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to lower these values for exhaustive tests because + * the tables cannot have infinities in them (this breaks the + * affine-isomorphism stuff which tracks z-ratios) */ +# if EXHAUSTIVE_TEST_ORDER > 128 +# define WINDOW_A 5 +# define WINDOW_G 8 +# elif EXHAUSTIVE_TEST_ORDER > 8 +# define WINDOW_A 4 +# define WINDOW_G 4 +# else +# define WINDOW_A 2 +# define WINDOW_G 2 +# endif +#else +/* optimal for 128-bit and 256-bit exponents. */ +#define WINDOW_A 5 +/** larger numbers may result in slightly better performance, at the cost of + exponentially larger precomputed tables. */ +#ifdef USE_ENDOMORPHISM +/** Two tables for window size 15: 1.375 MiB. */ +#define WINDOW_G 15 +#else +/** One table for window size 16: 1.375 MiB. */ +#define WINDOW_G 16 +#endif +#endif + +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) + +/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain + * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will + * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. + * Prej's Z values are undefined, except for the last value. + */ +static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { + secp256k1_gej d; + secp256k1_ge a_ge, d_ge; + int i; + + VERIFY_CHECK(!a->infinity); + + secp256k1_gej_double_var(&d, a, NULL); + + /* + * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate + * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. + */ + d_ge.x = d.x; + d_ge.y = d.y; + d_ge.infinity = 0; + + secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); + prej[0].x = a_ge.x; + prej[0].y = a_ge.y; + prej[0].z = a->z; + prej[0].infinity = 0; + + zr[0] = d.z; + for (i = 1; i < n; i++) { + secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + } + + /* + * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only + * the final point's z coordinate is actually used though, so just update that. + */ + secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); +} + +/** Fill a table 'pre' with precomputed odd multiples of a. + * + * There are two versions of this function: + * - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its + * resulting point set to a single constant Z denominator, stores the X and Y + * coordinates as ge_storage points in pre, and stores the global Z in rz. + * It only operates on tables sized for WINDOW_A wnaf multiples. + * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its + * resulting point set to actually affine points, and stores those in pre. + * It operates on tables of any size, but uses heap-allocated temporaries. + * + * To compute a*P + b*G, we compute a table for P using the first function, + * and for G using the second (which requires an inverse, but it only needs to + * happen once). + */ +static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { + secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); +} + +static void secp256k1_ecmult_odd_multiples_table_storage_var(int n, secp256k1_ge_storage *pre, const secp256k1_gej *a, const secp256k1_callback *cb) { + secp256k1_gej *prej = (secp256k1_gej*)checked_malloc(cb, sizeof(secp256k1_gej) * n); + secp256k1_ge *prea = (secp256k1_ge*)checked_malloc(cb, sizeof(secp256k1_ge) * n); + secp256k1_fe *zr = (secp256k1_fe*)checked_malloc(cb, sizeof(secp256k1_fe) * n); + int i; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(n, prej, zr, a); + /* Convert them in batch to affine coordinates. */ + secp256k1_ge_set_table_gej_var(prea, prej, zr, n); + /* Convert them to compact storage form. */ + for (i = 0; i < n; i++) { + secp256k1_ge_to_storage(&pre[i], &prea[i]); + } + + free(prea); + free(prej); + free(zr); +} + +/** The following two macro retrieves a particular odd multiple from a table + * of precomputed multiples. */ +#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + if ((n) > 0) { \ + *(r) = (pre)[((n)-1)/2]; \ + } else { \ + secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \ + } \ +} while(0) + +#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ + VERIFY_CHECK(((n) & 1) == 1); \ + VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ + VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ + if ((n) > 0) { \ + secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ + } else { \ + secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ + secp256k1_ge_neg((r), (r)); \ + } \ +} while(0) + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { + ctx->pre_g = NULL; +#ifdef USE_ENDOMORPHISM + ctx->pre_g_128 = NULL; +#endif +} + +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { + secp256k1_gej gj; + + if (ctx->pre_g != NULL) { + return; + } + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* precompute the tables with odd multiples */ + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj, cb); + +#ifdef USE_ENDOMORPHISM + { + secp256k1_gej g_128j; + int i; + + ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* calculate 2^128*generator */ + g_128j = gj; + for (i = 0; i < 128; i++) { + secp256k1_gej_double_var(&g_128j, &g_128j, NULL); + } + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j, cb); + } +#endif +} + +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { + if (src->pre_g == NULL) { + dst->pre_g = NULL; + } else { + size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g, src->pre_g, size); + } +#ifdef USE_ENDOMORPHISM + if (src->pre_g_128 == NULL) { + dst->pre_g_128 = NULL; + } else { + size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g_128, src->pre_g_128, size); + } +#endif +} + +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) { + return ctx->pre_g != NULL; +} + +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { + free(ctx->pre_g); +#ifdef USE_ENDOMORPHISM + free(ctx->pre_g_128); +#endif + secp256k1_ecmult_context_init(ctx); +} + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), + * with the following guarantees: + * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) + * - two non-zero entries in wnaf are separated by at least w-1 zeroes. + * - the number of set values in wnaf is returned. This number is at most 256, and at most one more + * than the number of bits in the (absolute value) of the input. + */ +static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { + secp256k1_scalar s = *a; + int last_set_bit = -1; + int bit = 0; + int sign = 1; + int carry = 0; + + VERIFY_CHECK(wnaf != NULL); + VERIFY_CHECK(0 <= len && len <= 256); + VERIFY_CHECK(a != NULL); + VERIFY_CHECK(2 <= w && w <= 31); + + memset(wnaf, 0, len * sizeof(wnaf[0])); + + if (secp256k1_scalar_get_bits(&s, 255, 1)) { + secp256k1_scalar_negate(&s, &s); + sign = -1; + } + + while (bit < len) { + int now; + int word; + if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { + bit++; + continue; + } + + now = w; + if (now > len - bit) { + now = len - bit; + } + + word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; + + carry = (word >> (w-1)) & 1; + word -= carry << w; + + wnaf[bit] = sign * word; + last_set_bit = bit; + + bit += now; + } +#ifdef VERIFY + CHECK(carry == 0); + while (bit < 256) { + CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); + } +#endif + return last_set_bit + 1; +} + +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_scalar na_1, na_lam; + /* Splitted G factors. */ + secp256k1_scalar ng_1, ng_128; + int wnaf_na_1[130]; + int wnaf_na_lam[130]; + int bits_na_1; + int bits_na_lam; + int wnaf_ng_1[129]; + int bits_ng_1; + int wnaf_ng_128[129]; + int bits_ng_128; +#else + int wnaf_na[256]; + int bits_na; + int wnaf_ng[256]; + int bits_ng; +#endif + int i; + int bits; + +#ifdef USE_ENDOMORPHISM + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&na_1, &na_lam, na); + + /* build wnaf representation for na_1 and na_lam. */ + bits_na_1 = secp256k1_ecmult_wnaf(wnaf_na_1, 130, &na_1, WINDOW_A); + bits_na_lam = secp256k1_ecmult_wnaf(wnaf_na_lam, 130, &na_lam, WINDOW_A); + VERIFY_CHECK(bits_na_1 <= 130); + VERIFY_CHECK(bits_na_lam <= 130); + bits = bits_na_1; + if (bits_na_lam > bits) { + bits = bits_na_lam; + } +#else + /* build wnaf representation for na. */ + bits_na = secp256k1_ecmult_wnaf(wnaf_na, 256, na, WINDOW_A); + bits = bits_na; +#endif + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + * The exception is the precomputed G table points, which are actually + * affine. Compared to the base used for other points, they have a Z ratio + * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same + * isomorphism to efficiently add with a known Z inverse. + */ + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, a); + +#ifdef USE_ENDOMORPHISM + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } + + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) { + bits = bits_ng_1; + } + if (bits_ng_128 > bits) { + bits = bits_ng_128; + } +#else + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); + if (bits_ng > bits) { + bits = bits_ng; + } +#endif + + secp256k1_gej_set_infinity(r); + + for (i = bits - 1; i >= 0; i--) { + int n; + secp256k1_gej_double_var(r, r, NULL); +#ifdef USE_ENDOMORPHISM + if (i < bits_na_1 && (n = wnaf_na_1[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_na_lam && (n = wnaf_na_lam[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } + if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#else + if (i < bits_na && (n = wnaf_na[i])) { + ECMULT_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < bits_ng && (n = wnaf_ng[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#endif + } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field.h new file mode 100644 index 000000000..bbb1ee866 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field.h @@ -0,0 +1,132 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_ +#define _SECP256K1_FIELD_ + +/** Field element module. + * + * Field elements can be represented in several ways, but code accessing + * it (and implementations) need to take certain properties into account: + * - Each field element can be normalized or not. + * - Each field element has a magnitude, which represents how far away + * its representation is away from normalization. Normalized elements + * always have a magnitude of 1, but a magnitude of 1 doesn't imply + * normality. + */ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_FIELD_10X26) +#include "field_10x26.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52.h" +#else +#error "Please select field implementation" +#endif + +#include "util.h" + +/** Normalize a field element. */ +static void secp256k1_fe_normalize(secp256k1_fe *r); + +/** Weakly normalize a field element: reduce it magnitude to 1, but don't fully normalize. */ +static void secp256k1_fe_normalize_weak(secp256k1_fe *r); + +/** Normalize a field element, without constant-time guarantee. */ +static void secp256k1_fe_normalize_var(secp256k1_fe *r); + +/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field + * implementation may optionally normalize the input, but this should not be relied upon. */ +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r); + +/** Verify whether a field element represents zero i.e. would normalize to a zero value. The field + * implementation may optionally normalize the input, but this should not be relied upon. */ +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r); + +/** Set a field element equal to a small integer. Resulting field element is normalized. */ +static void secp256k1_fe_set_int(secp256k1_fe *r, int a); + +/** Sets a field element equal to zero, initializing all fields. */ +static void secp256k1_fe_clear(secp256k1_fe *a); + +/** Verify whether a field element is zero. Requires the input to be normalized. */ +static int secp256k1_fe_is_zero(const secp256k1_fe *a); + +/** Check the "oddness" of a field element. Requires the input to be normalized. */ +static int secp256k1_fe_is_odd(const secp256k1_fe *a); + +/** Compare two field elements. Requires magnitude-1 inputs. */ +static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Same as secp256k1_fe_equal, but may be variable time. */ +static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Compare two field elements. Requires both inputs to be normalized */ +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b); + +/** Set a field element equal to 32-byte big endian value. If successful, the resulting field element is normalized. */ +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a); + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a); + +/** Set a field element equal to the additive inverse of another. Takes a maximum magnitude of the input + * as an argument. The magnitude of the output is one higher. */ +static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m); + +/** Multiplies the passed field element with a small integer constant. Multiplies the magnitude by that + * small integer. */ +static void secp256k1_fe_mul_int(secp256k1_fe *r, int a); + +/** Adds a field element to another. The result has the sum of the inputs' magnitudes as magnitude. */ +static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a); + +/** Sets a field element to be the product of two others. Requires the inputs' magnitudes to be at most 8. + * The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b); + +/** Sets a field element to be the square of another. Requires the input's magnitude to be at most 8. + * The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a); + +/** If a has a square root, it is computed in r and 1 is returned. If a does not + * have a square root, the root of its negation is computed and 0 is returned. + * The input's magnitude can be at most 8. The output magnitude is 1 (but not + * guaranteed to be normalized). The result in r will always be a square + * itself. */ +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a); + +/** Checks whether a field element is a quadratic residue. */ +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a); + +/** Sets a field element to be the (modular) inverse of another. Requires the input's magnitude to be + * at most 8. The output magnitude is 1 (but not guaranteed to be normalized). */ +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a); + +/** Potentially faster version of secp256k1_fe_inv, without constant-time guarantee. */ +static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a); + +/** Calculate the (modular) inverses of a batch of field elements. Requires the inputs' magnitudes to be + * at most 8. The output magnitudes are 1 (but not guaranteed to be normalized). The inputs and + * outputs must not overlap in memory. */ +static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len); + +/** Convert a field element to the storage type. */ +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a); + +/** Convert a field element back from the storage type. */ +static void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h new file mode 100644 index 000000000..61ee1e096 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_ +#define _SECP256K1_FIELD_REPR_ + +#include + +typedef struct { + /* X = sum(i=0..9, elem[i]*2^26) mod n */ + uint32_t n[10]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) & 0x3FFFFFFUL, \ + (((uint32_t)d0) >> 26) | (((uint32_t)(d1) & 0xFFFFFUL) << 6), \ + (((uint32_t)d1) >> 20) | (((uint32_t)(d2) & 0x3FFFUL) << 12), \ + (((uint32_t)d2) >> 14) | (((uint32_t)(d3) & 0xFFUL) << 18), \ + (((uint32_t)d3) >> 8) | (((uint32_t)(d4) & 0x3UL) << 24), \ + (((uint32_t)d4) >> 2) & 0x3FFFFFFUL, \ + (((uint32_t)d4) >> 28) | (((uint32_t)(d5) & 0x3FFFFFUL) << 4), \ + (((uint32_t)d5) >> 22) | (((uint32_t)(d6) & 0xFFFFUL) << 10), \ + (((uint32_t)d6) >> 16) | (((uint32_t)(d7) & 0x3FFUL) << 16), \ + (((uint32_t)d7) >> 10) \ +} + +#ifdef VERIFY +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} +#else +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} +#endif + +typedef struct { + uint32_t n[8]; +} secp256k1_fe_storage; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ (d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7) }} +#define SECP256K1_FE_STORAGE_CONST_GET(d) d.n[7], d.n[6], d.n[5], d.n[4],d.n[3], d.n[2], d.n[1], d.n[0] +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h new file mode 100644 index 000000000..5fb092f1b --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_10x26_impl.h @@ -0,0 +1,1140 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ +#define _SECP256K1_FIELD_REPR_IMPL_H_ + +#include "util.h" +#include "num.h" +#include "field.h" + +#ifdef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { + const uint32_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + r &= (d[0] <= 0x3FFFFFFUL * m); + r &= (d[1] <= 0x3FFFFFFUL * m); + r &= (d[2] <= 0x3FFFFFFUL * m); + r &= (d[3] <= 0x3FFFFFFUL * m); + r &= (d[4] <= 0x3FFFFFFUL * m); + r &= (d[5] <= 0x3FFFFFFUL * m); + r &= (d[6] <= 0x3FFFFFFUL * m); + r &= (d[7] <= 0x3FFFFFFUL * m); + r &= (d[8] <= 0x3FFFFFFUL * m); + r &= (d[9] <= 0x03FFFFFUL * m); + r &= (a->magnitude >= 0); + r &= (a->magnitude <= 32); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[9] == 0x03FFFFFUL)) { + uint32_t mid = d[8] & d[7] & d[6] & d[5] & d[4] & d[3] & d[2]; + if (mid == 0x3FFFFFFUL) { + r &= ((d[1] + 0x40UL + ((d[0] + 0x3D1UL) >> 26)) <= 0x3FFFFFFUL); + } + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_normalize(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t m; + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; m = t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; m &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; m &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; m &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; m &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; m &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; m &= t8; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t9 >> 22) | ((t9 == 0x03FFFFFUL) & (m == 0x3FFFFFFUL) + & ((t1 + 0x40UL + ((t0 + 0x3D1UL) >> 26)) > 0x3FFFFFFUL)); + + if (x) { + t0 += 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; + + /* If t9 didn't carry to bit 22 already, then it should have after any final reduction */ + VERIFY_CHECK(t9 >> 22 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t9 &= 0x03FFFFFUL; + } + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + r->n[5] = t5; r->n[6] = t6; r->n[7] = t7; r->n[8] = t8; r->n[9] = t9; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { + uint32_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4], + t5 = r->n[5], t6 = r->n[6], t7 = r->n[7], t8 = r->n[8], t9 = r->n[9]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint32_t z0, z1; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + uint32_t x = t9 >> 22; t9 &= 0x03FFFFFUL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; t1 += (x << 6); + t1 += (t0 >> 26); t0 &= 0x3FFFFFFUL; z0 = t0; z1 = t0 ^ 0x3D0UL; + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t z0, z1; + uint32_t x; + + t0 = r->n[0]; + t9 = r->n[9]; + + /* Reduce t9 at the start so there will be at most a single carry from the first pass */ + x = t9 >> 22; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x3D1UL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0x3FFFFFFUL; + z1 = z0 ^ 0x3D0UL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0UL) & (z1 != 0x3FFFFFFUL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + t4 = r->n[4]; + t5 = r->n[5]; + t6 = r->n[6]; + t7 = r->n[7]; + t8 = r->n[8]; + + t9 &= 0x03FFFFFUL; + t1 += (x << 6); + + t1 += (t0 >> 26); + t2 += (t1 >> 26); t1 &= 0x3FFFFFFUL; z0 |= t1; z1 &= t1 ^ 0x40UL; + t3 += (t2 >> 26); t2 &= 0x3FFFFFFUL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 26); t3 &= 0x3FFFFFFUL; z0 |= t3; z1 &= t3; + t5 += (t4 >> 26); t4 &= 0x3FFFFFFUL; z0 |= t4; z1 &= t4; + t6 += (t5 >> 26); t5 &= 0x3FFFFFFUL; z0 |= t5; z1 &= t5; + t7 += (t6 >> 26); t6 &= 0x3FFFFFFUL; z0 |= t6; z1 &= t6; + t8 += (t7 >> 26); t7 &= 0x3FFFFFFUL; z0 |= t7; z1 &= t7; + t9 += (t8 >> 26); t8 &= 0x3FFFFFFUL; z0 |= t8; z1 &= t8; + z0 |= t9; z1 &= t9 ^ 0x3C00000UL; + + /* ... except for a possible carry at bit 22 of t9 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t9 >> 23 == 0); + + return (z0 == 0) | (z1 == 0x3FFFFFFUL); +} + +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + r->n[0] = a; + r->n[1] = r->n[2] = r->n[3] = r->n[4] = r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + const uint32_t *t = a->n; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return (t[0] | t[1] | t[2] | t[3] | t[4] | t[5] | t[6] | t[7] | t[8] | t[9]) == 0; +} + +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + int i; +#ifdef VERIFY + a->magnitude = 0; + a->normalized = 1; +#endif + for (i=0; i<10; i++) { + a->n[i] = 0; + } +} + +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); +#endif + for (i = 9; i >= 0; i--) { + if (a->n[i] > b->n[i]) { + return 1; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + int i; + r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; + r->n[5] = r->n[6] = r->n[7] = r->n[8] = r->n[9] = 0; + for (i=0; i<32; i++) { + int j; + for (j=0; j<4; j++) { + int limb = (8*i+2*j)/26; + int shift = (8*i+2*j)%26; + r->n[limb] |= (uint32_t)((a[31-i] >> (2*j)) & 0x3) << shift; + } + } + if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) { + return 0; + } +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif + return 1; +} + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + for (i=0; i<32; i++) { + int j; + int c = 0; + for (j=0; j<4; j++) { + int limb = (8*i+2*j)/26; + int shift = (8*i+2*j)%26; + c |= ((a->n[limb] >> shift) & 0x3) << (2 * j); + } + r[31-i] = c; + } +} + +SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_verify(a); +#endif + r->n[0] = 0x3FFFC2FUL * 2 * (m + 1) - a->n[0]; + r->n[1] = 0x3FFFFBFUL * 2 * (m + 1) - a->n[1]; + r->n[2] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[2]; + r->n[3] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[4]; + r->n[5] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[5]; + r->n[6] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[6]; + r->n[7] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[7]; + r->n[8] = 0x3FFFFFFUL * 2 * (m + 1) - a->n[8]; + r->n[9] = 0x03FFFFFUL * 2 * (m + 1) - a->n[9]; +#ifdef VERIFY + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; + r->n[5] *= a; + r->n[6] *= a; + r->n[7] *= a; + r->n[8] *= a; + r->n[9] *= a; +#ifdef VERIFY + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + secp256k1_fe_verify(a); +#endif + r->n[0] += a->n[0]; + r->n[1] += a->n[1]; + r->n[2] += a->n[2]; + r->n[3] += a->n[3]; + r->n[4] += a->n[4]; + r->n[5] += a->n[5]; + r->n[6] += a->n[6]; + r->n[7] += a->n[7]; + r->n[8] += a->n[8]; + r->n[9] += a->n[9]; +#ifdef VERIFY + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +#if defined(USE_EXTERNAL_ASM) + +/* External assembler implementation */ +void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b); +void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a); + +#else + +#ifdef VERIFY +#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) +#else +#define VERIFY_BITS(x, n) do { } while(0) +#endif + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint32_t *r, const uint32_t *a, const uint32_t * SECP256K1_RESTRICT b) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t1, t0, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + VERIFY_BITS(b[0], 30); + VERIFY_BITS(b[1], 30); + VERIFY_BITS(b[2], 30); + VERIFY_BITS(b[3], 30); + VERIFY_BITS(b[4], 30); + VERIFY_BITS(b[5], 30); + VERIFY_BITS(b[6], 30); + VERIFY_BITS(b[7], 30); + VERIFY_BITS(b[8], 30); + VERIFY_BITS(b[9], 26); + + /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. + * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)a[0] * b[9] + + (uint64_t)a[1] * b[8] + + (uint64_t)a[2] * b[7] + + (uint64_t)a[3] * b[6] + + (uint64_t)a[4] * b[5] + + (uint64_t)a[5] * b[4] + + (uint64_t)a[6] * b[3] + + (uint64_t)a[7] * b[2] + + (uint64_t)a[8] * b[1] + + (uint64_t)a[9] * b[0]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * b[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)a[1] * b[9] + + (uint64_t)a[2] * b[8] + + (uint64_t)a[3] * b[7] + + (uint64_t)a[4] * b[6] + + (uint64_t)a[5] * b[5] + + (uint64_t)a[6] * b[4] + + (uint64_t)a[7] * b[3] + + (uint64_t)a[8] * b[2] + + (uint64_t)a[9] * b[1]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)a[0] * b[1] + + (uint64_t)a[1] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)a[2] * b[9] + + (uint64_t)a[3] * b[8] + + (uint64_t)a[4] * b[7] + + (uint64_t)a[5] * b[6] + + (uint64_t)a[6] * b[5] + + (uint64_t)a[7] * b[4] + + (uint64_t)a[8] * b[3] + + (uint64_t)a[9] * b[2]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)a[0] * b[2] + + (uint64_t)a[1] * b[1] + + (uint64_t)a[2] * b[0]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)a[3] * b[9] + + (uint64_t)a[4] * b[8] + + (uint64_t)a[5] * b[7] + + (uint64_t)a[6] * b[6] + + (uint64_t)a[7] * b[5] + + (uint64_t)a[8] * b[4] + + (uint64_t)a[9] * b[3]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[3] + + (uint64_t)a[1] * b[2] + + (uint64_t)a[2] * b[1] + + (uint64_t)a[3] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)a[4] * b[9] + + (uint64_t)a[5] * b[8] + + (uint64_t)a[6] * b[7] + + (uint64_t)a[7] * b[6] + + (uint64_t)a[8] * b[5] + + (uint64_t)a[9] * b[4]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[4] + + (uint64_t)a[1] * b[3] + + (uint64_t)a[2] * b[2] + + (uint64_t)a[3] * b[1] + + (uint64_t)a[4] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[5] * b[9] + + (uint64_t)a[6] * b[8] + + (uint64_t)a[7] * b[7] + + (uint64_t)a[8] * b[6] + + (uint64_t)a[9] * b[5]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[5] + + (uint64_t)a[1] * b[4] + + (uint64_t)a[2] * b[3] + + (uint64_t)a[3] * b[2] + + (uint64_t)a[4] * b[1] + + (uint64_t)a[5] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[6] * b[9] + + (uint64_t)a[7] * b[8] + + (uint64_t)a[8] * b[7] + + (uint64_t)a[9] * b[6]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[6] + + (uint64_t)a[1] * b[5] + + (uint64_t)a[2] * b[4] + + (uint64_t)a[3] * b[3] + + (uint64_t)a[4] * b[2] + + (uint64_t)a[5] * b[1] + + (uint64_t)a[6] * b[0]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[7] * b[9] + + (uint64_t)a[8] * b[8] + + (uint64_t)a[9] * b[7]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[7] + + (uint64_t)a[1] * b[6] + + (uint64_t)a[2] * b[5] + + (uint64_t)a[3] * b[4] + + (uint64_t)a[4] * b[3] + + (uint64_t)a[5] * b[2] + + (uint64_t)a[6] * b[1] + + (uint64_t)a[7] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[8] * b[9] + + (uint64_t)a[9] * b[8]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)a[0] * b[8] + + (uint64_t)a[1] * b[7] + + (uint64_t)a[2] * b[6] + + (uint64_t)a[3] * b[5] + + (uint64_t)a[4] * b[4] + + (uint64_t)a[5] * b[3] + + (uint64_t)a[6] * b[2] + + (uint64_t)a[7] * b[1] + + (uint64_t)a[8] * b[0]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * b[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint32_t *r, const uint32_t *a) { + uint64_t c, d; + uint64_t u0, u1, u2, u3, u4, u5, u6, u7, u8; + uint32_t t9, t0, t1, t2, t3, t4, t5, t6, t7; + const uint32_t M = 0x3FFFFFFUL, R0 = 0x3D10UL, R1 = 0x400UL; + + VERIFY_BITS(a[0], 30); + VERIFY_BITS(a[1], 30); + VERIFY_BITS(a[2], 30); + VERIFY_BITS(a[3], 30); + VERIFY_BITS(a[4], 30); + VERIFY_BITS(a[5], 30); + VERIFY_BITS(a[6], 30); + VERIFY_BITS(a[7], 30); + VERIFY_BITS(a[8], 30); + VERIFY_BITS(a[9], 26); + + /** [... a b c] is a shorthand for ... + a<<52 + b<<26 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0 0 0 0 0 0] = [x*R1 x*R0]. + */ + + d = (uint64_t)(a[0]*2) * a[9] + + (uint64_t)(a[1]*2) * a[8] + + (uint64_t)(a[2]*2) * a[7] + + (uint64_t)(a[3]*2) * a[6] + + (uint64_t)(a[4]*2) * a[5]; + /* VERIFY_BITS(d, 64); */ + /* [d 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + t9 = d & M; d >>= 26; + VERIFY_BITS(t9, 26); + VERIFY_BITS(d, 38); + /* [d t9 0 0 0 0 0 0 0 0 0] = [p9 0 0 0 0 0 0 0 0 0] */ + + c = (uint64_t)a[0] * a[0]; + VERIFY_BITS(c, 60); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p9 0 0 0 0 0 0 0 0 p0] */ + d += (uint64_t)(a[1]*2) * a[9] + + (uint64_t)(a[2]*2) * a[8] + + (uint64_t)(a[3]*2) * a[7] + + (uint64_t)(a[4]*2) * a[6] + + (uint64_t)a[5] * a[5]; + VERIFY_BITS(d, 63); + /* [d t9 0 0 0 0 0 0 0 0 c] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + u0 = d & M; d >>= 26; c += u0 * R0; + VERIFY_BITS(u0, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 61); + /* [d u0 t9 0 0 0 0 0 0 0 0 c-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + t0 = c & M; c >>= 26; c += u0 * R1; + VERIFY_BITS(t0, 26); + VERIFY_BITS(c, 37); + /* [d u0 t9 0 0 0 0 0 0 0 c-u0*R1 t0-u0*R0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 0 p0] */ + + c += (uint64_t)(a[0]*2) * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p10 p9 0 0 0 0 0 0 0 p1 p0] */ + d += (uint64_t)(a[2]*2) * a[9] + + (uint64_t)(a[3]*2) * a[8] + + (uint64_t)(a[4]*2) * a[7] + + (uint64_t)(a[5]*2) * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 t9 0 0 0 0 0 0 0 c t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + u1 = d & M; d >>= 26; c += u1 * R0; + VERIFY_BITS(u1, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u1 0 t9 0 0 0 0 0 0 0 c-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + t1 = c & M; c >>= 26; c += u1 * R1; + VERIFY_BITS(t1, 26); + VERIFY_BITS(c, 38); + /* [d u1 0 t9 0 0 0 0 0 0 c-u1*R1 t1-u1*R0 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 0 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[2] + + (uint64_t)a[1] * a[1]; + VERIFY_BITS(c, 62); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + d += (uint64_t)(a[3]*2) * a[9] + + (uint64_t)(a[4]*2) * a[8] + + (uint64_t)(a[5]*2) * a[7] + + (uint64_t)a[6] * a[6]; + VERIFY_BITS(d, 63); + /* [d 0 0 t9 0 0 0 0 0 0 c t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + u2 = d & M; d >>= 26; c += u2 * R0; + VERIFY_BITS(u2, 26); + VERIFY_BITS(d, 37); + VERIFY_BITS(c, 63); + /* [d u2 0 0 t9 0 0 0 0 0 0 c-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + t2 = c & M; c >>= 26; c += u2 * R1; + VERIFY_BITS(t2, 26); + VERIFY_BITS(c, 38); + /* [d u2 0 0 t9 0 0 0 0 0 c-u2*R1 t2-u2*R0 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 0 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[3] + + (uint64_t)(a[1]*2) * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + d += (uint64_t)(a[4]*2) * a[9] + + (uint64_t)(a[5]*2) * a[8] + + (uint64_t)(a[6]*2) * a[7]; + VERIFY_BITS(d, 63); + /* [d 0 0 0 t9 0 0 0 0 0 c t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + u3 = d & M; d >>= 26; c += u3 * R0; + VERIFY_BITS(u3, 26); + VERIFY_BITS(d, 37); + /* VERIFY_BITS(c, 64); */ + /* [d u3 0 0 0 t9 0 0 0 0 0 c-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + t3 = c & M; c >>= 26; c += u3 * R1; + VERIFY_BITS(t3, 26); + VERIFY_BITS(c, 39); + /* [d u3 0 0 0 t9 0 0 0 0 c-u3*R1 t3-u3*R0 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 0 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[4] + + (uint64_t)(a[1]*2) * a[3] + + (uint64_t)a[2] * a[2]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[5]*2) * a[9] + + (uint64_t)(a[6]*2) * a[8] + + (uint64_t)a[7] * a[7]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 t9 0 0 0 0 c t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + u4 = d & M; d >>= 26; c += u4 * R0; + VERIFY_BITS(u4, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u4 0 0 0 0 t9 0 0 0 0 c-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + t4 = c & M; c >>= 26; c += u4 * R1; + VERIFY_BITS(t4, 26); + VERIFY_BITS(c, 39); + /* [d u4 0 0 0 0 t9 0 0 0 c-u4*R1 t4-u4*R0 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 0 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[5] + + (uint64_t)(a[1]*2) * a[4] + + (uint64_t)(a[2]*2) * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[6]*2) * a[9] + + (uint64_t)(a[7]*2) * a[8]; + VERIFY_BITS(d, 62); + /* [d 0 0 0 0 0 t9 0 0 0 c t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + u5 = d & M; d >>= 26; c += u5 * R0; + VERIFY_BITS(u5, 26); + VERIFY_BITS(d, 36); + /* VERIFY_BITS(c, 64); */ + /* [d u5 0 0 0 0 0 t9 0 0 0 c-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + t5 = c & M; c >>= 26; c += u5 * R1; + VERIFY_BITS(t5, 26); + VERIFY_BITS(c, 39); + /* [d u5 0 0 0 0 0 t9 0 0 c-u5*R1 t5-u5*R0 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 0 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[6] + + (uint64_t)(a[1]*2) * a[5] + + (uint64_t)(a[2]*2) * a[4] + + (uint64_t)a[3] * a[3]; + VERIFY_BITS(c, 63); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[7]*2) * a[9] + + (uint64_t)a[8] * a[8]; + VERIFY_BITS(d, 61); + /* [d 0 0 0 0 0 0 t9 0 0 c t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + u6 = d & M; d >>= 26; c += u6 * R0; + VERIFY_BITS(u6, 26); + VERIFY_BITS(d, 35); + /* VERIFY_BITS(c, 64); */ + /* [d u6 0 0 0 0 0 0 t9 0 0 c-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + t6 = c & M; c >>= 26; c += u6 * R1; + VERIFY_BITS(t6, 26); + VERIFY_BITS(c, 39); + /* [d u6 0 0 0 0 0 0 t9 0 c-u6*R1 t6-u6*R0 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 0 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[7] + + (uint64_t)(a[1]*2) * a[6] + + (uint64_t)(a[2]*2) * a[5] + + (uint64_t)(a[3]*2) * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x8000007C00000007ULL); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)(a[8]*2) * a[9]; + VERIFY_BITS(d, 58); + /* [d 0 0 0 0 0 0 0 t9 0 c t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + u7 = d & M; d >>= 26; c += u7 * R0; + VERIFY_BITS(u7, 26); + VERIFY_BITS(d, 32); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x800001703FFFC2F7ULL); + /* [d u7 0 0 0 0 0 0 0 t9 0 c-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + t7 = c & M; c >>= 26; c += u7 * R1; + VERIFY_BITS(t7, 26); + VERIFY_BITS(c, 38); + /* [d u7 0 0 0 0 0 0 0 t9 c-u7*R1 t7-u7*R0 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 0 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += (uint64_t)(a[0]*2) * a[8] + + (uint64_t)(a[1]*2) * a[7] + + (uint64_t)(a[2]*2) * a[6] + + (uint64_t)(a[3]*2) * a[5] + + (uint64_t)a[4] * a[4]; + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000007B80000008ULL); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint64_t)a[9] * a[9]; + VERIFY_BITS(d, 57); + /* [d 0 0 0 0 0 0 0 0 t9 c t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + u8 = d & M; d >>= 26; c += u8 * R0; + VERIFY_BITS(u8, 26); + VERIFY_BITS(d, 31); + /* VERIFY_BITS(c, 64); */ + VERIFY_CHECK(c <= 0x9000016FBFFFC2F8ULL); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 t3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[3] = t3; + VERIFY_BITS(r[3], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 t4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = t4; + VERIFY_BITS(r[4], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 t5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[5] = t5; + VERIFY_BITS(r[5], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 t6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[6] = t6; + VERIFY_BITS(r[6], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 t7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[7] = t7; + VERIFY_BITS(r[7], 26); + /* [d u8 0 0 0 0 0 0 0 0 t9 c-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + r[8] = c & M; c >>= 26; c += u8 * R1; + VERIFY_BITS(r[8], 26); + VERIFY_BITS(c, 39); + /* [d u8 0 0 0 0 0 0 0 0 t9+c-u8*R1 r8-u8*R0 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 0 0 t9+c r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R0 + t9; + VERIFY_BITS(c, 45); + /* [d 0 0 0 0 0 0 0 0 0 c-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[9] = c & (M >> 4); c >>= 22; c += d * (R1 << 4); + VERIFY_BITS(r[9], 22); + VERIFY_BITS(c, 46); + /* [d 0 0 0 0 0 0 0 0 r9+((c-d*R1<<4)<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [d 0 0 0 0 0 0 0 -d*R1 r9+(c<<22)-d*R0 r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 t0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + d = c * (R0 >> 4) + t0; + VERIFY_BITS(d, 56); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1 d-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[0] = d & M; d >>= 26; + VERIFY_BITS(r[0], 26); + VERIFY_BITS(d, 30); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 t1+d r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += c * (R1 >> 4) + t1; + VERIFY_BITS(d, 53); + VERIFY_CHECK(d <= 0x10000003FFFFBFULL); + /* [r9+(c<<22) r8 r7 r6 r5 r4 r3 t2 d-c*R1>>4 r0-c*R0>>4] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + /* [r9 r8 r7 r6 r5 r4 r3 t2 d r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[1] = d & M; d >>= 26; + VERIFY_BITS(r[1], 26); + VERIFY_BITS(d, 27); + VERIFY_CHECK(d <= 0x4000000ULL); + /* [r9 r8 r7 r6 r5 r4 r3 t2+d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + d += t2; + VERIFY_BITS(d, 27); + /* [r9 r8 r7 r6 r5 r4 r3 d r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = d; + VERIFY_BITS(r[2], 27); + /* [r9 r8 r7 r6 r5 r4 r3 r2 r1 r0] = [p18 p17 p16 p15 p14 p13 p12 p11 p10 p9 p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} +#endif + +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(r != b); +#endif + secp256k1_fe_mul_inner(r->n, a->n, b->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_verify(a); +#endif + secp256k1_fe_sqr_inner(r->n, a->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((uint32_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); + r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1); + r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1); +#ifdef VERIFY + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint32_t mask0, mask1; + mask0 = flag + ~((uint32_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); + r->n[5] = (r->n[5] & mask0) | (a->n[5] & mask1); + r->n[6] = (r->n[6] & mask0) | (a->n[6] & mask1); + r->n[7] = (r->n[7] & mask0) | (a->n[7] & mask1); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + r->n[0] = a->n[0] | a->n[1] << 26; + r->n[1] = a->n[1] >> 6 | a->n[2] << 20; + r->n[2] = a->n[2] >> 12 | a->n[3] << 14; + r->n[3] = a->n[3] >> 18 | a->n[4] << 8; + r->n[4] = a->n[4] >> 24 | a->n[5] << 2 | a->n[6] << 28; + r->n[5] = a->n[6] >> 4 | a->n[7] << 22; + r->n[6] = a->n[7] >> 10 | a->n[8] << 16; + r->n[7] = a->n[8] >> 16 | a->n[9] << 10; +} + +static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + r->n[0] = a->n[0] & 0x3FFFFFFUL; + r->n[1] = a->n[0] >> 26 | ((a->n[1] << 6) & 0x3FFFFFFUL); + r->n[2] = a->n[1] >> 20 | ((a->n[2] << 12) & 0x3FFFFFFUL); + r->n[3] = a->n[2] >> 14 | ((a->n[3] << 18) & 0x3FFFFFFUL); + r->n[4] = a->n[3] >> 8 | ((a->n[4] << 24) & 0x3FFFFFFUL); + r->n[5] = (a->n[4] >> 2) & 0x3FFFFFFUL; + r->n[6] = a->n[4] >> 28 | ((a->n[5] << 4) & 0x3FFFFFFUL); + r->n[7] = a->n[5] >> 22 | ((a->n[6] << 10) & 0x3FFFFFFUL); + r->n[8] = a->n[6] >> 16 | ((a->n[7] << 16) & 0x3FFFFFFUL); + r->n[9] = a->n[7] >> 10; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; +#endif +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h new file mode 100644 index 000000000..8e69a560d --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52.h @@ -0,0 +1,47 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_ +#define _SECP256K1_FIELD_REPR_ + +#include + +typedef struct { + /* X = sum(i=0..4, elem[i]*2^52) mod n */ + uint64_t n[5]; +#ifdef VERIFY + int magnitude; + int normalized; +#endif +} secp256k1_fe; + +/* Unpacks a constant into a overlapping multi-limbed FE element. */ +#define SECP256K1_FE_CONST_INNER(d7, d6, d5, d4, d3, d2, d1, d0) { \ + (d0) | (((uint64_t)(d1) & 0xFFFFFUL) << 32), \ + ((uint64_t)(d1) >> 20) | (((uint64_t)(d2)) << 12) | (((uint64_t)(d3) & 0xFFUL) << 44), \ + ((uint64_t)(d3) >> 8) | (((uint64_t)(d4) & 0xFFFFFFFUL) << 24), \ + ((uint64_t)(d4) >> 28) | (((uint64_t)(d5)) << 4) | (((uint64_t)(d6) & 0xFFFFUL) << 36), \ + ((uint64_t)(d6) >> 16) | (((uint64_t)(d7)) << 16) \ +} + +#ifdef VERIFY +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0)), 1, 1} +#else +#define SECP256K1_FE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {SECP256K1_FE_CONST_INNER((d7), (d6), (d5), (d4), (d3), (d2), (d1), (d0))} +#endif + +typedef struct { + uint64_t n[4]; +} secp256k1_fe_storage; + +#define SECP256K1_FE_STORAGE_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{ \ + (d0) | (((uint64_t)(d1)) << 32), \ + (d2) | (((uint64_t)(d3)) << 32), \ + (d4) | (((uint64_t)(d5)) << 32), \ + (d6) | (((uint64_t)(d7)) << 32) \ +}} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h new file mode 100644 index 000000000..98cc004bf --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_asm_impl.h @@ -0,0 +1,502 @@ +/********************************************************************** + * Copyright (c) 2013-2014 Diederik Huys, Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * Changelog: + * - March 2013, Diederik Huys: original version + * - November 2014, Pieter Wuille: updated to use Peter Dettman's parallel multiplication algorithm + * - December 2014, Pieter Wuille: converted from YASM to GCC inline assembly + */ + +#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { +/** + * Registers: rdx:rax = multiplication accumulator + * r9:r8 = c + * r15:rcx = d + * r10-r14 = a0-a4 + * rbx = b + * rdi = r + * rsi = a / t? + */ + uint64_t tmp1, tmp2, tmp3; +__asm__ __volatile__( + "movq 0(%%rsi),%%r10\n" + "movq 8(%%rsi),%%r11\n" + "movq 16(%%rsi),%%r12\n" + "movq 24(%%rsi),%%r13\n" + "movq 32(%%rsi),%%r14\n" + + /* d += a3 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r13\n" + "movq %%rax,%%rcx\n" + "movq %%rdx,%%r15\n" + /* d += a2 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d = a0 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c = a4 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r14\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += (c & M) * R */ + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* t3 (tmp1) = d & M */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + "movq %%rsi,%q1\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* d += a4 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a0 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += c * R */ + "movq %%r8,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* t4 = d & M (%%rsi) */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* tx = t4 >> 48 (tmp3) */ + "movq %%rsi,%%rax\n" + "shrq $48,%%rax\n" + "movq %%rax,%q3\n" + /* t4 &= (M >> 4) (tmp2) */ + "movq $0xffffffffffff,%%rax\n" + "andq %%rax,%%rsi\n" + "movq %%rsi,%q2\n" + /* c = a0 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r10\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += a4 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a1 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* u0 = d & M (%%rsi) */ + "movq %%rcx,%%rsi\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* u0 = (u0 << 4) | tx (%%rsi) */ + "shlq $4,%%rsi\n" + "movq %q3,%%rax\n" + "orq %%rax,%%rsi\n" + /* c += u0 * (R >> 4) */ + "movq $0x1000003d1,%%rax\n" + "mulq %%rsi\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[0] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,0(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a1 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a0 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a4 * b2 */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a2 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c += (d & M) * R */ + "movq %%rcx,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 */ + "shrdq $52,%%r15,%%rcx\n" + "xorq %%r15,%%r15\n" + /* r[1] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,8(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a2 * b0 */ + "movq 0(%%rbx),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a1 * b1 */ + "movq 8(%%rbx),%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* c += a0 * b2 (last use of %%r10 = a0) */ + "movq 16(%%rbx),%%rax\n" + "mulq %%r10\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* fetch t3 (%%r10, overwrites a0), t4 (%%rsi) */ + "movq %q2,%%rsi\n" + "movq %q1,%%r10\n" + /* d += a4 * b3 */ + "movq 24(%%rbx),%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* d += a3 * b4 */ + "movq 32(%%rbx),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rcx\n" + "adcq %%rdx,%%r15\n" + /* c += (d & M) * R */ + "movq %%rcx,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 (%%rcx only) */ + "shrdq $52,%%r15,%%rcx\n" + /* r[2] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,16(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += t3 */ + "addq %%r10,%%r8\n" + /* c += d * R */ + "movq %%rcx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[3] = c & M */ + "movq %%r8,%%rax\n" + "movq $0xfffffffffffff,%%rdx\n" + "andq %%rdx,%%rax\n" + "movq %%rax,24(%%rdi)\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* c += t4 (%%r8 only) */ + "addq %%rsi,%%r8\n" + /* r[4] = c */ + "movq %%r8,32(%%rdi)\n" +: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "b"(b), "D"(r) +: "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" +); +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { +/** + * Registers: rdx:rax = multiplication accumulator + * r9:r8 = c + * rcx:rbx = d + * r10-r14 = a0-a4 + * r15 = M (0xfffffffffffff) + * rdi = r + * rsi = a / t? + */ + uint64_t tmp1, tmp2, tmp3; +__asm__ __volatile__( + "movq 0(%%rsi),%%r10\n" + "movq 8(%%rsi),%%r11\n" + "movq 16(%%rsi),%%r12\n" + "movq 24(%%rsi),%%r13\n" + "movq 32(%%rsi),%%r14\n" + "movq $0xfffffffffffff,%%r15\n" + + /* d = (a0*2) * a3 */ + "leaq (%%r10,%%r10,1),%%rax\n" + "mulq %%r13\n" + "movq %%rax,%%rbx\n" + "movq %%rdx,%%rcx\n" + /* d += (a1*2) * a2 */ + "leaq (%%r11,%%r11,1),%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c = a4 * a4 */ + "movq %%r14,%%rax\n" + "mulq %%r14\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += (c & M) * R */ + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* t3 (tmp1) = d & M */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + "movq %%rsi,%q1\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* a4 *= 2 */ + "addq %%r14,%%r14\n" + /* d += a0 * a4 */ + "movq %%r10,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d+= (a1*2) * a3 */ + "leaq (%%r11,%%r11,1),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += a2 * a2 */ + "movq %%r12,%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += c * R */ + "movq %%r8,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* t4 = d & M (%%rsi) */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* tx = t4 >> 48 (tmp3) */ + "movq %%rsi,%%rax\n" + "shrq $48,%%rax\n" + "movq %%rax,%q3\n" + /* t4 &= (M >> 4) (tmp2) */ + "movq $0xffffffffffff,%%rax\n" + "andq %%rax,%%rsi\n" + "movq %%rsi,%q2\n" + /* c = a0 * a0 */ + "movq %%r10,%%rax\n" + "mulq %%r10\n" + "movq %%rax,%%r8\n" + "movq %%rdx,%%r9\n" + /* d += a1 * a4 */ + "movq %%r11,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += (a2*2) * a3 */ + "leaq (%%r12,%%r12,1),%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* u0 = d & M (%%rsi) */ + "movq %%rbx,%%rsi\n" + "andq %%r15,%%rsi\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* u0 = (u0 << 4) | tx (%%rsi) */ + "shlq $4,%%rsi\n" + "movq %q3,%%rax\n" + "orq %%rax,%%rsi\n" + /* c += u0 * (R >> 4) */ + "movq $0x1000003d1,%%rax\n" + "mulq %%rsi\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[0] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,0(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* a0 *= 2 */ + "addq %%r10,%%r10\n" + /* c += a0 * a1 */ + "movq %%r10,%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a2 * a4 */ + "movq %%r12,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* d += a3 * a3 */ + "movq %%r13,%%rax\n" + "mulq %%r13\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c += (d & M) * R */ + "movq %%rbx,%%rax\n" + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 */ + "shrdq $52,%%rcx,%%rbx\n" + "xorq %%rcx,%%rcx\n" + /* r[1] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,8(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += a0 * a2 (last use of %%r10) */ + "movq %%r10,%%rax\n" + "mulq %%r12\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* fetch t3 (%%r10, overwrites a0),t4 (%%rsi) */ + "movq %q2,%%rsi\n" + "movq %q1,%%r10\n" + /* c += a1 * a1 */ + "movq %%r11,%%rax\n" + "mulq %%r11\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d += a3 * a4 */ + "movq %%r13,%%rax\n" + "mulq %%r14\n" + "addq %%rax,%%rbx\n" + "adcq %%rdx,%%rcx\n" + /* c += (d & M) * R */ + "movq %%rbx,%%rax\n" + "andq %%r15,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* d >>= 52 (%%rbx only) */ + "shrdq $52,%%rcx,%%rbx\n" + /* r[2] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,16(%%rdi)\n" + /* c >>= 52 */ + "shrdq $52,%%r9,%%r8\n" + "xorq %%r9,%%r9\n" + /* c += t3 */ + "addq %%r10,%%r8\n" + /* c += d * R */ + "movq %%rbx,%%rax\n" + "movq $0x1000003d10,%%rdx\n" + "mulq %%rdx\n" + "addq %%rax,%%r8\n" + "adcq %%rdx,%%r9\n" + /* r[3] = c & M */ + "movq %%r8,%%rax\n" + "andq %%r15,%%rax\n" + "movq %%rax,24(%%rdi)\n" + /* c >>= 52 (%%r8 only) */ + "shrdq $52,%%r9,%%r8\n" + /* c += t4 (%%r8 only) */ + "addq %%rsi,%%r8\n" + /* r[4] = c */ + "movq %%r8,32(%%rdi)\n" +: "+S"(a), "=m"(tmp1), "=m"(tmp2), "=m"(tmp3) +: "D"(r) +: "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11", "%r12", "%r13", "%r14", "%r15", "cc", "memory" +); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h new file mode 100644 index 000000000..dd88f38c7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_impl.h @@ -0,0 +1,451 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_REPR_IMPL_H_ +#define _SECP256K1_FIELD_REPR_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "util.h" +#include "num.h" +#include "field.h" + +#if defined(USE_ASM_X86_64) +#include "field_5x52_asm_impl.h" +#else +#include "field_5x52_int128_impl.h" +#endif + +/** Implements arithmetic modulo FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F, + * represented as 5 uint64_t's in base 2^52. The values are allowed to contain >52 each. In particular, + * each FieldElem has a 'magnitude' associated with it. Internally, a magnitude M means each element + * is at most M*(2^53-1), except the most significant one, which is limited to M*(2^49-1). All operations + * accept any input with magnitude at most M, and have different rules for propagating magnitude to their + * output. + */ + +#ifdef VERIFY +static void secp256k1_fe_verify(const secp256k1_fe *a) { + const uint64_t *d = a->n; + int m = a->normalized ? 1 : 2 * a->magnitude, r = 1; + /* secp256k1 'p' value defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + r &= (d[0] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[1] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[2] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[3] <= 0xFFFFFFFFFFFFFULL * m); + r &= (d[4] <= 0x0FFFFFFFFFFFFULL * m); + r &= (a->magnitude >= 0); + r &= (a->magnitude <= 2048); + if (a->normalized) { + r &= (a->magnitude <= 1); + if (r && (d[4] == 0x0FFFFFFFFFFFFULL) && ((d[3] & d[2] & d[1]) == 0xFFFFFFFFFFFFFULL)) { + r &= (d[0] < 0xFFFFEFFFFFC2FULL); + } + } + VERIFY_CHECK(r == 1); +} +#endif + +static void secp256k1_fe_normalize(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + /* Apply the final reduction (for constant-time behaviour, we do it always) */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t4 &= 0x0FFFFFFFFFFFFULL; + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_weak(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_normalize_var(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t m; + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; m = t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; m &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; m &= t3; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + /* At most a single final reduction is needed; check if the value is >= the field characteristic */ + x = (t4 >> 48) | ((t4 == 0x0FFFFFFFFFFFFULL) & (m == 0xFFFFFFFFFFFFFULL) + & (t0 >= 0xFFFFEFFFFFC2FULL)); + + if (x) { + t0 += 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; + + /* If t4 didn't carry to bit 48 already, then it should have after any final reduction */ + VERIFY_CHECK(t4 >> 48 == x); + + /* Mask off the possible multiple of 2^256 from the final reduction */ + t4 &= 0x0FFFFFFFFFFFFULL; + } + + r->n[0] = t0; r->n[1] = t1; r->n[2] = t2; r->n[3] = t3; r->n[4] = t4; + +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +static int secp256k1_fe_normalizes_to_zero(secp256k1_fe *r) { + uint64_t t0 = r->n[0], t1 = r->n[1], t2 = r->n[2], t3 = r->n[3], t4 = r->n[4]; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + uint64_t z0, z1; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + uint64_t x = t4 >> 48; t4 &= 0x0FFFFFFFFFFFFULL; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + t1 += (t0 >> 52); t0 &= 0xFFFFFFFFFFFFFULL; z0 = t0; z1 = t0 ^ 0x1000003D0ULL; + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +static int secp256k1_fe_normalizes_to_zero_var(secp256k1_fe *r) { + uint64_t t0, t1, t2, t3, t4; + uint64_t z0, z1; + uint64_t x; + + t0 = r->n[0]; + t4 = r->n[4]; + + /* Reduce t4 at the start so there will be at most a single carry from the first pass */ + x = t4 >> 48; + + /* The first pass ensures the magnitude is 1, ... */ + t0 += x * 0x1000003D1ULL; + + /* z0 tracks a possible raw value of 0, z1 tracks a possible raw value of P */ + z0 = t0 & 0xFFFFFFFFFFFFFULL; + z1 = z0 ^ 0x1000003D0ULL; + + /* Fast return path should catch the majority of cases */ + if ((z0 != 0ULL) & (z1 != 0xFFFFFFFFFFFFFULL)) { + return 0; + } + + t1 = r->n[1]; + t2 = r->n[2]; + t3 = r->n[3]; + + t4 &= 0x0FFFFFFFFFFFFULL; + + t1 += (t0 >> 52); + t2 += (t1 >> 52); t1 &= 0xFFFFFFFFFFFFFULL; z0 |= t1; z1 &= t1; + t3 += (t2 >> 52); t2 &= 0xFFFFFFFFFFFFFULL; z0 |= t2; z1 &= t2; + t4 += (t3 >> 52); t3 &= 0xFFFFFFFFFFFFFULL; z0 |= t3; z1 &= t3; + z0 |= t4; z1 &= t4 ^ 0xF000000000000ULL; + + /* ... except for a possible carry at bit 48 of t4 (i.e. bit 256 of the field element) */ + VERIFY_CHECK(t4 >> 49 == 0); + + return (z0 == 0) | (z1 == 0xFFFFFFFFFFFFFULL); +} + +SECP256K1_INLINE static void secp256k1_fe_set_int(secp256k1_fe *r, int a) { + r->n[0] = a; + r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static int secp256k1_fe_is_zero(const secp256k1_fe *a) { + const uint64_t *t = a->n; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return (t[0] | t[1] | t[2] | t[3] | t[4]) == 0; +} + +SECP256K1_INLINE static int secp256k1_fe_is_odd(const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + return a->n[0] & 1; +} + +SECP256K1_INLINE static void secp256k1_fe_clear(secp256k1_fe *a) { + int i; +#ifdef VERIFY + a->magnitude = 0; + a->normalized = 1; +#endif + for (i=0; i<5; i++) { + a->n[i] = 0; + } +} + +static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + VERIFY_CHECK(b->normalized); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); +#endif + for (i = 4; i >= 0; i--) { + if (a->n[i] > b->n[i]) { + return 1; + } + if (a->n[i] < b->n[i]) { + return -1; + } + } + return 0; +} + +static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) { + int i; + r->n[0] = r->n[1] = r->n[2] = r->n[3] = r->n[4] = 0; + for (i=0; i<32; i++) { + int j; + for (j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + r->n[limb] |= (uint64_t)((a[31-i] >> (4*j)) & 0xF) << shift; + } + } + if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) { + return 0; + } +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; + secp256k1_fe_verify(r); +#endif + return 1; +} + +/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */ +static void secp256k1_fe_get_b32(unsigned char *r, const secp256k1_fe *a) { + int i; +#ifdef VERIFY + VERIFY_CHECK(a->normalized); + secp256k1_fe_verify(a); +#endif + for (i=0; i<32; i++) { + int j; + int c = 0; + for (j=0; j<2; j++) { + int limb = (8*i+4*j)/52; + int shift = (8*i+4*j)%52; + c |= ((a->n[limb] >> shift) & 0xF) << (4 * j); + } + r[31-i] = c; + } +} + +SECP256K1_INLINE static void secp256k1_fe_negate(secp256k1_fe *r, const secp256k1_fe *a, int m) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= m); + secp256k1_fe_verify(a); +#endif + r->n[0] = 0xFFFFEFFFFFC2FULL * 2 * (m + 1) - a->n[0]; + r->n[1] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[1]; + r->n[2] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[2]; + r->n[3] = 0xFFFFFFFFFFFFFULL * 2 * (m + 1) - a->n[3]; + r->n[4] = 0x0FFFFFFFFFFFFULL * 2 * (m + 1) - a->n[4]; +#ifdef VERIFY + r->magnitude = m + 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_mul_int(secp256k1_fe *r, int a) { + r->n[0] *= a; + r->n[1] *= a; + r->n[2] *= a; + r->n[3] *= a; + r->n[4] *= a; +#ifdef VERIFY + r->magnitude *= a; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +SECP256K1_INLINE static void secp256k1_fe_add(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + secp256k1_fe_verify(a); +#endif + r->n[0] += a->n[0]; + r->n[1] += a->n[1]; + r->n[2] += a->n[2]; + r->n[3] += a->n[3]; + r->n[4] += a->n[4]; +#ifdef VERIFY + r->magnitude += a->magnitude; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_mul(secp256k1_fe *r, const secp256k1_fe *a, const secp256k1_fe * SECP256K1_RESTRICT b) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + VERIFY_CHECK(b->magnitude <= 8); + secp256k1_fe_verify(a); + secp256k1_fe_verify(b); + VERIFY_CHECK(r != b); +#endif + secp256k1_fe_mul_inner(r->n, a->n, b->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static void secp256k1_fe_sqr(secp256k1_fe *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->magnitude <= 8); + secp256k1_fe_verify(a); +#endif + secp256k1_fe_sqr_inner(r->n, a->n); +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 0; + secp256k1_fe_verify(r); +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_fe *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((uint64_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); + r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1); +#ifdef VERIFY + if (a->magnitude > r->magnitude) { + r->magnitude = a->magnitude; + } + r->normalized &= a->normalized; +#endif +} + +static SECP256K1_INLINE void secp256k1_fe_storage_cmov(secp256k1_fe_storage *r, const secp256k1_fe_storage *a, int flag) { + uint64_t mask0, mask1; + mask0 = flag + ~((uint64_t)0); + mask1 = ~mask0; + r->n[0] = (r->n[0] & mask0) | (a->n[0] & mask1); + r->n[1] = (r->n[1] & mask0) | (a->n[1] & mask1); + r->n[2] = (r->n[2] & mask0) | (a->n[2] & mask1); + r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1); +} + +static void secp256k1_fe_to_storage(secp256k1_fe_storage *r, const secp256k1_fe *a) { +#ifdef VERIFY + VERIFY_CHECK(a->normalized); +#endif + r->n[0] = a->n[0] | a->n[1] << 52; + r->n[1] = a->n[1] >> 12 | a->n[2] << 40; + r->n[2] = a->n[2] >> 24 | a->n[3] << 28; + r->n[3] = a->n[3] >> 36 | a->n[4] << 16; +} + +static SECP256K1_INLINE void secp256k1_fe_from_storage(secp256k1_fe *r, const secp256k1_fe_storage *a) { + r->n[0] = a->n[0] & 0xFFFFFFFFFFFFFULL; + r->n[1] = a->n[0] >> 52 | ((a->n[1] << 12) & 0xFFFFFFFFFFFFFULL); + r->n[2] = a->n[1] >> 40 | ((a->n[2] << 24) & 0xFFFFFFFFFFFFFULL); + r->n[3] = a->n[2] >> 28 | ((a->n[3] << 36) & 0xFFFFFFFFFFFFFULL); + r->n[4] = a->n[3] >> 16; +#ifdef VERIFY + r->magnitude = 1; + r->normalized = 1; +#endif +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h new file mode 100644 index 000000000..0bf22bdd3 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_5x52_int128_impl.h @@ -0,0 +1,277 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_INNER5X52_IMPL_H_ +#define _SECP256K1_FIELD_INNER5X52_IMPL_H_ + +#include + +#ifdef VERIFY +#define VERIFY_BITS(x, n) VERIFY_CHECK(((x) >> (n)) == 0) +#else +#define VERIFY_BITS(x, n) do { } while(0) +#endif + +SECP256K1_INLINE static void secp256k1_fe_mul_inner(uint64_t *r, const uint64_t *a, const uint64_t * SECP256K1_RESTRICT b) { + uint128_t c, d; + uint64_t t3, t4, tx, u0; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + VERIFY_BITS(b[0], 56); + VERIFY_BITS(b[1], 56); + VERIFY_BITS(b[2], 56); + VERIFY_BITS(b[3], 56); + VERIFY_BITS(b[4], 52); + VERIFY_CHECK(r != b); + + /* [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*b[x-i], i=0..x). + * Note that [x 0 0 0 0 0] = [x*R]. + */ + + d = (uint128_t)a0 * b[3] + + (uint128_t)a1 * b[2] + + (uint128_t)a2 * b[1] + + (uint128_t)a3 * b[0]; + VERIFY_BITS(d, 114); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * b[4]; + VERIFY_BITS(c, 112); + /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + d += (c & M) * R; c >>= 52; + VERIFY_BITS(d, 115); + VERIFY_BITS(c, 60); + /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = d & M; d >>= 52; + VERIFY_BITS(t3, 52); + VERIFY_BITS(d, 63); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + + d += (uint128_t)a0 * b[4] + + (uint128_t)a1 * b[3] + + (uint128_t)a2 * b[2] + + (uint128_t)a3 * b[1] + + (uint128_t)a4 * b[0]; + VERIFY_BITS(d, 115); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + d += c * R; + VERIFY_BITS(d, 116); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(d, 64); + /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + tx = (t4 >> 48); t4 &= (M >> 4); + VERIFY_BITS(tx, 4); + VERIFY_BITS(t4, 48); + /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + + c = (uint128_t)a0 * b[0]; + VERIFY_BITS(c, 112); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ + d += (uint128_t)a1 * b[4] + + (uint128_t)a2 * b[3] + + (uint128_t)a3 * b[2] + + (uint128_t)a4 * b[1]; + VERIFY_BITS(d, 115); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 63); + /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = (u0 << 4) | tx; + VERIFY_BITS(u0, 56); + /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + c += (uint128_t)u0 * (R >> 4); + VERIFY_BITS(c, 115); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(c, 61); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ + + c += (uint128_t)a0 * b[1] + + (uint128_t)a1 * b[0]; + VERIFY_BITS(c, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ + d += (uint128_t)a2 * b[4] + + (uint128_t)a3 * b[3] + + (uint128_t)a4 * b[2]; + VERIFY_BITS(d, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + + c += (uint128_t)a0 * b[2] + + (uint128_t)a1 * b[1] + + (uint128_t)a2 * b[0]; + VERIFY_BITS(c, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint128_t)a3 * b[4] + + (uint128_t)a4 * b[3]; + VERIFY_BITS(d, 114); + /* [d 0 0 t4 t3 c t1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; c >>= 52; + VERIFY_BITS(r[2], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += d * R + t3; + VERIFY_BITS(c, 100); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(c, 48); + /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += t4; + VERIFY_BITS(c, 49); + /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = c; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +SECP256K1_INLINE static void secp256k1_fe_sqr_inner(uint64_t *r, const uint64_t *a) { + uint128_t c, d; + uint64_t a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3], a4 = a[4]; + int64_t t3, t4, tx, u0; + const uint64_t M = 0xFFFFFFFFFFFFFULL, R = 0x1000003D10ULL; + + VERIFY_BITS(a[0], 56); + VERIFY_BITS(a[1], 56); + VERIFY_BITS(a[2], 56); + VERIFY_BITS(a[3], 56); + VERIFY_BITS(a[4], 52); + + /** [... a b c] is a shorthand for ... + a<<104 + b<<52 + c<<0 mod n. + * px is a shorthand for sum(a[i]*a[x-i], i=0..x). + * Note that [x 0 0 0 0 0] = [x*R]. + */ + + d = (uint128_t)(a0*2) * a3 + + (uint128_t)(a1*2) * a2; + VERIFY_BITS(d, 114); + /* [d 0 0 0] = [p3 0 0 0] */ + c = (uint128_t)a4 * a4; + VERIFY_BITS(c, 112); + /* [c 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + d += (c & M) * R; c >>= 52; + VERIFY_BITS(d, 115); + VERIFY_BITS(c, 60); + /* [c 0 0 0 0 0 d 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + t3 = d & M; d >>= 52; + VERIFY_BITS(t3, 52); + VERIFY_BITS(d, 63); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 0 p3 0 0 0] */ + + a4 *= 2; + d += (uint128_t)a0 * a4 + + (uint128_t)(a1*2) * a3 + + (uint128_t)a2 * a2; + VERIFY_BITS(d, 115); + /* [c 0 0 0 0 d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + d += c * R; + VERIFY_BITS(d, 116); + /* [d t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + t4 = d & M; d >>= 52; + VERIFY_BITS(t4, 52); + VERIFY_BITS(d, 64); + /* [d t4 t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + tx = (t4 >> 48); t4 &= (M >> 4); + VERIFY_BITS(tx, 4); + VERIFY_BITS(t4, 48); + /* [d t4+(tx<<48) t3 0 0 0] = [p8 0 0 0 p4 p3 0 0 0] */ + + c = (uint128_t)a0 * a0; + VERIFY_BITS(c, 112); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 0 p4 p3 0 0 p0] */ + d += (uint128_t)a1 * a4 + + (uint128_t)(a2*2) * a3; + VERIFY_BITS(d, 114); + /* [d t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = d & M; d >>= 52; + VERIFY_BITS(u0, 52); + VERIFY_BITS(d, 62); + /* [d u0 t4+(tx<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + /* [d 0 t4+(tx<<48)+(u0<<52) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + u0 = (u0 << 4) | tx; + VERIFY_BITS(u0, 56); + /* [d 0 t4+(u0<<48) t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + c += (uint128_t)u0 * (R >> 4); + VERIFY_BITS(c, 113); + /* [d 0 t4 t3 0 0 c] = [p8 0 0 p5 p4 p3 0 0 p0] */ + r[0] = c & M; c >>= 52; + VERIFY_BITS(r[0], 52); + VERIFY_BITS(c, 61); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 0 p0] */ + + a0 *= 2; + c += (uint128_t)a0 * a1; + VERIFY_BITS(c, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 0 p5 p4 p3 0 p1 p0] */ + d += (uint128_t)a2 * a4 + + (uint128_t)a3 * a3; + VERIFY_BITS(d, 114); + /* [d 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 t4 t3 0 c r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + r[1] = c & M; c >>= 52; + VERIFY_BITS(r[1], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 0 p1 p0] */ + + c += (uint128_t)a0 * a2 + + (uint128_t)a1 * a1; + VERIFY_BITS(c, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 0 p6 p5 p4 p3 p2 p1 p0] */ + d += (uint128_t)a3 * a4; + VERIFY_BITS(d, 114); + /* [d 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += (d & M) * R; d >>= 52; + VERIFY_BITS(c, 115); + VERIFY_BITS(d, 62); + /* [d 0 0 0 t4 t3 c r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[2] = c & M; c >>= 52; + VERIFY_BITS(r[2], 52); + VERIFY_BITS(c, 63); + /* [d 0 0 0 t4 t3+c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + + c += d * R + t3; + VERIFY_BITS(c, 100); + /* [t4 c r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[3] = c & M; c >>= 52; + VERIFY_BITS(r[3], 52); + VERIFY_BITS(c, 48); + /* [t4+c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + c += t4; + VERIFY_BITS(c, 49); + /* [c r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ + r[4] = c; + VERIFY_BITS(r[4], 49); + /* [r4 r3 r2 r1 r0] = [p8 p7 p6 p5 p4 p3 p2 p1 p0] */ +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h new file mode 100644 index 000000000..5127b279b --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/field_impl.h @@ -0,0 +1,315 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_FIELD_IMPL_H_ +#define _SECP256K1_FIELD_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "util.h" + +#if defined(USE_FIELD_10X26) +#include "field_10x26_impl.h" +#elif defined(USE_FIELD_5X52) +#include "field_5x52_impl.h" +#else +#error "Please select field implementation" +#endif + +SECP256K1_INLINE static int secp256k1_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero(&na); +} + +SECP256K1_INLINE static int secp256k1_fe_equal_var(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe na; + secp256k1_fe_negate(&na, a, 1); + secp256k1_fe_add(&na, b); + return secp256k1_fe_normalizes_to_zero_var(&na); +} + +static int secp256k1_fe_sqrt(secp256k1_fe *r, const secp256k1_fe *a) { + /** Given that p is congruent to 3 mod 4, we can compute the square root of + * a mod p as the (p+1)/4'th power of a. + * + * As (p+1)/4 is an even number, it will have the same result for a and for + * (-a). Only one of these two numbers actually has a square root however, + * so we test at the end by squaring and comparing to the input. + * Also because (p+1)/4 is an even number, the computed square root is + * itself always a square (a ** ((p+1)/4) is the square of a ** ((p+1)/8)). + */ + secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; + int j; + + /** The binary representation of (p + 1)/4 has 3 blocks of 1s, with lengths in + * { 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: + * 1, [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<6; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + secp256k1_fe_sqr(&t1, &t1); + secp256k1_fe_sqr(r, &t1); + + /* Check that a square root was actually calculated */ + + secp256k1_fe_sqr(&t1, r); + return secp256k1_fe_equal(&t1, a); +} + +static void secp256k1_fe_inv(secp256k1_fe *r, const secp256k1_fe *a) { + secp256k1_fe x2, x3, x6, x9, x11, x22, x44, x88, x176, x220, x223, t1; + int j; + + /** The binary representation of (p - 2) has 5 blocks of 1s, with lengths in + * { 1, 2, 22, 223 }. Use an addition chain to calculate 2^n - 1 for each block: + * [1], [2], 3, 6, 9, 11, [22], 44, 88, 176, 220, [223] + */ + + secp256k1_fe_sqr(&x2, a); + secp256k1_fe_mul(&x2, &x2, a); + + secp256k1_fe_sqr(&x3, &x2); + secp256k1_fe_mul(&x3, &x3, a); + + x6 = x3; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x6, &x6); + } + secp256k1_fe_mul(&x6, &x6, &x3); + + x9 = x6; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x9, &x9); + } + secp256k1_fe_mul(&x9, &x9, &x3); + + x11 = x9; + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&x11, &x11); + } + secp256k1_fe_mul(&x11, &x11, &x2); + + x22 = x11; + for (j=0; j<11; j++) { + secp256k1_fe_sqr(&x22, &x22); + } + secp256k1_fe_mul(&x22, &x22, &x11); + + x44 = x22; + for (j=0; j<22; j++) { + secp256k1_fe_sqr(&x44, &x44); + } + secp256k1_fe_mul(&x44, &x44, &x22); + + x88 = x44; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x88, &x88); + } + secp256k1_fe_mul(&x88, &x88, &x44); + + x176 = x88; + for (j=0; j<88; j++) { + secp256k1_fe_sqr(&x176, &x176); + } + secp256k1_fe_mul(&x176, &x176, &x88); + + x220 = x176; + for (j=0; j<44; j++) { + secp256k1_fe_sqr(&x220, &x220); + } + secp256k1_fe_mul(&x220, &x220, &x44); + + x223 = x220; + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&x223, &x223); + } + secp256k1_fe_mul(&x223, &x223, &x3); + + /* The final result is then assembled using a sliding window over the blocks. */ + + t1 = x223; + for (j=0; j<23; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x22); + for (j=0; j<5; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, a); + for (j=0; j<3; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(&t1, &t1, &x2); + for (j=0; j<2; j++) { + secp256k1_fe_sqr(&t1, &t1); + } + secp256k1_fe_mul(r, a, &t1); +} + +static void secp256k1_fe_inv_var(secp256k1_fe *r, const secp256k1_fe *a) { +#if defined(USE_FIELD_INV_BUILTIN) + secp256k1_fe_inv(r, a); +#elif defined(USE_FIELD_INV_NUM) + secp256k1_num n, m; + static const secp256k1_fe negone = SECP256K1_FE_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, 0xFFFFFC2EUL + ); + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + unsigned char b[32]; + int res; + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + res = secp256k1_fe_set_b32(r, b); + (void)res; + VERIFY_CHECK(res); + /* Verify the result is the (unique) valid inverse using non-GMP code. */ + secp256k1_fe_mul(&c, &c, r); + secp256k1_fe_add(&c, &negone); + CHECK(secp256k1_fe_normalizes_to_zero_var(&c)); +#else +#error "Please select field inverse implementation" +#endif +} + +static void secp256k1_fe_inv_all_var(secp256k1_fe *r, const secp256k1_fe *a, size_t len) { + secp256k1_fe u; + size_t i; + if (len < 1) { + return; + } + + VERIFY_CHECK((r + len <= a) || (a + len <= r)); + + r[0] = a[0]; + + i = 0; + while (++i < len) { + secp256k1_fe_mul(&r[i], &r[i - 1], &a[i]); + } + + secp256k1_fe_inv_var(&u, &r[--i]); + + while (i > 0) { + size_t j = i--; + secp256k1_fe_mul(&r[j], &r[i], &u); + secp256k1_fe_mul(&u, &u, &a[j]); + } + + r[0] = u; +} + +static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) { +#ifndef USE_NUM_NONE + unsigned char b[32]; + secp256k1_num n; + secp256k1_num m; + /* secp256k1 field prime, value p defined in "Standards for Efficient Cryptography" (SEC2) 2.7.1. */ + static const unsigned char prime[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFC,0x2F + }; + + secp256k1_fe c = *a; + secp256k1_fe_normalize_var(&c); + secp256k1_fe_get_b32(b, &c); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_num_set_bin(&m, prime, 32); + return secp256k1_num_jacobi(&n, &m) >= 0; +#else + secp256k1_fe r; + return secp256k1_fe_sqrt(&r, a); +#endif +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c new file mode 100644 index 000000000..1835fd491 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/gen_context.c @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Thomas Daede, Cory Fields * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#define USE_BASIC_CONFIG 1 + +#include "basic-config.h" +#include "include/secp256k1.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_gen_impl.h" + +static void default_error_callback_fn(const char* str, void* data) { + (void)data; + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} + +static const secp256k1_callback default_error_callback = { + default_error_callback_fn, + NULL +}; + +int main(int argc, char **argv) { + secp256k1_ecmult_gen_context ctx; + int inner; + int outer; + FILE* fp; + + (void)argc; + (void)argv; + + fp = fopen("src/ecmult_static_context.h","w"); + if (fp == NULL) { + fprintf(stderr, "Could not open src/ecmult_static_context.h for writing!\n"); + return -1; + } + + fprintf(fp, "#ifndef _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); + fprintf(fp, "#define _SECP256K1_ECMULT_STATIC_CONTEXT_\n"); + fprintf(fp, "#include \"group.h\"\n"); + fprintf(fp, "#define SC SECP256K1_GE_STORAGE_CONST\n"); + fprintf(fp, "static const secp256k1_ge_storage secp256k1_ecmult_static_context[64][16] = {\n"); + + secp256k1_ecmult_gen_context_init(&ctx); + secp256k1_ecmult_gen_context_build(&ctx, &default_error_callback); + for(outer = 0; outer != 64; outer++) { + fprintf(fp,"{\n"); + for(inner = 0; inner != 16; inner++) { + fprintf(fp," SC(%uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu, %uu)", SECP256K1_GE_STORAGE_CONST_GET((*ctx.prec)[outer][inner])); + if (inner != 15) { + fprintf(fp,",\n"); + } else { + fprintf(fp,"\n"); + } + } + if (outer != 63) { + fprintf(fp,"},\n"); + } else { + fprintf(fp,"}\n"); + } + } + fprintf(fp,"};\n"); + secp256k1_ecmult_gen_context_clear(&ctx); + + fprintf(fp, "#undef SC\n"); + fprintf(fp, "#endif\n"); + fclose(fp); + + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group.h new file mode 100644 index 000000000..4957b248f --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group.h @@ -0,0 +1,144 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_GROUP_ +#define _SECP256K1_GROUP_ + +#include "num.h" +#include "field.h" + +/** A group element of the secp256k1 curve, in affine coordinates. */ +typedef struct { + secp256k1_fe x; + secp256k1_fe y; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_ge; + +#define SECP256K1_GE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), 0} +#define SECP256K1_GE_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} + +/** A group element of the secp256k1 curve, in jacobian coordinates. */ +typedef struct { + secp256k1_fe x; /* actual X: x/z^2 */ + secp256k1_fe y; /* actual Y: y/z^3 */ + secp256k1_fe z; + int infinity; /* whether this represents the point at infinity */ +} secp256k1_gej; + +#define SECP256K1_GEJ_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_CONST((i),(j),(k),(l),(m),(n),(o),(p)), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1), 0} +#define SECP256K1_GEJ_CONST_INFINITY {SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 0), 1} + +typedef struct { + secp256k1_fe_storage x; + secp256k1_fe_storage y; +} secp256k1_ge_storage; + +#define SECP256K1_GE_STORAGE_CONST(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) {SECP256K1_FE_STORAGE_CONST((a),(b),(c),(d),(e),(f),(g),(h)), SECP256K1_FE_STORAGE_CONST((i),(j),(k),(l),(m),(n),(o),(p))} + +#define SECP256K1_GE_STORAGE_CONST_GET(t) SECP256K1_FE_STORAGE_CONST_GET(t.x), SECP256K1_FE_STORAGE_CONST_GET(t.y) + +/** Set a group element equal to the point with given X and Y coordinates */ +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y); + +/** Set a group element (affine) equal to the point with the given X coordinate + * and a Y coordinate that is a quadratic residue modulo p. The return value + * is true iff a coordinate with the given X coordinate exists. + */ +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x); + +/** Set a group element (affine) equal to the point with the given X coordinate, and given oddness + * for Y. Return value indicates whether the result is valid. */ +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd); + +/** Check whether a group element is the point at infinity. */ +static int secp256k1_ge_is_infinity(const secp256k1_ge *a); + +/** Check whether a group element is valid (i.e., on the curve). */ +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a); + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a); + +/** Set a group element equal to another which is given in jacobian coordinates */ +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); + +/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb); + +/** Set a batch of group elements equal to the inputs given in jacobian + * coordinates (with known z-ratios). zr must contain the known z-ratios such + * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. */ +static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len); + +/** Bring a batch inputs given in jacobian coordinates (with known z-ratios) to + * the same global z "denominator". zr must contain the known z-ratios such + * that mul(a[i].z, zr[i+1]) == a[i+1].z. zr[0] is ignored. The x and y + * coordinates of the result are stored in r, the common z coordinate is + * stored in globalz. */ +static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr); + +/** Set a group element (jacobian) equal to the point at infinity. */ +static void secp256k1_gej_set_infinity(secp256k1_gej *r); + +/** Set a group element (jacobian) equal to another which is given in affine coordinates. */ +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a); + +/** Compare the X coordinate of a group element (jacobian). */ +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a); + +/** Set r equal to the inverse of a (i.e., mirrored around the X axis) */ +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a); + +/** Check whether a group element is the point at infinity. */ +static int secp256k1_gej_is_infinity(const secp256k1_gej *a); + +/** Check whether a group element's y coordinate is a quadratic residue. */ +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a); + +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). + * a may not be zero. Constant time. */ +static void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + +/** Set r equal to the double of a. If rzr is not-NULL, r->z = a->z * *rzr (where infinity means an implicit z = 0). */ +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b (with b given in affine coordinates, and not infinity). */ +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b); + +/** Set r equal to the sum of a and b (with b given in affine coordinates). This is more efficient + than secp256k1_gej_add_var. It is identical to secp256k1_gej_add_ge but without constant-time + guarantee, and b is allowed to be infinity. If rzr is non-NULL, r->z = a->z * *rzr (a cannot be infinity in that case). */ +static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr); + +/** Set r equal to the sum of a and b (with the inverse of b's Z coordinate passed as bzinv). */ +static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv); + +#ifdef USE_ENDOMORPHISM +/** Set r to be equal to lambda times a, where lambda is chosen in a way such that this is very fast. */ +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a); +#endif + +/** Clear a secp256k1_gej to prevent leaking sensitive information. */ +static void secp256k1_gej_clear(secp256k1_gej *r); + +/** Clear a secp256k1_ge to prevent leaking sensitive information. */ +static void secp256k1_ge_clear(secp256k1_ge *r); + +/** Convert a group element to the storage type. */ +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a); + +/** Convert a group element back from the storage type. */ +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a); + +/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ +static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag); + +/** Rescale a jacobian point by b which must be non-zero. Constant-time. */ +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h new file mode 100644 index 000000000..7d723532f --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/group_impl.h @@ -0,0 +1,700 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_GROUP_IMPL_H_ +#define _SECP256K1_GROUP_IMPL_H_ + +#include "num.h" +#include "field.h" +#include "group.h" + +/* These points can be generated in sage as follows: + * + * 0. Setup a worksheet with the following parameters. + * b = 4 # whatever CURVE_B will be set to + * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + * C = EllipticCurve ([F (0), F (b)]) + * + * 1. Determine all the small orders available to you. (If there are + * no satisfactory ones, go back and change b.) + * print C.order().factor(limit=1000) + * + * 2. Choose an order as one of the prime factors listed in the above step. + * (You can also multiply some to get a composite order, though the + * tests will crash trying to invert scalars during signing.) We take a + * random point and scale it to drop its order to the desired value. + * There is some probability this won't work; just try again. + * order = 199 + * P = C.random_point() + * P = (int(P.order()) / int(order)) * P + * assert(P.order() == order) + * + * 3. Print the values. You'll need to use a vim macro or something to + * split the hex output into 4-byte chunks. + * print "%x %x" % P.xy() + */ +#if defined(EXHAUSTIVE_TEST_ORDER) +# if EXHAUSTIVE_TEST_ORDER == 199 +const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0xFA7CC9A7, 0x0737F2DB, 0xA749DD39, 0x2B4FB069, + 0x3B017A7D, 0xA808C2F1, 0xFB12940C, 0x9EA66C18, + 0x78AC123A, 0x5ED8AEF3, 0x8732BC91, 0x1F3A2868, + 0x48DF246C, 0x808DAE72, 0xCFE52572, 0x7F0501ED +); + +const int CURVE_B = 4; +# elif EXHAUSTIVE_TEST_ORDER == 13 +const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0xedc60018, 0xa51a786b, 0x2ea91f4d, 0x4c9416c0, + 0x9de54c3b, 0xa1316554, 0x6cf4345c, 0x7277ef15, + 0x54cb1b6b, 0xdc8c1273, 0x087844ea, 0x43f4603e, + 0x0eaf9a43, 0xf6effe55, 0x939f806d, 0x37adf8ac +); +const int CURVE_B = 2; +# else +# error No known generator for the specified exhaustive test group order. +# endif +#else +/** Generator for secp256k1, value 'g' defined in + * "Standards for Efficient Cryptography" (SEC2) 2.7.1. + */ +static const secp256k1_ge secp256k1_ge_const_g = SECP256K1_GE_CONST( + 0x79BE667EUL, 0xF9DCBBACUL, 0x55A06295UL, 0xCE870B07UL, + 0x029BFCDBUL, 0x2DCE28D9UL, 0x59F2815BUL, 0x16F81798UL, + 0x483ADA77UL, 0x26A3C465UL, 0x5DA4FBFCUL, 0x0E1108A8UL, + 0xFD17B448UL, 0xA6855419UL, 0x9C47D08FUL, 0xFB10D4B8UL +); + +const int CURVE_B = 7; +#endif + +static void secp256k1_ge_set_gej_zinv(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zi) { + secp256k1_fe zi2; + secp256k1_fe zi3; + secp256k1_fe_sqr(&zi2, zi); + secp256k1_fe_mul(&zi3, &zi2, zi); + secp256k1_fe_mul(&r->x, &a->x, &zi2); + secp256k1_fe_mul(&r->y, &a->y, &zi3); + r->infinity = a->infinity; +} + +static void secp256k1_ge_set_xy(secp256k1_ge *r, const secp256k1_fe *x, const secp256k1_fe *y) { + r->infinity = 0; + r->x = *x; + r->y = *y; +} + +static int secp256k1_ge_is_infinity(const secp256k1_ge *a) { + return a->infinity; +} + +static void secp256k1_ge_neg(secp256k1_ge *r, const secp256k1_ge *a) { + *r = *a; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + secp256k1_fe_inv(&a->z, &a->z); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_mul(&z3, &a->z, &z2); + secp256k1_fe_mul(&a->x, &a->x, &z2); + secp256k1_fe_mul(&a->y, &a->y, &z3); + secp256k1_fe_set_int(&a->z, 1); + r->x = a->x; + r->y = a->y; +} + +static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { + secp256k1_fe z2, z3; + r->infinity = a->infinity; + if (a->infinity) { + return; + } + secp256k1_fe_inv_var(&a->z, &a->z); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_mul(&z3, &a->z, &z2); + secp256k1_fe_mul(&a->x, &a->x, &z2); + secp256k1_fe_mul(&a->y, &a->y, &z3); + secp256k1_fe_set_int(&a->z, 1); + r->x = a->x; + r->y = a->y; +} + +static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len, const secp256k1_callback *cb) { + secp256k1_fe *az; + secp256k1_fe *azi; + size_t i; + size_t count = 0; + az = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * len); + for (i = 0; i < len; i++) { + if (!a[i].infinity) { + az[count++] = a[i].z; + } + } + + azi = (secp256k1_fe *)checked_malloc(cb, sizeof(secp256k1_fe) * count); + secp256k1_fe_inv_all_var(azi, az, count); + free(az); + + count = 0; + for (i = 0; i < len; i++) { + r[i].infinity = a[i].infinity; + if (!a[i].infinity) { + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &azi[count++]); + } + } + free(azi); +} + +static void secp256k1_ge_set_table_gej_var(secp256k1_ge *r, const secp256k1_gej *a, const secp256k1_fe *zr, size_t len) { + size_t i = len - 1; + secp256k1_fe zi; + + if (len > 0) { + /* Compute the inverse of the last z coordinate, and use it to compute the last affine output. */ + secp256k1_fe_inv(&zi, &a[i].z); + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + + /* Work out way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + secp256k1_fe_mul(&zi, &zi, &zr[i]); + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zi); + } + } +} + +static void secp256k1_ge_globalz_set_table_gej(size_t len, secp256k1_ge *r, secp256k1_fe *globalz, const secp256k1_gej *a, const secp256k1_fe *zr) { + size_t i = len - 1; + secp256k1_fe zs; + + if (len > 0) { + /* The z of the final point gives us the "global Z" for the table. */ + r[i].x = a[i].x; + r[i].y = a[i].y; + *globalz = a[i].z; + r[i].infinity = 0; + zs = zr[i]; + + /* Work our way backwards, using the z-ratios to scale the x/y values. */ + while (i > 0) { + if (i != len - 1) { + secp256k1_fe_mul(&zs, &zs, &zr[i]); + } + i--; + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &zs); + } + } +} + +static void secp256k1_gej_set_infinity(secp256k1_gej *r) { + r->infinity = 1; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_gej_clear(secp256k1_gej *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); + secp256k1_fe_clear(&r->z); +} + +static void secp256k1_ge_clear(secp256k1_ge *r) { + r->infinity = 0; + secp256k1_fe_clear(&r->x); + secp256k1_fe_clear(&r->y); +} + +static int secp256k1_ge_set_xquad(secp256k1_ge *r, const secp256k1_fe *x) { + secp256k1_fe x2, x3, c; + r->x = *x; + secp256k1_fe_sqr(&x2, x); + secp256k1_fe_mul(&x3, x, &x2); + r->infinity = 0; + secp256k1_fe_set_int(&c, CURVE_B); + secp256k1_fe_add(&c, &x3); + return secp256k1_fe_sqrt(&r->y, &c); +} + +static int secp256k1_ge_set_xo_var(secp256k1_ge *r, const secp256k1_fe *x, int odd) { + if (!secp256k1_ge_set_xquad(r, x)) { + return 0; + } + secp256k1_fe_normalize_var(&r->y); + if (secp256k1_fe_is_odd(&r->y) != odd) { + secp256k1_fe_negate(&r->y, &r->y, 1); + } + return 1; + +} + +static void secp256k1_gej_set_ge(secp256k1_gej *r, const secp256k1_ge *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + secp256k1_fe_set_int(&r->z, 1); +} + +static int secp256k1_gej_eq_x_var(const secp256k1_fe *x, const secp256k1_gej *a) { + secp256k1_fe r, r2; + VERIFY_CHECK(!a->infinity); + secp256k1_fe_sqr(&r, &a->z); secp256k1_fe_mul(&r, &r, x); + r2 = a->x; secp256k1_fe_normalize_weak(&r2); + return secp256k1_fe_equal_var(&r, &r2); +} + +static void secp256k1_gej_neg(secp256k1_gej *r, const secp256k1_gej *a) { + r->infinity = a->infinity; + r->x = a->x; + r->y = a->y; + r->z = a->z; + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_negate(&r->y, &r->y, 1); +} + +static int secp256k1_gej_is_infinity(const secp256k1_gej *a) { + return a->infinity; +} + +static int secp256k1_gej_is_valid_var(const secp256k1_gej *a) { + secp256k1_fe y2, x3, z2, z6; + if (a->infinity) { + return 0; + } + /** y^2 = x^3 + 7 + * (Y/Z^3)^2 = (X/Z^2)^3 + 7 + * Y^2 / Z^6 = X^3 / Z^6 + 7 + * Y^2 = X^3 + 7*Z^6 + */ + secp256k1_fe_sqr(&y2, &a->y); + secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); + secp256k1_fe_sqr(&z2, &a->z); + secp256k1_fe_sqr(&z6, &z2); secp256k1_fe_mul(&z6, &z6, &z2); + secp256k1_fe_mul_int(&z6, CURVE_B); + secp256k1_fe_add(&x3, &z6); + secp256k1_fe_normalize_weak(&x3); + return secp256k1_fe_equal_var(&y2, &x3); +} + +static int secp256k1_ge_is_valid_var(const secp256k1_ge *a) { + secp256k1_fe y2, x3, c; + if (a->infinity) { + return 0; + } + /* y^2 = x^3 + 7 */ + secp256k1_fe_sqr(&y2, &a->y); + secp256k1_fe_sqr(&x3, &a->x); secp256k1_fe_mul(&x3, &x3, &a->x); + secp256k1_fe_set_int(&c, CURVE_B); + secp256k1_fe_add(&x3, &c); + secp256k1_fe_normalize_weak(&x3); + return secp256k1_fe_equal_var(&y2, &x3); +} + +static void secp256k1_gej_double_var(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + /* Operations: 3 mul, 4 sqr, 0 normalize, 12 mul_int/add/negate. + * + * Note that there is an implementation described at + * https://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + * which trades a multiply for a square, but in practice this is actually slower, + * mainly because it requires more normalizations. + */ + secp256k1_fe t1,t2,t3,t4; + /** For secp256k1, 2Q is infinity if and only if Q is infinity. This is because if 2Q = infinity, + * Q must equal -Q, or that Q.y == -(Q.y), or Q.y is 0. For a point on y^2 = x^3 + 7 to have + * y=0, x^3 must be -7 mod p. However, -7 has no cube root mod p. + * + * Having said this, if this function receives a point on a sextic twist, e.g. by + * a fault attack, it is possible for y to be 0. This happens for y^2 = x^3 + 6, + * since -6 does have a cube root mod p. For this point, this function will not set + * the infinity flag even though the point doubles to infinity, and the result + * point will be gibberish (z = 0 but infinity = 0). + */ + r->infinity = a->infinity; + if (r->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + return; + } + + if (rzr != NULL) { + *rzr = a->y; + secp256k1_fe_normalize_weak(rzr); + secp256k1_fe_mul_int(rzr, 2); + } + + secp256k1_fe_mul(&r->z, &a->z, &a->y); + secp256k1_fe_mul_int(&r->z, 2); /* Z' = 2*Y*Z (2) */ + secp256k1_fe_sqr(&t1, &a->x); + secp256k1_fe_mul_int(&t1, 3); /* T1 = 3*X^2 (3) */ + secp256k1_fe_sqr(&t2, &t1); /* T2 = 9*X^4 (1) */ + secp256k1_fe_sqr(&t3, &a->y); + secp256k1_fe_mul_int(&t3, 2); /* T3 = 2*Y^2 (2) */ + secp256k1_fe_sqr(&t4, &t3); + secp256k1_fe_mul_int(&t4, 2); /* T4 = 8*Y^4 (2) */ + secp256k1_fe_mul(&t3, &t3, &a->x); /* T3 = 2*X*Y^2 (1) */ + r->x = t3; + secp256k1_fe_mul_int(&r->x, 4); /* X' = 8*X*Y^2 (4) */ + secp256k1_fe_negate(&r->x, &r->x, 4); /* X' = -8*X*Y^2 (5) */ + secp256k1_fe_add(&r->x, &t2); /* X' = 9*X^4 - 8*X*Y^2 (6) */ + secp256k1_fe_negate(&t2, &t2, 1); /* T2 = -9*X^4 (2) */ + secp256k1_fe_mul_int(&t3, 6); /* T3 = 12*X*Y^2 (6) */ + secp256k1_fe_add(&t3, &t2); /* T3 = 12*X*Y^2 - 9*X^4 (8) */ + secp256k1_fe_mul(&r->y, &t1, &t3); /* Y' = 36*X^3*Y^2 - 27*X^6 (1) */ + secp256k1_fe_negate(&t2, &t4, 2); /* T2 = -8*Y^4 (3) */ + secp256k1_fe_add(&r->y, &t2); /* Y' = 36*X^3*Y^2 - 27*X^6 - 8*Y^4 (4) */ +} + +static SECP256K1_INLINE void secp256k1_gej_double_nonzero(secp256k1_gej *r, const secp256k1_gej *a, secp256k1_fe *rzr) { + VERIFY_CHECK(!secp256k1_gej_is_infinity(a)); + secp256k1_gej_double_var(r, a, rzr); +} + +static void secp256k1_gej_add_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_gej *b, secp256k1_fe *rzr) { + /* Operations: 12 mul, 4 sqr, 2 normalize, 12 mul_int/add/negate */ + secp256k1_fe z22, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + *r = *b; + return; + } + + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + + r->infinity = 0; + secp256k1_fe_sqr(&z22, &b->z); + secp256k1_fe_sqr(&z12, &a->z); + secp256k1_fe_mul(&u1, &a->x, &z22); + secp256k1_fe_mul(&u2, &b->x, &z12); + secp256k1_fe_mul(&s1, &a->y, &z22); secp256k1_fe_mul(&s1, &s1, &b->z); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + secp256k1_fe_mul(&h, &h, &b->z); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + +static void secp256k1_gej_add_ge_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, secp256k1_fe *rzr) { + /* 8 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ + secp256k1_fe z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + if (a->infinity) { + VERIFY_CHECK(rzr == NULL); + secp256k1_gej_set_ge(r, b); + return; + } + if (b->infinity) { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 1); + } + *r = *a; + return; + } + r->infinity = 0; + + secp256k1_fe_sqr(&z12, &a->z); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &a->z); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, rzr); + } else { + if (rzr != NULL) { + secp256k1_fe_set_int(rzr, 0); + } + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + if (rzr != NULL) { + *rzr = h; + } + secp256k1_fe_mul(&r->z, &a->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + +static void secp256k1_gej_add_zinv_var(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b, const secp256k1_fe *bzinv) { + /* 9 mul, 3 sqr, 4 normalize, 12 mul_int/add/negate */ + secp256k1_fe az, z12, u1, u2, s1, s2, h, i, i2, h2, h3, t; + + if (b->infinity) { + *r = *a; + return; + } + if (a->infinity) { + secp256k1_fe bzinv2, bzinv3; + r->infinity = b->infinity; + secp256k1_fe_sqr(&bzinv2, bzinv); + secp256k1_fe_mul(&bzinv3, &bzinv2, bzinv); + secp256k1_fe_mul(&r->x, &b->x, &bzinv2); + secp256k1_fe_mul(&r->y, &b->y, &bzinv3); + secp256k1_fe_set_int(&r->z, 1); + return; + } + r->infinity = 0; + + /** We need to calculate (rx,ry,rz) = (ax,ay,az) + (bx,by,1/bzinv). Due to + * secp256k1's isomorphism we can multiply the Z coordinates on both sides + * by bzinv, and get: (rx,ry,rz*bzinv) = (ax,ay,az*bzinv) + (bx,by,1). + * This means that (rx,ry,rz) can be calculated as + * (ax,ay,az*bzinv) + (bx,by,1), when not applying the bzinv factor to rz. + * The variable az below holds the modified Z coordinate for a, which is used + * for the computation of rx and ry, but not for rz. + */ + secp256k1_fe_mul(&az, &a->z, bzinv); + + secp256k1_fe_sqr(&z12, &az); + u1 = a->x; secp256k1_fe_normalize_weak(&u1); + secp256k1_fe_mul(&u2, &b->x, &z12); + s1 = a->y; secp256k1_fe_normalize_weak(&s1); + secp256k1_fe_mul(&s2, &b->y, &z12); secp256k1_fe_mul(&s2, &s2, &az); + secp256k1_fe_negate(&h, &u1, 1); secp256k1_fe_add(&h, &u2); + secp256k1_fe_negate(&i, &s1, 1); secp256k1_fe_add(&i, &s2); + if (secp256k1_fe_normalizes_to_zero_var(&h)) { + if (secp256k1_fe_normalizes_to_zero_var(&i)) { + secp256k1_gej_double_var(r, a, NULL); + } else { + r->infinity = 1; + } + return; + } + secp256k1_fe_sqr(&i2, &i); + secp256k1_fe_sqr(&h2, &h); + secp256k1_fe_mul(&h3, &h, &h2); + r->z = a->z; secp256k1_fe_mul(&r->z, &r->z, &h); + secp256k1_fe_mul(&t, &u1, &h2); + r->x = t; secp256k1_fe_mul_int(&r->x, 2); secp256k1_fe_add(&r->x, &h3); secp256k1_fe_negate(&r->x, &r->x, 3); secp256k1_fe_add(&r->x, &i2); + secp256k1_fe_negate(&r->y, &r->x, 5); secp256k1_fe_add(&r->y, &t); secp256k1_fe_mul(&r->y, &r->y, &i); + secp256k1_fe_mul(&h3, &h3, &s1); secp256k1_fe_negate(&h3, &h3, 1); + secp256k1_fe_add(&r->y, &h3); +} + + +static void secp256k1_gej_add_ge(secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_ge *b) { + /* Operations: 7 mul, 5 sqr, 4 normalize, 21 mul_int/add/negate/cmov */ + static const secp256k1_fe fe_1 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_fe zz, u1, u2, s1, s2, t, tt, m, n, q, rr; + secp256k1_fe m_alt, rr_alt; + int infinity, degenerate; + VERIFY_CHECK(!b->infinity); + VERIFY_CHECK(a->infinity == 0 || a->infinity == 1); + + /** In: + * Eric Brier and Marc Joye, Weierstrass Elliptic Curves and Side-Channel Attacks. + * In D. Naccache and P. Paillier, Eds., Public Key Cryptography, vol. 2274 of Lecture Notes in Computer Science, pages 335-345. Springer-Verlag, 2002. + * we find as solution for a unified addition/doubling formula: + * lambda = ((x1 + x2)^2 - x1 * x2 + a) / (y1 + y2), with a = 0 for secp256k1's curve equation. + * x3 = lambda^2 - (x1 + x2) + * 2*y3 = lambda * (x1 + x2 - 2 * x3) - (y1 + y2). + * + * Substituting x_i = Xi / Zi^2 and yi = Yi / Zi^3, for i=1,2,3, gives: + * U1 = X1*Z2^2, U2 = X2*Z1^2 + * S1 = Y1*Z2^3, S2 = Y2*Z1^3 + * Z = Z1*Z2 + * T = U1+U2 + * M = S1+S2 + * Q = T*M^2 + * R = T^2-U1*U2 + * X3 = 4*(R^2-Q) + * Y3 = 4*(R*(3*Q-2*R^2)-M^4) + * Z3 = 2*M*Z + * (Note that the paper uses xi = Xi / Zi and yi = Yi / Zi instead.) + * + * This formula has the benefit of being the same for both addition + * of distinct points and doubling. However, it breaks down in the + * case that either point is infinity, or that y1 = -y2. We handle + * these cases in the following ways: + * + * - If b is infinity we simply bail by means of a VERIFY_CHECK. + * + * - If a is infinity, we detect this, and at the end of the + * computation replace the result (which will be meaningless, + * but we compute to be constant-time) with b.x : b.y : 1. + * + * - If a = -b, we have y1 = -y2, which is a degenerate case. + * But here the answer is infinity, so we simply set the + * infinity flag of the result, overriding the computed values + * without even needing to cmov. + * + * - If y1 = -y2 but x1 != x2, which does occur thanks to certain + * properties of our curve (specifically, 1 has nontrivial cube + * roots in our field, and the curve equation has no x coefficient) + * then the answer is not infinity but also not given by the above + * equation. In this case, we cmov in place an alternate expression + * for lambda. Specifically (y1 - y2)/(x1 - x2). Where both these + * expressions for lambda are defined, they are equal, and can be + * obtained from each other by multiplication by (y1 + y2)/(y1 + y2) + * then substitution of x^3 + 7 for y^2 (using the curve equation). + * For all pairs of nonzero points (a, b) at least one is defined, + * so this covers everything. + */ + + secp256k1_fe_sqr(&zz, &a->z); /* z = Z1^2 */ + u1 = a->x; secp256k1_fe_normalize_weak(&u1); /* u1 = U1 = X1*Z2^2 (1) */ + secp256k1_fe_mul(&u2, &b->x, &zz); /* u2 = U2 = X2*Z1^2 (1) */ + s1 = a->y; secp256k1_fe_normalize_weak(&s1); /* s1 = S1 = Y1*Z2^3 (1) */ + secp256k1_fe_mul(&s2, &b->y, &zz); /* s2 = Y2*Z1^2 (1) */ + secp256k1_fe_mul(&s2, &s2, &a->z); /* s2 = S2 = Y2*Z1^3 (1) */ + t = u1; secp256k1_fe_add(&t, &u2); /* t = T = U1+U2 (2) */ + m = s1; secp256k1_fe_add(&m, &s2); /* m = M = S1+S2 (2) */ + secp256k1_fe_sqr(&rr, &t); /* rr = T^2 (1) */ + secp256k1_fe_negate(&m_alt, &u2, 1); /* Malt = -X2*Z1^2 */ + secp256k1_fe_mul(&tt, &u1, &m_alt); /* tt = -U1*U2 (2) */ + secp256k1_fe_add(&rr, &tt); /* rr = R = T^2-U1*U2 (3) */ + /** If lambda = R/M = 0/0 we have a problem (except in the "trivial" + * case that Z = z1z2 = 0, and this is special-cased later on). */ + degenerate = secp256k1_fe_normalizes_to_zero(&m) & + secp256k1_fe_normalizes_to_zero(&rr); + /* This only occurs when y1 == -y2 and x1^3 == x2^3, but x1 != x2. + * This means either x1 == beta*x2 or beta*x1 == x2, where beta is + * a nontrivial cube root of one. In either case, an alternate + * non-indeterminate expression for lambda is (y1 - y2)/(x1 - x2), + * so we set R/M equal to this. */ + rr_alt = s1; + secp256k1_fe_mul_int(&rr_alt, 2); /* rr = Y1*Z2^3 - Y2*Z1^3 (2) */ + secp256k1_fe_add(&m_alt, &u1); /* Malt = X1*Z2^2 - X2*Z1^2 */ + + secp256k1_fe_cmov(&rr_alt, &rr, !degenerate); + secp256k1_fe_cmov(&m_alt, &m, !degenerate); + /* Now Ralt / Malt = lambda and is guaranteed not to be 0/0. + * From here on out Ralt and Malt represent the numerator + * and denominator of lambda; R and M represent the explicit + * expressions x1^2 + x2^2 + x1x2 and y1 + y2. */ + secp256k1_fe_sqr(&n, &m_alt); /* n = Malt^2 (1) */ + secp256k1_fe_mul(&q, &n, &t); /* q = Q = T*Malt^2 (1) */ + /* These two lines use the observation that either M == Malt or M == 0, + * so M^3 * Malt is either Malt^4 (which is computed by squaring), or + * zero (which is "computed" by cmov). So the cost is one squaring + * versus two multiplications. */ + secp256k1_fe_sqr(&n, &n); + secp256k1_fe_cmov(&n, &m, degenerate); /* n = M^3 * Malt (2) */ + secp256k1_fe_sqr(&t, &rr_alt); /* t = Ralt^2 (1) */ + secp256k1_fe_mul(&r->z, &a->z, &m_alt); /* r->z = Malt*Z (1) */ + infinity = secp256k1_fe_normalizes_to_zero(&r->z) * (1 - a->infinity); + secp256k1_fe_mul_int(&r->z, 2); /* r->z = Z3 = 2*Malt*Z (2) */ + secp256k1_fe_negate(&q, &q, 1); /* q = -Q (2) */ + secp256k1_fe_add(&t, &q); /* t = Ralt^2-Q (3) */ + secp256k1_fe_normalize_weak(&t); + r->x = t; /* r->x = Ralt^2-Q (1) */ + secp256k1_fe_mul_int(&t, 2); /* t = 2*x3 (2) */ + secp256k1_fe_add(&t, &q); /* t = 2*x3 - Q: (4) */ + secp256k1_fe_mul(&t, &t, &rr_alt); /* t = Ralt*(2*x3 - Q) (1) */ + secp256k1_fe_add(&t, &n); /* t = Ralt*(2*x3 - Q) + M^3*Malt (3) */ + secp256k1_fe_negate(&r->y, &t, 3); /* r->y = Ralt*(Q - 2x3) - M^3*Malt (4) */ + secp256k1_fe_normalize_weak(&r->y); + secp256k1_fe_mul_int(&r->x, 4); /* r->x = X3 = 4*(Ralt^2-Q) */ + secp256k1_fe_mul_int(&r->y, 4); /* r->y = Y3 = 4*Ralt*(Q - 2x3) - 4*M^3*Malt (4) */ + + /** In case a->infinity == 1, replace r with (b->x, b->y, 1). */ + secp256k1_fe_cmov(&r->x, &b->x, a->infinity); + secp256k1_fe_cmov(&r->y, &b->y, a->infinity); + secp256k1_fe_cmov(&r->z, &fe_1, a->infinity); + r->infinity = infinity; +} + +static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *s) { + /* Operations: 4 mul, 1 sqr */ + secp256k1_fe zz; + VERIFY_CHECK(!secp256k1_fe_is_zero(s)); + secp256k1_fe_sqr(&zz, s); + secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ + secp256k1_fe_mul(&r->y, &r->y, &zz); + secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ + secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ +} + +static void secp256k1_ge_to_storage(secp256k1_ge_storage *r, const secp256k1_ge *a) { + secp256k1_fe x, y; + VERIFY_CHECK(!a->infinity); + x = a->x; + secp256k1_fe_normalize(&x); + y = a->y; + secp256k1_fe_normalize(&y); + secp256k1_fe_to_storage(&r->x, &x); + secp256k1_fe_to_storage(&r->y, &y); +} + +static void secp256k1_ge_from_storage(secp256k1_ge *r, const secp256k1_ge_storage *a) { + secp256k1_fe_from_storage(&r->x, &a->x); + secp256k1_fe_from_storage(&r->y, &a->y); + r->infinity = 0; +} + +static SECP256K1_INLINE void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_ge_storage *a, int flag) { + secp256k1_fe_storage_cmov(&r->x, &a->x, flag); + secp256k1_fe_storage_cmov(&r->y, &a->y, flag); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_ge_mul_lambda(secp256k1_ge *r, const secp256k1_ge *a) { + static const secp256k1_fe beta = SECP256K1_FE_CONST( + 0x7ae96a2bul, 0x657c0710ul, 0x6e64479eul, 0xac3434e9ul, + 0x9cf04975ul, 0x12f58995ul, 0xc1396c28ul, 0x719501eeul + ); + *r = *a; + secp256k1_fe_mul(&r->x, &r->x, &beta); +} +#endif + +static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { + secp256k1_fe yz; + + if (a->infinity) { + return 0; + } + + /* We rely on the fact that the Jacobi symbol of 1 / a->z^3 is the same as + * that of a->z. Thus a->y / a->z^3 is a quadratic residue iff a->y * a->z + is */ + secp256k1_fe_mul(&yz, &a->y, &a->z); + return secp256k1_fe_is_quad_var(&yz); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h new file mode 100644 index 000000000..fca98cab9 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash.h @@ -0,0 +1,41 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_HASH_ +#define _SECP256K1_HASH_ + +#include +#include + +typedef struct { + uint32_t s[8]; + uint32_t buf[16]; /* In big endian */ + size_t bytes; +} secp256k1_sha256_t; + +static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash); +static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t size); +static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32); + +typedef struct { + secp256k1_sha256_t inner, outer; +} secp256k1_hmac_sha256_t; + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t size); +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size); +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32); + +typedef struct { + unsigned char v[32]; + unsigned char k[32]; + int retry; +} secp256k1_rfc6979_hmac_sha256_t; + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen); +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen); +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h new file mode 100644 index 000000000..b47e65f83 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/hash_impl.h @@ -0,0 +1,281 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_HASH_IMPL_H_ +#define _SECP256K1_HASH_IMPL_H_ + +#include "hash.h" + +#include +#include +#include + +#define Ch(x,y,z) ((z) ^ ((x) & ((y) ^ (z)))) +#define Maj(x,y,z) (((x) & (y)) | ((z) & ((x) | (y)))) +#define Sigma0(x) (((x) >> 2 | (x) << 30) ^ ((x) >> 13 | (x) << 19) ^ ((x) >> 22 | (x) << 10)) +#define Sigma1(x) (((x) >> 6 | (x) << 26) ^ ((x) >> 11 | (x) << 21) ^ ((x) >> 25 | (x) << 7)) +#define sigma0(x) (((x) >> 7 | (x) << 25) ^ ((x) >> 18 | (x) << 14) ^ ((x) >> 3)) +#define sigma1(x) (((x) >> 17 | (x) << 15) ^ ((x) >> 19 | (x) << 13) ^ ((x) >> 10)) + +#define Round(a,b,c,d,e,f,g,h,k,w) do { \ + uint32_t t1 = (h) + Sigma1(e) + Ch((e), (f), (g)) + (k) + (w); \ + uint32_t t2 = Sigma0(a) + Maj((a), (b), (c)); \ + (d) += t1; \ + (h) = t1 + t2; \ +} while(0) + +#ifdef WORDS_BIGENDIAN +#define BE32(x) (x) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#endif + +static void secp256k1_sha256_initialize(secp256k1_sha256_t *hash) { + hash->s[0] = 0x6a09e667ul; + hash->s[1] = 0xbb67ae85ul; + hash->s[2] = 0x3c6ef372ul; + hash->s[3] = 0xa54ff53aul; + hash->s[4] = 0x510e527ful; + hash->s[5] = 0x9b05688cul; + hash->s[6] = 0x1f83d9abul; + hash->s[7] = 0x5be0cd19ul; + hash->bytes = 0; +} + +/** Perform one SHA-256 transformation, processing 16 big endian 32-bit words. */ +static void secp256k1_sha256_transform(uint32_t* s, const uint32_t* chunk) { + uint32_t a = s[0], b = s[1], c = s[2], d = s[3], e = s[4], f = s[5], g = s[6], h = s[7]; + uint32_t w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15; + + Round(a, b, c, d, e, f, g, h, 0x428a2f98, w0 = BE32(chunk[0])); + Round(h, a, b, c, d, e, f, g, 0x71374491, w1 = BE32(chunk[1])); + Round(g, h, a, b, c, d, e, f, 0xb5c0fbcf, w2 = BE32(chunk[2])); + Round(f, g, h, a, b, c, d, e, 0xe9b5dba5, w3 = BE32(chunk[3])); + Round(e, f, g, h, a, b, c, d, 0x3956c25b, w4 = BE32(chunk[4])); + Round(d, e, f, g, h, a, b, c, 0x59f111f1, w5 = BE32(chunk[5])); + Round(c, d, e, f, g, h, a, b, 0x923f82a4, w6 = BE32(chunk[6])); + Round(b, c, d, e, f, g, h, a, 0xab1c5ed5, w7 = BE32(chunk[7])); + Round(a, b, c, d, e, f, g, h, 0xd807aa98, w8 = BE32(chunk[8])); + Round(h, a, b, c, d, e, f, g, 0x12835b01, w9 = BE32(chunk[9])); + Round(g, h, a, b, c, d, e, f, 0x243185be, w10 = BE32(chunk[10])); + Round(f, g, h, a, b, c, d, e, 0x550c7dc3, w11 = BE32(chunk[11])); + Round(e, f, g, h, a, b, c, d, 0x72be5d74, w12 = BE32(chunk[12])); + Round(d, e, f, g, h, a, b, c, 0x80deb1fe, w13 = BE32(chunk[13])); + Round(c, d, e, f, g, h, a, b, 0x9bdc06a7, w14 = BE32(chunk[14])); + Round(b, c, d, e, f, g, h, a, 0xc19bf174, w15 = BE32(chunk[15])); + + Round(a, b, c, d, e, f, g, h, 0xe49b69c1, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0xefbe4786, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x0fc19dc6, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x240ca1cc, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x2de92c6f, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4a7484aa, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5cb0a9dc, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x76f988da, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x983e5152, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa831c66d, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xb00327c8, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xbf597fc7, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xc6e00bf3, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd5a79147, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0x06ca6351, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x14292967, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x27b70a85, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x2e1b2138, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x4d2c6dfc, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x53380d13, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x650a7354, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x766a0abb, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x81c2c92e, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x92722c85, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0xa2bfe8a1, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0xa81a664b, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0xc24b8b70, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0xc76c51a3, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0xd192e819, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xd6990624, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xf40e3585, w14 += sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0x106aa070, w15 += sigma1(w13) + w8 + sigma0(w0)); + + Round(a, b, c, d, e, f, g, h, 0x19a4c116, w0 += sigma1(w14) + w9 + sigma0(w1)); + Round(h, a, b, c, d, e, f, g, 0x1e376c08, w1 += sigma1(w15) + w10 + sigma0(w2)); + Round(g, h, a, b, c, d, e, f, 0x2748774c, w2 += sigma1(w0) + w11 + sigma0(w3)); + Round(f, g, h, a, b, c, d, e, 0x34b0bcb5, w3 += sigma1(w1) + w12 + sigma0(w4)); + Round(e, f, g, h, a, b, c, d, 0x391c0cb3, w4 += sigma1(w2) + w13 + sigma0(w5)); + Round(d, e, f, g, h, a, b, c, 0x4ed8aa4a, w5 += sigma1(w3) + w14 + sigma0(w6)); + Round(c, d, e, f, g, h, a, b, 0x5b9cca4f, w6 += sigma1(w4) + w15 + sigma0(w7)); + Round(b, c, d, e, f, g, h, a, 0x682e6ff3, w7 += sigma1(w5) + w0 + sigma0(w8)); + Round(a, b, c, d, e, f, g, h, 0x748f82ee, w8 += sigma1(w6) + w1 + sigma0(w9)); + Round(h, a, b, c, d, e, f, g, 0x78a5636f, w9 += sigma1(w7) + w2 + sigma0(w10)); + Round(g, h, a, b, c, d, e, f, 0x84c87814, w10 += sigma1(w8) + w3 + sigma0(w11)); + Round(f, g, h, a, b, c, d, e, 0x8cc70208, w11 += sigma1(w9) + w4 + sigma0(w12)); + Round(e, f, g, h, a, b, c, d, 0x90befffa, w12 += sigma1(w10) + w5 + sigma0(w13)); + Round(d, e, f, g, h, a, b, c, 0xa4506ceb, w13 += sigma1(w11) + w6 + sigma0(w14)); + Round(c, d, e, f, g, h, a, b, 0xbef9a3f7, w14 + sigma1(w12) + w7 + sigma0(w15)); + Round(b, c, d, e, f, g, h, a, 0xc67178f2, w15 + sigma1(w13) + w8 + sigma0(w0)); + + s[0] += a; + s[1] += b; + s[2] += c; + s[3] += d; + s[4] += e; + s[5] += f; + s[6] += g; + s[7] += h; +} + +static void secp256k1_sha256_write(secp256k1_sha256_t *hash, const unsigned char *data, size_t len) { + size_t bufsize = hash->bytes & 0x3F; + hash->bytes += len; + while (bufsize + len >= 64) { + /* Fill the buffer, and process it. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, 64 - bufsize); + data += 64 - bufsize; + len -= 64 - bufsize; + secp256k1_sha256_transform(hash->s, hash->buf); + bufsize = 0; + } + if (len) { + /* Fill the buffer with what remains. */ + memcpy(((unsigned char*)hash->buf) + bufsize, data, len); + } +} + +static void secp256k1_sha256_finalize(secp256k1_sha256_t *hash, unsigned char *out32) { + static const unsigned char pad[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + uint32_t sizedesc[2]; + uint32_t out[8]; + int i = 0; + sizedesc[0] = BE32(hash->bytes >> 29); + sizedesc[1] = BE32(hash->bytes << 3); + secp256k1_sha256_write(hash, pad, 1 + ((119 - (hash->bytes % 64)) % 64)); + secp256k1_sha256_write(hash, (const unsigned char*)sizedesc, 8); + for (i = 0; i < 8; i++) { + out[i] = BE32(hash->s[i]); + hash->s[i] = 0; + } + memcpy(out32, (const unsigned char*)out, 32); +} + +static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256_t *hash, const unsigned char *key, size_t keylen) { + int n; + unsigned char rkey[64]; + if (keylen <= 64) { + memcpy(rkey, key, keylen); + memset(rkey + keylen, 0, 64 - keylen); + } else { + secp256k1_sha256_t sha256; + secp256k1_sha256_initialize(&sha256); + secp256k1_sha256_write(&sha256, key, keylen); + secp256k1_sha256_finalize(&sha256, rkey); + memset(rkey + 32, 0, 32); + } + + secp256k1_sha256_initialize(&hash->outer); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c; + } + secp256k1_sha256_write(&hash->outer, rkey, 64); + + secp256k1_sha256_initialize(&hash->inner); + for (n = 0; n < 64; n++) { + rkey[n] ^= 0x5c ^ 0x36; + } + secp256k1_sha256_write(&hash->inner, rkey, 64); + memset(rkey, 0, 64); +} + +static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256_t *hash, const unsigned char *data, size_t size) { + secp256k1_sha256_write(&hash->inner, data, size); +} + +static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256_t *hash, unsigned char *out32) { + unsigned char temp[32]; + secp256k1_sha256_finalize(&hash->inner, temp); + secp256k1_sha256_write(&hash->outer, temp, 32); + memset(temp, 0, 32); + secp256k1_sha256_finalize(&hash->outer, out32); +} + + +static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256_t *rng, const unsigned char *key, size_t keylen) { + secp256k1_hmac_sha256_t hmac; + static const unsigned char zero[1] = {0x00}; + static const unsigned char one[1] = {0x01}; + + memset(rng->v, 0x01, 32); /* RFC6979 3.2.b. */ + memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */ + + /* RFC6979 3.2.d. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + + /* RFC6979 3.2.f. */ + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, one, 1); + secp256k1_hmac_sha256_write(&hmac, key, keylen); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + rng->retry = 0; +} + +static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256_t *rng, unsigned char *out, size_t outlen) { + /* RFC6979 3.2.h. */ + static const unsigned char zero[1] = {0x00}; + if (rng->retry) { + secp256k1_hmac_sha256_t hmac; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_write(&hmac, zero, 1); + secp256k1_hmac_sha256_finalize(&hmac, rng->k); + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + } + + while (outlen > 0) { + secp256k1_hmac_sha256_t hmac; + int now = outlen; + secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32); + secp256k1_hmac_sha256_write(&hmac, rng->v, 32); + secp256k1_hmac_sha256_finalize(&hmac, rng->v); + if (now > 32) { + now = 32; + } + memcpy(out, rng->v, now); + out += now; + outlen -= now; + } + + rng->retry = 1; +} + +static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256_t *rng) { + memset(rng->k, 0, 32); + memset(rng->v, 0, 32); + rng->retry = 0; +} + +#undef BE32 +#undef Round +#undef sigma1 +#undef sigma0 +#undef Sigma1 +#undef Sigma0 +#undef Maj +#undef Ch + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java new file mode 100644 index 000000000..1c67802fb --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1.java @@ -0,0 +1,446 @@ +/* + * Copyright 2013 Google Inc. + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +import java.math.BigInteger; +import com.google.common.base.Preconditions; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + *

This class holds native methods to handle ECDSA verification.

+ * + *

You can find an example library that can be used for this at https://github.com/bitcoin/secp256k1

+ * + *

To build secp256k1 for use with bitcoinj, run + * `./configure --enable-jni --enable-experimental --enable-module-ecdh` + * and `make` then copy `.libs/libsecp256k1.so` to your system library path + * or point the JVM to the folder containing it with -Djava.library.path + *

+ */ +public class NativeSecp256k1 { + + private static final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock(); + private static final Lock r = rwl.readLock(); + private static final Lock w = rwl.writeLock(); + private static ThreadLocal nativeECDSABuffer = new ThreadLocal(); + /** + * Verifies the given secp256k1 signature in native code. + * Calling when enabled == false is undefined (probably library not loaded) + * + * @param data The data which was signed, must be exactly 32 bytes + * @param signature The signature + * @param pub The public key which did the signing + */ + public static boolean verify(byte[] data, byte[] signature, byte[] pub) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && signature.length <= 520 && pub.length <= 520); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 520) { + byteBuff = ByteBuffer.allocateDirect(520); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(signature); + byteBuff.put(pub); + + byte[][] retByteArray; + + r.lock(); + try { + return secp256k1_ecdsa_verify(byteBuff, Secp256k1Context.getContext(), signature.length, pub.length) == 1; + } finally { + r.unlock(); + } + } + + /** + * libsecp256k1 Create an ECDSA signature. + * + * @param data Message hash, 32 bytes + * @param key Secret key, 32 bytes + * + * Return values + * @param sig byte array of signature + */ + public static byte[] sign(byte[] data, byte[] sec) throws AssertFailException{ + Preconditions.checkArgument(data.length == 32 && sec.length <= 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + 32) { + byteBuff = ByteBuffer.allocateDirect(32 + 32); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(data); + byteBuff.put(sec); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ecdsa_sign(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] sigArr = retByteArray[0]; + int sigLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(sigArr.length, sigLen, "Got bad signature length."); + + return retVal == 0 ? new byte[0] : sigArr; + } + + /** + * libsecp256k1 Seckey Verify - returns 1 if valid, 0 if invalid + * + * @param seckey ECDSA Secret key, 32 bytes + */ + public static boolean secKeyVerify(byte[] seckey) { + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + r.lock(); + try { + return secp256k1_ec_seckey_verify(byteBuff,Secp256k1Context.getContext()) == 1; + } finally { + r.unlock(); + } + } + + + /** + * libsecp256k1 Compute Pubkey - computes public key from secret key + * + * @param seckey ECDSA Secret key, 32 bytes + * + * Return values + * @param pubkey ECDSA Public key, 33 or 65 bytes + */ + //TODO add a 'compressed' arg + public static byte[] computePubkey(byte[] seckey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seckey.length) { + byteBuff = ByteBuffer.allocateDirect(seckey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + + byte[][] retByteArray; + + r.lock(); + try { + retByteArray = secp256k1_ec_pubkey_create(byteBuff, Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + int pubLen = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + return retVal == 0 ? new byte[0]: pubArr; + } + + /** + * libsecp256k1 Cleanup - This destroys the secp256k1 context object + * This should be called at the end of the program for proper cleanup of the context. + */ + public static synchronized void cleanup() { + w.lock(); + try { + secp256k1_destroy_context(Secp256k1Context.getContext()); + } finally { + w.unlock(); + } + } + + public static long cloneContext() { + r.lock(); + try { + return secp256k1_ctx_clone(Secp256k1Context.getContext()); + } finally { r.unlock(); } + } + + /** + * libsecp256k1 PrivKey Tweak-Mul - Tweak privkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakMul(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_mul(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PrivKey Tweak-Add - Tweak privkey by adding to it + * + * @param tweak some bytes to tweak with + * @param seckey 32-byte seckey + */ + public static byte[] privKeyTweakAdd(byte[] privkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(privkey.length == 32); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < privkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(privkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(privkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_privkey_tweak_add(byteBuff,Secp256k1Context.getContext()); + } finally { + r.unlock(); + } + + byte[] privArr = retByteArray[0]; + + int privLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(privArr.length, privLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return privArr; + } + + /** + * libsecp256k1 PubKey Tweak-Add - Tweak pubkey by adding to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakAdd(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_add(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 PubKey Tweak-Mul - Tweak pubkey by multiplying to it + * + * @param tweak some bytes to tweak with + * @param pubkey 32-byte seckey + */ + public static byte[] pubKeyTweakMul(byte[] pubkey, byte[] tweak) throws AssertFailException{ + Preconditions.checkArgument(pubkey.length == 33 || pubkey.length == 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < pubkey.length + tweak.length) { + byteBuff = ByteBuffer.allocateDirect(pubkey.length + tweak.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(pubkey); + byteBuff.put(tweak); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_pubkey_tweak_mul(byteBuff,Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] pubArr = retByteArray[0]; + + int pubLen = (byte) new BigInteger(new byte[] { retByteArray[1][0] }).intValue() & 0xFF; + int retVal = new BigInteger(new byte[] { retByteArray[1][1] }).intValue(); + + assertEquals(pubArr.length, pubLen, "Got bad pubkey length."); + + assertEquals(retVal, 1, "Failed return value check."); + + return pubArr; + } + + /** + * libsecp256k1 create ECDH secret - constant time ECDH calculation + * + * @param seckey byte array of secret key used in exponentiaion + * @param pubkey byte array of public key used in exponentiaion + */ + public static byte[] createECDHSecret(byte[] seckey, byte[] pubkey) throws AssertFailException{ + Preconditions.checkArgument(seckey.length <= 32 && pubkey.length <= 65); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < 32 + pubkey.length) { + byteBuff = ByteBuffer.allocateDirect(32 + pubkey.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seckey); + byteBuff.put(pubkey); + + byte[][] retByteArray; + r.lock(); + try { + retByteArray = secp256k1_ecdh(byteBuff, Secp256k1Context.getContext(), pubkey.length); + } finally { + r.unlock(); + } + + byte[] resArr = retByteArray[0]; + int retVal = new BigInteger(new byte[] { retByteArray[1][0] }).intValue(); + + assertEquals(resArr.length, 32, "Got bad result length."); + assertEquals(retVal, 1, "Failed return value check."); + + return resArr; + } + + /** + * libsecp256k1 randomize - updates the context randomization + * + * @param seed 32-byte random seed + */ + public static synchronized boolean randomize(byte[] seed) throws AssertFailException{ + Preconditions.checkArgument(seed.length == 32 || seed == null); + + ByteBuffer byteBuff = nativeECDSABuffer.get(); + if (byteBuff == null || byteBuff.capacity() < seed.length) { + byteBuff = ByteBuffer.allocateDirect(seed.length); + byteBuff.order(ByteOrder.nativeOrder()); + nativeECDSABuffer.set(byteBuff); + } + byteBuff.rewind(); + byteBuff.put(seed); + + w.lock(); + try { + return secp256k1_context_randomize(byteBuff, Secp256k1Context.getContext()) == 1; + } finally { + w.unlock(); + } + } + + private static native long secp256k1_ctx_clone(long context); + + private static native int secp256k1_context_randomize(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_add(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_privkey_tweak_mul(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_pubkey_tweak_add(ByteBuffer byteBuff, long context, int pubLen); + + private static native byte[][] secp256k1_pubkey_tweak_mul(ByteBuffer byteBuff, long context, int pubLen); + + private static native void secp256k1_destroy_context(long context); + + private static native int secp256k1_ecdsa_verify(ByteBuffer byteBuff, long context, int sigLen, int pubLen); + + private static native byte[][] secp256k1_ecdsa_sign(ByteBuffer byteBuff, long context); + + private static native int secp256k1_ec_seckey_verify(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_create(ByteBuffer byteBuff, long context); + + private static native byte[][] secp256k1_ec_pubkey_parse(ByteBuffer byteBuff, long context, int inputLen); + + private static native byte[][] secp256k1_ecdh(ByteBuffer byteBuff, long context, int inputLen); + +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java new file mode 100644 index 000000000..c00d08899 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Test.java @@ -0,0 +1,226 @@ +package org.bitcoin; + +import com.google.common.io.BaseEncoding; +import java.util.Arrays; +import java.math.BigInteger; +import javax.xml.bind.DatatypeConverter; +import static org.bitcoin.NativeSecp256k1Util.*; + +/** + * This class holds test cases defined for testing this library. + */ +public class NativeSecp256k1Test { + + //TODO improve comments/add more tests + /** + * This tests verify() for a valid signature + */ + public static void testVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + assertEquals( result, true , "testVerifyPos"); + } + + /** + * This tests verify() for a non-valid signature + */ + public static void testVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A91".toLowerCase()); //sha256hash of "testing" + byte[] sig = BaseEncoding.base16().lowerCase().decode("3044022079BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F817980220294F14E883B3F525B5367756C2A11EF6CF84B730B36C17CB0C56F0AAB2C98589".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + result = NativeSecp256k1.verify( data, sig, pub); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testVerifyNeg"); + } + + /** + * This tests secret key verify() for a valid secretkey + */ + public static void testSecKeyVerifyPos() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, true , "testSecKeyVerifyPos"); + } + + /** + * This tests secret key verify() for a invalid secretkey + */ + public static void testSecKeyVerifyNeg() throws AssertFailException{ + boolean result = false; + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + result = NativeSecp256k1.secKeyVerify( sec ); + //System.out.println(" TEST " + new BigInteger(1, resultbytes).toString(16)); + assertEquals( result, false , "testSecKeyVerifyNeg"); + } + + /** + * This tests public key create() for a valid secretkey + */ + public static void testPubKeyCreatePos() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString , "04C591A8FF19AC9C4E4E5793673B83123437E975285E7B442F4EE2654DFFCA5E2D2103ED494718C697AC9AEBCFD19612E224DB46661011863ED2FC54E71861E2A6" , "testPubKeyCreatePos"); + } + + /** + * This tests public key create() for a invalid secretkey + */ + public static void testPubKeyCreateNeg() throws AssertFailException{ + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.computePubkey( sec); + String pubkeyString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( pubkeyString, "" , "testPubKeyCreateNeg"); + } + + /** + * This tests sign() for a valid secretkey + */ + public static void testSignPos() throws AssertFailException{ + + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "30440220182A108E1448DC8F1FB467D06A0F3BB8EA0533584CB954EF8DA112F1D60E39A202201C66F36DA211C087F3AF88B50EDF4F9BDAA6CF5FD6817E74DCA34DB12390C6E9" , "testSignPos"); + } + + /** + * This tests sign() for a invalid secretkey + */ + public static void testSignNeg() throws AssertFailException{ + byte[] data = BaseEncoding.base16().lowerCase().decode("CF80CD8AED482D5D1527D7DC72FCEFF84E6326592848447D2DC0B0E87DFC9A90".toLowerCase()); //sha256hash of "testing" + byte[] sec = BaseEncoding.base16().lowerCase().decode("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.sign(data, sec); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString, "" , "testSignNeg"); + } + + /** + * This tests private key tweak-add + */ + public static void testPrivKeyTweakAdd_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakAdd( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "A168571E189E6F9A7E2D657A4B53AE99B909F7E712D1C23CED28093CD57C88F3" , "testPrivKeyAdd_1"); + } + + /** + * This tests private key tweak-mul + */ + public static void testPrivKeyTweakMul_1() throws AssertFailException { + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.privKeyTweakMul( sec , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "97F8184235F101550F3C71C927507651BD3F1CDB4A5A33B8986ACF0DEE20FFFC" , "testPrivKeyMul_1"); + } + + /** + * This tests private key tweak-add uncompressed + */ + public static void testPrivKeyTweakAdd_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakAdd( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "0411C6790F4B663CCE607BAAE08C43557EDC1A4D11D88DFCB3D841D0C6A941AF525A268E2A863C148555C48FB5FBA368E88718A46E205FABC3DBA2CCFFAB0796EF" , "testPrivKeyAdd_2"); + } + + /** + * This tests private key tweak-mul uncompressed + */ + public static void testPrivKeyTweakMul_2() throws AssertFailException { + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + byte[] data = BaseEncoding.base16().lowerCase().decode("3982F19BEF1615BCCFBB05E321C10E1D4CBA3DF0E841C2E41EEB6016347653C3".toLowerCase()); //sha256hash of "tweak" + + byte[] resultArr = NativeSecp256k1.pubKeyTweakMul( pub , data ); + String sigString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( sigString , "04E0FE6FE55EBCA626B98A807F6CAF654139E14E5E3698F01A9A658E21DC1D2791EC060D4F412A794D5370F672BC94B722640B5F76914151CFCA6E712CA48CC589" , "testPrivKeyMul_2"); + } + + /** + * This tests seed randomization + */ + public static void testRandomize() throws AssertFailException { + byte[] seed = BaseEncoding.base16().lowerCase().decode("A441B15FE9A3CF56661190A0B93B9DEC7D04127288CC87250967CF3B52894D11".toLowerCase()); //sha256hash of "random" + boolean result = NativeSecp256k1.randomize(seed); + assertEquals( result, true, "testRandomize"); + } + + public static void testCreateECDHSecret() throws AssertFailException{ + + byte[] sec = BaseEncoding.base16().lowerCase().decode("67E56582298859DDAE725F972992A07C6C4FB9F62A8FFF58CE3CA926A1063530".toLowerCase()); + byte[] pub = BaseEncoding.base16().lowerCase().decode("040A629506E1B65CD9D2E0BA9C75DF9C4FED0DB16DC9625ED14397F0AFC836FAE595DC53F8B0EFE61E703075BD9B143BAC75EC0E19F82A2208CAEB32BE53414C40".toLowerCase()); + + byte[] resultArr = NativeSecp256k1.createECDHSecret(sec, pub); + String ecdhString = javax.xml.bind.DatatypeConverter.printHexBinary(resultArr); + assertEquals( ecdhString, "2A2A67007A926E6594AF3EB564FC74005B37A9C8AEF2033C4552051B5C87F043" , "testCreateECDHSecret"); + } + + public static void main(String[] args) throws AssertFailException{ + + + System.out.println("\n libsecp256k1 enabled: " + Secp256k1Context.isEnabled() + "\n"); + + assertEquals( Secp256k1Context.isEnabled(), true, "isEnabled" ); + + //Test verify() success/fail + testVerifyPos(); + testVerifyNeg(); + + //Test secKeyVerify() success/fail + testSecKeyVerifyPos(); + testSecKeyVerifyNeg(); + + //Test computePubkey() success/fail + testPubKeyCreatePos(); + testPubKeyCreateNeg(); + + //Test sign() success/fail + testSignPos(); + testSignNeg(); + + //Test privKeyTweakAdd() 1 + testPrivKeyTweakAdd_1(); + + //Test privKeyTweakMul() 2 + testPrivKeyTweakMul_1(); + + //Test privKeyTweakAdd() 3 + testPrivKeyTweakAdd_2(); + + //Test privKeyTweakMul() 4 + testPrivKeyTweakMul_2(); + + //Test randomize() + testRandomize(); + + //Test ECDH + testCreateECDHSecret(); + + NativeSecp256k1.cleanup(); + + System.out.println(" All tests passed." ); + + } +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java new file mode 100644 index 000000000..04732ba04 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/NativeSecp256k1Util.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +public class NativeSecp256k1Util{ + + public static void assertEquals( int val, int val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + } + + public static void assertEquals( boolean val, boolean val2, String message ) throws AssertFailException{ + if( val != val2 ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static void assertEquals( String val, String val2, String message ) throws AssertFailException{ + if( !val.equals(val2) ) + throw new AssertFailException("FAIL: " + message); + else + System.out.println("PASS: " + message); + } + + public static class AssertFailException extends Exception { + public AssertFailException(String message) { + super( message ); + } + } +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java new file mode 100644 index 000000000..216c986a8 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org/bitcoin/Secp256k1Context.java @@ -0,0 +1,51 @@ +/* + * Copyright 2014-2016 the libsecp256k1 contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.bitcoin; + +/** + * This class holds the context reference used in native methods + * to handle ECDSA operations. + */ +public class Secp256k1Context { + private static final boolean enabled; //true if the library is loaded + private static final long context; //ref to pointer to context obj + + static { //static initializer + boolean isEnabled = true; + long contextRef = -1; + try { + System.loadLibrary("secp256k1"); + contextRef = secp256k1_init_context(); + } catch (UnsatisfiedLinkError e) { + System.out.println("UnsatisfiedLinkError: " + e.toString()); + isEnabled = false; + } + enabled = isEnabled; + context = contextRef; + } + + public static boolean isEnabled() { + return enabled; + } + + public static long getContext() { + if(!enabled) return -1; //sanity check + return context; + } + + private static native long secp256k1_init_context(); +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c new file mode 100644 index 000000000..bcef7b32c --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include "org_bitcoin_NativeSecp256k1.h" +#include "include/secp256k1.h" +#include "include/secp256k1_ecdh.h" +#include "include/secp256k1_recovery.h" + + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + const secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + jlong ctx_clone_l = (uintptr_t) secp256k1_context_clone(ctx); + + (void)classObject;(void)env; + + return ctx_clone_l; + +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + const unsigned char* seed = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_context_randomize(ctx, seed); + +} + +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv* env, jclass classObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + secp256k1_context_destroy(ctx); + + (void)classObject;(void)env; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint siglen, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* sigdata = { (unsigned char*) (data + 32) }; + const unsigned char* pubdata = { (unsigned char*) (data + siglen + 32) }; + + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pubkey; + + int ret = secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigdata, siglen); + + if( ret ) { + ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if( ret ) { + ret = secp256k1_ecdsa_verify(ctx, &sig, data, &pubkey); + } + } + + (void)classObject; + + return ret; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* data = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + unsigned char* secKey = (unsigned char*) (data + 32); + + jobjectArray retArray; + jbyteArray sigArray, intsByteArray; + unsigned char intsarray[2]; + + secp256k1_ecdsa_signature sig[72]; + + int ret = secp256k1_ecdsa_sign(ctx, sig, data, secKey, NULL, NULL ); + + unsigned char outputSer[72]; + size_t outputLen = 72; + + if( ret ) { + int ret2 = secp256k1_ecdsa_signature_serialize_der(ctx,outputSer, &outputLen, sig ); (void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + sigArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, sigArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, sigArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + (void)classObject; + + return secp256k1_ec_seckey_verify(ctx, secKey); +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secKey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + + secp256k1_pubkey pubkey; + + jobjectArray retArray; + jbyteArray pubkeyArray, intsByteArray; + unsigned char intsarray[2]; + + int ret = secp256k1_ec_pubkey_create(ctx, &pubkey, secKey); + + unsigned char outputSer[65]; + size_t outputLen = 65; + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubkeyArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubkeyArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubkeyArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; + +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_add(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* privkey = (unsigned char*) (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (privkey + 32); + + jobjectArray retArray; + jbyteArray privArray, intsByteArray; + unsigned char intsarray[2]; + + int privkeylen = 32; + + int ret = secp256k1_ec_privkey_tweak_mul(ctx, privkey, tweak); + + intsarray[0] = privkeylen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + privArray = (*env)->NewByteArray(env, privkeylen); + (*env)->SetByteArrayRegion(env, privArray, 0, privkeylen, (jbyte*)privkey); + (*env)->SetObjectArrayElement(env, retArray, 0, privArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; +/* secp256k1_pubkey* pubkey = (secp256k1_pubkey*) (*env)->GetDirectBufferAddress(env, byteBufferObject);*/ + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if( ret ) { + ret = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + unsigned char* pkey = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* tweak = (unsigned char*) (pkey + publen); + + jobjectArray retArray; + jbyteArray pubArray, intsByteArray; + unsigned char intsarray[2]; + unsigned char outputSer[65]; + size_t outputLen = 65; + + secp256k1_pubkey pubkey; + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pkey, publen); + + if ( ret ) { + ret = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, tweak); + } + + if( ret ) { + int ret2 = secp256k1_ec_pubkey_serialize(ctx,outputSer, &outputLen, &pubkey,SECP256K1_EC_UNCOMPRESSED );(void)ret2; + } + + intsarray[0] = outputLen; + intsarray[1] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + pubArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, pubArray, 0, outputLen, (jbyte*)outputSer); + (*env)->SetObjectArrayElement(env, retArray, 0, pubArray); + + intsByteArray = (*env)->NewByteArray(env, 2); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 2, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} + +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1pubkey_1combine + (JNIEnv * env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint numkeys) +{ + (void)classObject;(void)env;(void)byteBufferObject;(void)ctx_l;(void)numkeys; + + return 0; +} + +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen) +{ + secp256k1_context *ctx = (secp256k1_context*)(uintptr_t)ctx_l; + const unsigned char* secdata = (*env)->GetDirectBufferAddress(env, byteBufferObject); + const unsigned char* pubdata = (const unsigned char*) (secdata + 32); + + jobjectArray retArray; + jbyteArray outArray, intsByteArray; + unsigned char intsarray[1]; + secp256k1_pubkey pubkey; + unsigned char nonce_res[32]; + size_t outputLen = 32; + + int ret = secp256k1_ec_pubkey_parse(ctx, &pubkey, pubdata, publen); + + if (ret) { + ret = secp256k1_ecdh( + ctx, + nonce_res, + &pubkey, + secdata + ); + } + + intsarray[0] = ret; + + retArray = (*env)->NewObjectArray(env, 2, + (*env)->FindClass(env, "[B"), + (*env)->NewByteArray(env, 1)); + + outArray = (*env)->NewByteArray(env, outputLen); + (*env)->SetByteArrayRegion(env, outArray, 0, 32, (jbyte*)nonce_res); + (*env)->SetObjectArrayElement(env, retArray, 0, outArray); + + intsByteArray = (*env)->NewByteArray(env, 1); + (*env)->SetByteArrayRegion(env, intsByteArray, 0, 1, (jbyte*)intsarray); + (*env)->SetObjectArrayElement(env, retArray, 1, intsByteArray); + + (void)classObject; + + return retArray; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h new file mode 100644 index 000000000..fe613c9e9 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_NativeSecp256k1.h @@ -0,0 +1,119 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_NativeSecp256k1 */ + +#ifndef _Included_org_bitcoin_NativeSecp256k1 +#define _Included_org_bitcoin_NativeSecp256k1 +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ctx_clone + * Signature: (J)J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ctx_1clone + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_context_randomize + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1context_1randomize + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_privkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1privkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_add + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1add + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_pubkey_tweak_mul + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1pubkey_1tweak_1mul + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_destroy_context + * Signature: (J)V + */ +SECP256K1_API void JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1destroy_1context + (JNIEnv *, jclass, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_verify + * Signature: (Ljava/nio/ByteBuffer;JII)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1verify + (JNIEnv *, jclass, jobject, jlong, jint, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdsa_sign + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdsa_1sign + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_seckey_verify + * Signature: (Ljava/nio/ByteBuffer;J)I + */ +SECP256K1_API jint JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1seckey_1verify + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_create + * Signature: (Ljava/nio/ByteBuffer;J)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1create + (JNIEnv *, jclass, jobject, jlong); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ec_pubkey_parse + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ec_1pubkey_1parse + (JNIEnv *, jclass, jobject, jlong, jint); + +/* + * Class: org_bitcoin_NativeSecp256k1 + * Method: secp256k1_ecdh + * Signature: (Ljava/nio/ByteBuffer;JI)[[B + */ +SECP256K1_API jobjectArray JNICALL Java_org_bitcoin_NativeSecp256k1_secp256k1_1ecdh + (JNIEnv* env, jclass classObject, jobject byteBufferObject, jlong ctx_l, jint publen); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c new file mode 100644 index 000000000..a52939e7e --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.c @@ -0,0 +1,15 @@ +#include +#include +#include "org_bitcoin_Secp256k1Context.h" +#include "include/secp256k1.h" + +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv* env, jclass classObject) +{ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + (void)classObject;(void)env; + + return (uintptr_t)ctx; +} + diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h new file mode 100644 index 000000000..0d2bc84b7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/java/org_bitcoin_Secp256k1Context.h @@ -0,0 +1,22 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +#include "include/secp256k1.h" +/* Header for class org_bitcoin_Secp256k1Context */ + +#ifndef _Included_org_bitcoin_Secp256k1Context +#define _Included_org_bitcoin_Secp256k1Context +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: org_bitcoin_Secp256k1Context + * Method: secp256k1_init_context + * Signature: ()J + */ +SECP256K1_API jlong JNICALL Java_org_bitcoin_Secp256k1Context_secp256k1_1init_1context + (JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include new file mode 100644 index 000000000..e3088b469 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_ecdh.h +noinst_HEADERS += src/modules/ecdh/main_impl.h +noinst_HEADERS += src/modules/ecdh/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_ecdh +bench_ecdh_SOURCES = src/bench_ecdh.c +bench_ecdh_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h new file mode 100644 index 000000000..9e30fb73d --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/main_impl.h @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_ECDH_MAIN_ +#define _SECP256K1_MODULE_ECDH_MAIN_ + +#include "include/secp256k1_ecdh.h" +#include "ecmult_const_impl.h" + +int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const secp256k1_pubkey *point, const unsigned char *scalar) { + int ret = 0; + int overflow = 0; + secp256k1_gej res; + secp256k1_ge pt; + secp256k1_scalar s; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(result != NULL); + ARG_CHECK(point != NULL); + ARG_CHECK(scalar != NULL); + + secp256k1_pubkey_load(ctx, &pt, point); + secp256k1_scalar_set_b32(&s, scalar, &overflow); + if (overflow || secp256k1_scalar_is_zero(&s)) { + ret = 0; + } else { + unsigned char x[32]; + unsigned char y[1]; + secp256k1_sha256_t sha; + + secp256k1_ecmult_const(&res, &pt, &s); + secp256k1_ge_set_gej(&pt, &res); + /* Compute a hash of the point in compressed form + * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not + * expect its output to be secret and has a timing sidechannel. */ + secp256k1_fe_normalize(&pt.x); + secp256k1_fe_normalize(&pt.y); + secp256k1_fe_get_b32(x, &pt.x); + y[0] = 0x02 | secp256k1_fe_is_odd(&pt.y); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, y, sizeof(y)); + secp256k1_sha256_write(&sha, x, sizeof(x)); + secp256k1_sha256_finalize(&sha, result); + ret = 1; + } + + secp256k1_scalar_clear(&s); + return ret; +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h new file mode 100644 index 000000000..85a5d0a9a --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/ecdh/tests_impl.h @@ -0,0 +1,105 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_ECDH_TESTS_ +#define _SECP256K1_MODULE_ECDH_TESTS_ + +void test_ecdh_api(void) { + /* Setup context that just counts errors */ + secp256k1_context *tctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_pubkey point; + unsigned char res[32]; + unsigned char s_one[32] = { 0 }; + int32_t ecount = 0; + s_one[31] = 1; + + secp256k1_context_set_error_callback(tctx, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(tctx, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ec_pubkey_create(tctx, &point, s_one) == 1); + + /* Check all NULLs are detected */ + CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ecdh(tctx, NULL, &point, s_one) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdh(tctx, res, NULL, s_one) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdh(tctx, res, &point, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdh(tctx, res, &point, s_one) == 1); + CHECK(ecount == 3); + + /* Cleanup */ + secp256k1_context_destroy(tctx); +} + +void test_ecdh_generator_basepoint(void) { + unsigned char s_one[32] = { 0 }; + secp256k1_pubkey point[2]; + int i; + + s_one[31] = 1; + /* Check against pubkey creation when the basepoint is the generator */ + for (i = 0; i < 100; ++i) { + secp256k1_sha256_t sha; + unsigned char s_b32[32]; + unsigned char output_ecdh[32]; + unsigned char output_ser[32]; + unsigned char point_ser[33]; + size_t point_ser_len = sizeof(point_ser); + secp256k1_scalar s; + + random_scalar_order(&s); + secp256k1_scalar_get_b32(s_b32, &s); + + /* compute using ECDH function */ + CHECK(secp256k1_ec_pubkey_create(ctx, &point[0], s_one) == 1); + CHECK(secp256k1_ecdh(ctx, output_ecdh, &point[0], s_b32) == 1); + /* compute "explicitly" */ + CHECK(secp256k1_ec_pubkey_create(ctx, &point[1], s_b32) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, point_ser, &point_ser_len, &point[1], SECP256K1_EC_COMPRESSED) == 1); + CHECK(point_ser_len == sizeof(point_ser)); + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, point_ser, point_ser_len); + secp256k1_sha256_finalize(&sha, output_ser); + /* compare */ + CHECK(memcmp(output_ecdh, output_ser, sizeof(output_ser)) == 0); + } +} + +void test_bad_scalar(void) { + unsigned char s_zero[32] = { 0 }; + unsigned char s_overflow[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + unsigned char s_rand[32] = { 0 }; + unsigned char output[32]; + secp256k1_scalar rand; + secp256k1_pubkey point; + + /* Create random point */ + random_scalar_order(&rand); + secp256k1_scalar_get_b32(s_rand, &rand); + CHECK(secp256k1_ec_pubkey_create(ctx, &point, s_rand) == 1); + + /* Try to multiply it by bad values */ + CHECK(secp256k1_ecdh(ctx, output, &point, s_zero) == 0); + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 0); + /* ...and a good one */ + s_overflow[31] -= 1; + CHECK(secp256k1_ecdh(ctx, output, &point, s_overflow) == 1); +} + +void run_ecdh_tests(void) { + test_ecdh_api(); + test_ecdh_generator_basepoint(); + test_bad_scalar(); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include new file mode 100644 index 000000000..bf23c26e7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/Makefile.am.include @@ -0,0 +1,8 @@ +include_HEADERS += include/secp256k1_recovery.h +noinst_HEADERS += src/modules/recovery/main_impl.h +noinst_HEADERS += src/modules/recovery/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_recover +bench_recover_SOURCES = src/bench_recover.c +bench_recover_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h new file mode 100755 index 000000000..c6fbe2398 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/main_impl.h @@ -0,0 +1,193 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_RECOVERY_MAIN_ +#define _SECP256K1_MODULE_RECOVERY_MAIN_ + +#include "include/secp256k1_recovery.h" + +static void secp256k1_ecdsa_recoverable_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, int* recid, const secp256k1_ecdsa_recoverable_signature* sig) { + (void)ctx; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } + *recid = sig->data[64]; +} + +static void secp256k1_ecdsa_recoverable_signature_save(secp256k1_ecdsa_recoverable_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s, int recid) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } + sig->data[64] = recid; +} + +int secp256k1_ecdsa_recoverable_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature* sig, const unsigned char *input64, int recid) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + ARG_CHECK(recid >= 0 && recid <= 3); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(sig, &r, &s, recid); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_recoverable_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, int *recid, const secp256k1_ecdsa_recoverable_signature* sig) { + secp256k1_scalar r, s; + + (void)ctx; + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(recid != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, recid, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_recoverable_signature_convert(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const secp256k1_ecdsa_recoverable_signature* sigin) { + secp256k1_scalar r, s; + int recid; + + (void)ctx; + ARG_CHECK(sig != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, sigin); + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; +} + +static int secp256k1_ecdsa_sig_recover(const secp256k1_ecmult_context *ctx, const secp256k1_scalar *sigr, const secp256k1_scalar* sigs, secp256k1_ge *pubkey, const secp256k1_scalar *message, int recid) { + unsigned char brx[32]; + secp256k1_fe fx; + secp256k1_ge x; + secp256k1_gej xj; + secp256k1_scalar rn, u1, u2; + secp256k1_gej qj; + int r; + + if (secp256k1_scalar_is_zero(sigr) || secp256k1_scalar_is_zero(sigs)) { + return 0; + } + + secp256k1_scalar_get_b32(brx, sigr); + r = secp256k1_fe_set_b32(&fx, brx); + (void)r; + VERIFY_CHECK(r); /* brx comes from a scalar, so is less than the order; certainly less than p */ + if (recid & 2) { + if (secp256k1_fe_cmp_var(&fx, &secp256k1_ecdsa_const_p_minus_order) >= 0) { + return 0; + } + secp256k1_fe_add(&fx, &secp256k1_ecdsa_const_order_as_fe); + } + if (!secp256k1_ge_set_xo_var(&x, &fx, recid & 1)) { + return 0; + } + secp256k1_gej_set_ge(&xj, &x); + secp256k1_scalar_inverse_var(&rn, sigr); + secp256k1_scalar_mul(&u1, &rn, message); + secp256k1_scalar_negate(&u1, &u1); + secp256k1_scalar_mul(&u2, &rn, sigs); + secp256k1_ecmult(ctx, &qj, &xj, &u2, &u1); + secp256k1_ge_set_gej_var(pubkey, &qj); + return !secp256k1_gej_is_infinity(&qj); +} + +int secp256k1_ecdsa_sign_recoverable(const secp256k1_context* ctx, secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int recid; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (!overflow && !secp256k1_scalar_is_zero(&sec)) { + unsigned char nonce32[32]; + unsigned int count = 0; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + if (!secp256k1_scalar_is_zero(&non) && !overflow) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, &recid)) { + break; + } + } + count++; + } + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + } + if (ret) { + secp256k1_ecdsa_recoverable_signature_save(signature, &r, &s, recid); + } else { + memset(signature, 0, sizeof(*signature)); + } + return ret; +} + +int secp256k1_ecdsa_recover(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_ecdsa_recoverable_signature *signature, const unsigned char *msg32) { + secp256k1_ge q; + secp256k1_scalar r, s; + secp256k1_scalar m; + int recid; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, signature); + VERIFY_CHECK(recid >= 0 && recid < 4); /* should have been caught in parse_compact */ + secp256k1_scalar_set_b32(&m, msg32, NULL); + if (secp256k1_ecdsa_sig_recover(&ctx->ecmult_ctx, &r, &s, &q, &m, recid)) { + secp256k1_pubkey_save(pubkey, &q); + return 1; + } else { + memset(pubkey, 0, sizeof(*pubkey)); + return 0; + } +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h new file mode 100644 index 000000000..765c7dd81 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/modules/recovery/tests_impl.h @@ -0,0 +1,393 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_RECOVERY_TESTS_ +#define _SECP256K1_MODULE_RECOVERY_TESTS_ + +static int recovery_test_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + + /* On the first run, return 0 to force a second run */ + if (counter == 0) { + memset(nonce32, 0, 32); + return 1; + } + /* On the second run, return an overflow to force a third run */ + if (counter == 1) { + memset(nonce32, 0xff, 32); + return 1; + } + /* On the next run, return a valid nonce, but flip a coin as to whether or not to fail signing. */ + memset(nonce32, 1, 32); + return secp256k1_rand_bits(1); +} + +void test_ecdsa_recovery_api(void) { + /* Setup contexts that just count errors */ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + secp256k1_ecdsa_signature normal_sig; + secp256k1_ecdsa_recoverable_signature recsig; + unsigned char privkey[32] = { 1 }; + unsigned char message[32] = { 2 }; + int32_t ecount = 0; + int recid = 0; + unsigned char sig[74]; + unsigned char zero_privkey[32] = { 0 }; + unsigned char over_privkey[32] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Check bad contexts and NULLs for signing */ + ecount = 0; + CHECK(secp256k1_ecdsa_sign_recoverable(none, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(sign, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(vrfy, &recsig, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign_recoverable(both, NULL, message, privkey, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, NULL, privkey, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + /* This will fail or succeed randomly, and in either case will not ARG_CHECK failure */ + secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, recovery_test_nonce_function, NULL); + CHECK(ecount == 5); + /* These will all fail, but not in ARG_CHECK way */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, zero_privkey, NULL, NULL) == 0); + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, over_privkey, NULL, NULL) == 0); + /* This one will succeed. */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + CHECK(ecount == 5); + + /* Check signing with a goofy nonce function */ + + /* Check bad contexts and NULLs for recovery */ + ecount = 0; + CHECK(secp256k1_ecdsa_recover(none, &recpubkey, &recsig, message) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recover(sign, &recpubkey, &recsig, message) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(vrfy, &recpubkey, &recsig, message) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, message) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recover(both, NULL, &recsig, message) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, NULL, message) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_recover(both, &recpubkey, &recsig, NULL) == 0); + CHECK(ecount == 5); + + /* Check NULLs for conversion */ + CHECK(secp256k1_ecdsa_sign(both, &normal_sig, message, privkey, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, NULL, &recsig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(both, &normal_sig, &recsig) == 1); + + /* Check NULLs for de/serialization */ + CHECK(secp256k1_ecdsa_sign_recoverable(both, &recsig, message, privkey, NULL, NULL) == 1); + ecount = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, NULL, &recid, &recsig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, NULL, &recsig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(both, sig, &recid, &recsig) == 1); + + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, NULL, sig, recid) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, NULL, recid) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, -1) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, 5) == 0); + CHECK(ecount == 7); + /* overflow in signature will fail but not affect ecount */ + memcpy(sig, over_privkey, 32); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(both, &recsig, sig, recid) == 0); + CHECK(ecount == 7); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +void test_ecdsa_recovery_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + secp256k1_ecdsa_signature signature[5]; + secp256k1_ecdsa_recoverable_signature rsignature[5]; + unsigned char sig[74]; + secp256k1_pubkey pubkey; + secp256k1_pubkey recpubkey; + int recid = 0; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + secp256k1_scalar_get_b32(message, &msg); + } + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Serialize/parse compact and verify/recover. */ + extra[0] = 0; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[1], message, privkey, NULL, extra) == 1); + extra[31] = 1; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[2], message, privkey, NULL, extra) == 1); + extra[31] = 0; + extra[0] = 1; + CHECK(secp256k1_ecdsa_sign_recoverable(ctx, &rsignature[3], message, privkey, NULL, extra) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(memcmp(&signature[4], &signature[0], 64) == 0); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + memset(&rsignature[4], 0, sizeof(rsignature[4])); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 1); + /* Parse compact (with recovery id) and recover. */ + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 1); + CHECK(memcmp(&pubkey, &recpubkey, sizeof(pubkey)) == 0); + /* Serialize/destroy/parse signature and verify again. */ + CHECK(secp256k1_ecdsa_recoverable_signature_serialize_compact(ctx, sig, &recid, &rsignature[4]) == 1); + sig[secp256k1_rand_bits(6)] += 1 + secp256k1_rand_int(255); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsignature[4], sig, recid) == 1); + CHECK(secp256k1_ecdsa_recoverable_signature_convert(ctx, &signature[4], &rsignature[4]) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[4], message, &pubkey) == 0); + /* Recover again */ + CHECK(secp256k1_ecdsa_recover(ctx, &recpubkey, &rsignature[4], message) == 0 || + memcmp(&pubkey, &recpubkey, sizeof(pubkey)) != 0); +} + +/* Tests several edge cases. */ +void test_ecdsa_recovery_edge_cases(void) { + const unsigned char msg32[32] = { + 'T', 'h', 'i', 's', ' ', 'i', 's', ' ', + 'a', ' ', 'v', 'e', 'r', 'y', ' ', 's', + 'e', 'c', 'r', 'e', 't', ' ', 'm', 'e', + 's', 's', 'a', 'g', 'e', '.', '.', '.' + }; + const unsigned char sig64[64] = { + /* Generated by signing the above message with nonce 'This is the nonce we will use...' + * and secret key 0 (which is not valid), resulting in recid 0. */ + 0x67, 0xCB, 0x28, 0x5F, 0x9C, 0xD1, 0x94, 0xE8, + 0x40, 0xD6, 0x29, 0x39, 0x7A, 0xF5, 0x56, 0x96, + 0x62, 0xFD, 0xE4, 0x46, 0x49, 0x99, 0x59, 0x63, + 0x17, 0x9A, 0x7D, 0xD1, 0x7B, 0xD2, 0x35, 0x32, + 0x4B, 0x1B, 0x7D, 0xF3, 0x4C, 0xE1, 0xF6, 0x8E, + 0x69, 0x4F, 0xF6, 0xF1, 0x1A, 0xC7, 0x51, 0xDD, + 0x7D, 0xD7, 0x3E, 0x38, 0x7E, 0xE4, 0xFC, 0x86, + 0x6E, 0x1B, 0xE8, 0xEC, 0xC7, 0xDD, 0x95, 0x57 + }; + secp256k1_pubkey pubkey; + /* signature (r,s) = (4,4), which can be recovered with all 4 recids. */ + const unsigned char sigb64[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + secp256k1_pubkey pubkeyb; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + int recid; + + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 0)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 1)); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 2)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sig64, 3)); + CHECK(!secp256k1_ecdsa_recover(ctx, &pubkey, &rsig, msg32)); + + for (recid = 0; recid < 4; recid++) { + int i; + int recid2; + /* (4,4) encoded in DER. */ + unsigned char sigbder[8] = {0x30, 0x06, 0x02, 0x01, 0x04, 0x02, 0x01, 0x04}; + unsigned char sigcder_zr[7] = {0x30, 0x05, 0x02, 0x00, 0x02, 0x01, 0x01}; + unsigned char sigcder_zs[7] = {0x30, 0x05, 0x02, 0x01, 0x01, 0x02, 0x00}; + unsigned char sigbderalt1[39] = { + 0x30, 0x25, 0x02, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt2[39] = { + 0x30, 0x25, 0x02, 0x01, 0x04, 0x02, 0x20, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + unsigned char sigbderalt3[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0x02, 0x01, 0x04, + }; + unsigned char sigbderalt4[40] = { + 0x30, 0x26, 0x02, 0x01, 0x04, 0x02, 0x21, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + }; + /* (order + r,4) encoded in DER. */ + unsigned char sigbderlong[40] = { + 0x30, 0x26, 0x02, 0x21, 0x00, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, + 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, + 0x8C, 0xD0, 0x36, 0x41, 0x45, 0x02, 0x01, 0x04 + }; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 1); + for (recid2 = 0; recid2 < 4; recid2++) { + secp256k1_pubkey pubkey2b; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigb64, recid2) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkey2b, &rsig, msg32) == 1); + /* Verifying with (order + r,4) should always fail. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderlong, sizeof(sigbderlong)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + /* DER parsing tests. */ + /* Zero length r/s. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zr, sizeof(sigcder_zr)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder_zs, sizeof(sigcder_zs)) == 0); + /* Leading zeros. */ + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt1, sizeof(sigbderalt1)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt2, sizeof(sigbderalt2)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 0); + sigbderalt3[4] = 1; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt3, sizeof(sigbderalt3)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + sigbderalt4[7] = 1; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbderalt4, sizeof(sigbderalt4)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + /* Damage signature. */ + sigbder[7]++; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + sigbder[7]--; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, 6) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder) - 1) == 0); + for(i = 0; i < 8; i++) { + int c; + unsigned char orig = sigbder[i]; + /*Try every single-byte change.*/ + for (c = 0; c < 256; c++) { + if (c == orig ) { + continue; + } + sigbder[i] = c; + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigbder, sizeof(sigbder)) == 0 || secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyb) == 0); + } + sigbder[i] = orig; + } + } + + /* Test r/s equal to zero */ + { + /* (1,1) encoded in DER. */ + unsigned char sigcder[8] = {0x30, 0x06, 0x02, 0x01, 0x01, 0x02, 0x01, 0x01}; + unsigned char sigc64[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + secp256k1_pubkey pubkeyc; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyc, &rsig, msg32) == 1); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 1); + sigcder[4] = 0; + sigc64[31] = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + sigcder[4] = 1; + sigcder[7] = 0; + sigc64[31] = 1; + sigc64[63] = 0; + CHECK(secp256k1_ecdsa_recoverable_signature_parse_compact(ctx, &rsig, sigc64, 0) == 1); + CHECK(secp256k1_ecdsa_recover(ctx, &pubkeyb, &rsig, msg32) == 0); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, sigcder, sizeof(sigcder)) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg32, &pubkeyc) == 0); + } +} + +void run_recovery_tests(void) { + int i; + for (i = 0; i < count; i++) { + test_ecdsa_recovery_api(); + } + for (i = 0; i < 64*count; i++) { + test_ecdsa_recovery_end_to_end(); + } + test_ecdsa_recovery_edge_cases(); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num.h new file mode 100644 index 000000000..eff842200 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num.h @@ -0,0 +1,74 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_ +#define _SECP256K1_NUM_ + +#ifndef USE_NUM_NONE + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(USE_NUM_GMP) +#include "num_gmp.h" +#else +#error "Please select num implementation" +#endif + +/** Copy a number. */ +static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a); + +/** Convert a number's absolute value to a binary big-endian string. + * There must be enough place. */ +static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a); + +/** Set a number to the value of a binary big-endian string. */ +static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen); + +/** Compute a modular inverse. The input must be less than the modulus. */ +static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m); + +/** Compute the jacobi symbol (a|b). b must be positive and odd. */ +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b); + +/** Compare the absolute value of two numbers. */ +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b); + +/** Test whether two number are equal (including sign). */ +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b); + +/** Add two (signed) numbers. */ +static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Subtract two (signed) numbers. */ +static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Multiply two (signed) numbers. */ +static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b); + +/** Replace a number by its remainder modulo m. M's sign is ignored. The result is a number between 0 and m-1, + even if r was negative. */ +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m); + +/** Right-shift the passed number by bits. */ +static void secp256k1_num_shift(secp256k1_num *r, int bits); + +/** Check whether a number is zero. */ +static int secp256k1_num_is_zero(const secp256k1_num *a); + +/** Check whether a number is one. */ +static int secp256k1_num_is_one(const secp256k1_num *a); + +/** Check whether a number is strictly negative. */ +static int secp256k1_num_is_neg(const secp256k1_num *a); + +/** Change a number's sign. */ +static void secp256k1_num_negate(secp256k1_num *r); + +#endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h new file mode 100644 index 000000000..7dd813088 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp.h @@ -0,0 +1,20 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_REPR_ +#define _SECP256K1_NUM_REPR_ + +#include + +#define NUM_LIMBS ((256+GMP_NUMB_BITS-1)/GMP_NUMB_BITS) + +typedef struct { + mp_limb_t data[2*NUM_LIMBS]; + int neg; + int limbs; +} secp256k1_num; + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h new file mode 100644 index 000000000..3a46495ee --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_gmp_impl.h @@ -0,0 +1,288 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_REPR_IMPL_H_ +#define _SECP256K1_NUM_REPR_IMPL_H_ + +#include +#include +#include + +#include "util.h" +#include "num.h" + +#ifdef VERIFY +static void secp256k1_num_sanity(const secp256k1_num *a) { + VERIFY_CHECK(a->limbs == 1 || (a->limbs > 1 && a->data[a->limbs-1] != 0)); +} +#else +#define secp256k1_num_sanity(a) do { } while(0) +#endif + +static void secp256k1_num_copy(secp256k1_num *r, const secp256k1_num *a) { + *r = *a; +} + +static void secp256k1_num_get_bin(unsigned char *r, unsigned int rlen, const secp256k1_num *a) { + unsigned char tmp[65]; + int len = 0; + int shift = 0; + if (a->limbs>1 || a->data[0] != 0) { + len = mpn_get_str(tmp, 256, (mp_limb_t*)a->data, a->limbs); + } + while (shift < len && tmp[shift] == 0) shift++; + VERIFY_CHECK(len-shift <= (int)rlen); + memset(r, 0, rlen - len + shift); + if (len > shift) { + memcpy(r + rlen - len + shift, tmp + shift, len - shift); + } + memset(tmp, 0, sizeof(tmp)); +} + +static void secp256k1_num_set_bin(secp256k1_num *r, const unsigned char *a, unsigned int alen) { + int len; + VERIFY_CHECK(alen > 0); + VERIFY_CHECK(alen <= 64); + len = mpn_set_str(r->data, a, alen, 256); + if (len == 0) { + r->data[0] = 0; + len = 1; + } + VERIFY_CHECK(len <= NUM_LIMBS*2); + r->limbs = len; + r->neg = 0; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_add_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t c = mpn_add(r->data, a->data, a->limbs, b->data, b->limbs); + r->limbs = a->limbs; + if (c != 0) { + VERIFY_CHECK(r->limbs < 2*NUM_LIMBS); + r->data[r->limbs++] = c; + } +} + +static void secp256k1_num_sub_abs(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t c = mpn_sub(r->data, a->data, a->limbs, b->data, b->limbs); + (void)c; + VERIFY_CHECK(c == 0); + r->limbs = a->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_mod(secp256k1_num *r, const secp256k1_num *m) { + secp256k1_num_sanity(r); + secp256k1_num_sanity(m); + + if (r->limbs >= m->limbs) { + mp_limb_t t[2*NUM_LIMBS]; + mpn_tdiv_qr(t, r->data, 0, r->data, r->limbs, m->data, m->limbs); + memset(t, 0, sizeof(t)); + r->limbs = m->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } + } + + if (r->neg && (r->limbs > 1 || r->data[0] != 0)) { + secp256k1_num_sub_abs(r, m, r); + r->neg = 0; + } +} + +static void secp256k1_num_mod_inverse(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *m) { + int i; + mp_limb_t g[NUM_LIMBS+1]; + mp_limb_t u[NUM_LIMBS+1]; + mp_limb_t v[NUM_LIMBS+1]; + mp_size_t sn; + mp_size_t gn; + secp256k1_num_sanity(a); + secp256k1_num_sanity(m); + + /** mpn_gcdext computes: (G,S) = gcdext(U,V), where + * * G = gcd(U,V) + * * G = U*S + V*T + * * U has equal or more limbs than V, and V has no padding + * If we set U to be (a padded version of) a, and V = m: + * G = a*S + m*T + * G = a*S mod m + * Assuming G=1: + * S = 1/a mod m + */ + VERIFY_CHECK(m->limbs <= NUM_LIMBS); + VERIFY_CHECK(m->data[m->limbs-1] != 0); + for (i = 0; i < m->limbs; i++) { + u[i] = (i < a->limbs) ? a->data[i] : 0; + v[i] = m->data[i]; + } + sn = NUM_LIMBS+1; + gn = mpn_gcdext(g, r->data, &sn, u, m->limbs, v, m->limbs); + (void)gn; + VERIFY_CHECK(gn == 1); + VERIFY_CHECK(g[0] == 1); + r->neg = a->neg ^ m->neg; + if (sn < 0) { + mpn_sub(r->data, m->data, m->limbs, r->data, -sn); + r->limbs = m->limbs; + while (r->limbs > 1 && r->data[r->limbs-1]==0) { + r->limbs--; + } + } else { + r->limbs = sn; + } + memset(g, 0, sizeof(g)); + memset(u, 0, sizeof(u)); + memset(v, 0, sizeof(v)); +} + +static int secp256k1_num_jacobi(const secp256k1_num *a, const secp256k1_num *b) { + int ret; + mpz_t ga, gb; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + VERIFY_CHECK(!b->neg && (b->limbs > 0) && (b->data[0] & 1)); + + mpz_inits(ga, gb, NULL); + + mpz_import(gb, b->limbs, -1, sizeof(mp_limb_t), 0, 0, b->data); + mpz_import(ga, a->limbs, -1, sizeof(mp_limb_t), 0, 0, a->data); + if (a->neg) { + mpz_neg(ga, ga); + } + + ret = mpz_jacobi(ga, gb); + + mpz_clears(ga, gb, NULL); + + return ret; +} + +static int secp256k1_num_is_one(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 1); +} + +static int secp256k1_num_is_zero(const secp256k1_num *a) { + return (a->limbs == 1 && a->data[0] == 0); +} + +static int secp256k1_num_is_neg(const secp256k1_num *a) { + return (a->limbs > 1 || a->data[0] != 0) && a->neg; +} + +static int secp256k1_num_cmp(const secp256k1_num *a, const secp256k1_num *b) { + if (a->limbs > b->limbs) { + return 1; + } + if (a->limbs < b->limbs) { + return -1; + } + return mpn_cmp(a->data, b->data, a->limbs); +} + +static int secp256k1_num_eq(const secp256k1_num *a, const secp256k1_num *b) { + if (a->limbs > b->limbs) { + return 0; + } + if (a->limbs < b->limbs) { + return 0; + } + if ((a->neg && !secp256k1_num_is_zero(a)) != (b->neg && !secp256k1_num_is_zero(b))) { + return 0; + } + return mpn_cmp(a->data, b->data, a->limbs) == 0; +} + +static void secp256k1_num_subadd(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b, int bneg) { + if (!(b->neg ^ bneg ^ a->neg)) { /* a and b have the same sign */ + r->neg = a->neg; + if (a->limbs >= b->limbs) { + secp256k1_num_add_abs(r, a, b); + } else { + secp256k1_num_add_abs(r, b, a); + } + } else { + if (secp256k1_num_cmp(a, b) > 0) { + r->neg = a->neg; + secp256k1_num_sub_abs(r, a, b); + } else { + r->neg = b->neg ^ bneg; + secp256k1_num_sub_abs(r, b, a); + } + } +} + +static void secp256k1_num_add(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + secp256k1_num_subadd(r, a, b, 0); +} + +static void secp256k1_num_sub(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + secp256k1_num_subadd(r, a, b, 1); +} + +static void secp256k1_num_mul(secp256k1_num *r, const secp256k1_num *a, const secp256k1_num *b) { + mp_limb_t tmp[2*NUM_LIMBS+1]; + secp256k1_num_sanity(a); + secp256k1_num_sanity(b); + + VERIFY_CHECK(a->limbs + b->limbs <= 2*NUM_LIMBS+1); + if ((a->limbs==1 && a->data[0]==0) || (b->limbs==1 && b->data[0]==0)) { + r->limbs = 1; + r->neg = 0; + r->data[0] = 0; + return; + } + if (a->limbs >= b->limbs) { + mpn_mul(tmp, a->data, a->limbs, b->data, b->limbs); + } else { + mpn_mul(tmp, b->data, b->limbs, a->data, a->limbs); + } + r->limbs = a->limbs + b->limbs; + if (r->limbs > 1 && tmp[r->limbs - 1]==0) { + r->limbs--; + } + VERIFY_CHECK(r->limbs <= 2*NUM_LIMBS); + mpn_copyi(r->data, tmp, r->limbs); + r->neg = a->neg ^ b->neg; + memset(tmp, 0, sizeof(tmp)); +} + +static void secp256k1_num_shift(secp256k1_num *r, int bits) { + if (bits % GMP_NUMB_BITS) { + /* Shift within limbs. */ + mpn_rshift(r->data, r->data, r->limbs, bits % GMP_NUMB_BITS); + } + if (bits >= GMP_NUMB_BITS) { + int i; + /* Shift full limbs. */ + for (i = 0; i < r->limbs; i++) { + int index = i + (bits / GMP_NUMB_BITS); + if (index < r->limbs && index < 2*NUM_LIMBS) { + r->data[i] = r->data[index]; + } else { + r->data[i] = 0; + } + } + } + while (r->limbs>1 && r->data[r->limbs-1]==0) { + r->limbs--; + } +} + +static void secp256k1_num_negate(secp256k1_num *r) { + r->neg ^= 1; +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h new file mode 100644 index 000000000..0b0e3a072 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/num_impl.h @@ -0,0 +1,24 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_NUM_IMPL_H_ +#define _SECP256K1_NUM_IMPL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include "num.h" + +#if defined(USE_NUM_GMP) +#include "num_gmp_impl.h" +#elif defined(USE_NUM_NONE) +/* Nothing. */ +#else +#error "Please select num implementation" +#endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h new file mode 100644 index 000000000..27e9d8375 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar.h @@ -0,0 +1,106 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_ +#define _SECP256K1_SCALAR_ + +#include "num.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32.h" +#else +#error "Please select scalar implementation" +#endif + +/** Clear a scalar to prevent the leak of sensitive data. */ +static void secp256k1_scalar_clear(secp256k1_scalar *r); + +/** Access bits from a scalar. All requested bits must belong to the same 32-bit limb. */ +static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Access bits from a scalar. Not constant time. */ +static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Set a scalar from a big endian byte array. */ +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); + +/** Set a scalar to an unsigned integer. */ +static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); + +/** Convert a scalar to a byte array. */ +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); + +/** Add two scalars together (modulo the group order). Returns whether it overflowed. */ +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); + +/** Multiply two scalars (modulo the group order). */ +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Shift a scalar right by some amount strictly between 0 and 16, returning + * the low bits that were shifted off */ +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); + +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order). */ +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the complement of a scalar (modulo the group order). */ +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Check whether a scalar equals zero. */ +static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); + +/** Check whether a scalar equals one. */ +static int secp256k1_scalar_is_one(const secp256k1_scalar *a); + +/** Check whether a scalar, considered as an nonnegative integer, is even. */ +static int secp256k1_scalar_is_even(const secp256k1_scalar *a); + +/** Check whether a scalar is higher than the group order divided by 2. */ +static int secp256k1_scalar_is_high(const secp256k1_scalar *a); + +/** Conditionally negate a number, in constant time. + * Returns -1 if the number was negated, 1 otherwise */ +static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag); + +#ifndef USE_NUM_NONE +/** Convert a scalar to a number. */ +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a); + +/** Get the order of the group as a number. */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r); +#endif + +/** Compare two scalars. */ +static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b); + +#ifdef USE_ENDOMORPHISM +/** Find r1 and r2 such that r1+r2*2^128 = a. */ +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +#endif + +/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ +static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h new file mode 100644 index 000000000..cff406038 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint64_t d[4]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h new file mode 100644 index 000000000..56e7bd82a --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_4x64_impl.h @@ -0,0 +1,949 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) +#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) +#define SECP256K1_N_2 ((uint64_t)0xFFFFFFFFFFFFFFFEULL) +#define SECP256K1_N_3 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint64_t)0xDFE92F46681B20A0ULL) +#define SECP256K1_N_H_1 ((uint64_t)0x5D576E7357A4501DULL) +#define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) +#define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); + return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 6 == offset >> 6) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 6) + 1 < 4); + return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & ((((uint64_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ + no |= (a->d[2] < SECP256K1_N_2); + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1); + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { + uint128_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint64_t)r->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint128_t t = (uint128_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint128_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + (((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[3] + (((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 64) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; + r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; + r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; + r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; + bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; + bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; + bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t nonzero = 0xFFFFFFFFFFFFFFFFULL * (secp256k1_scalar_is_zero(a) == 0); + uint128_t t = (uint128_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_H_3); + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; /* No need for a > check. */ + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint64_t mask = !flag - 1; + uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; + uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; + return 2 * (mask == 0) - 1; +} + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c1 += th; /* overflow is handled on the next line */ \ + c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint64_t tl, th; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c1 += th; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint64_t tl, th, th2, tl2; \ + { \ + uint128_t t = (uint128_t)a * b; \ + th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ + c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ + th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ + unsigned int over; \ + c0 += (a); /* overflow is handled on the next line */ \ + over = (c0 < (a)) ? 1 : 0; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ + c0 += (a); /* overflow is handled on the next line */ \ + c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +#ifdef USE_ASM_X86_64 + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; + uint64_t c; + + __asm__ __volatile__( + /* Preload. */ + "movq 32(%%rsi), %%r11\n" + "movq 40(%%rsi), %%r12\n" + "movq 48(%%rsi), %%r13\n" + "movq 56(%%rsi), %%r14\n" + /* Initialize r8,r9,r10 */ + "movq 0(%%rsi), %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += n0 * c0 */ + "movq %8, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract m0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += l1 */ + "addq 8(%%rsi), %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += n1 * c0 */ + "movq %8, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n0 * c1 */ + "movq %9, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract m1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += l2 */ + "addq 16(%%rsi), %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n2 * c0 */ + "movq %8, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n1 * c1 */ + "movq %9, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n0 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract m2 */ + "movq %%r10, %q2\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += l3 */ + "addq 24(%%rsi), %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n3 * c0 */ + "movq %8, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n2 * c1 */ + "movq %9, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n1 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* extract m3 */ + "movq %%r8, %q3\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += n3 * c1 */ + "movq %9, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n2 */ + "addq %%r13, %%r9\n" + "adcq $0, %%r10\n" + "adcq $0, %%r8\n" + /* extract m4 */ + "movq %%r9, %q4\n" + /* (r10,r8) += n3 */ + "addq %%r14, %%r10\n" + "adcq $0, %%r8\n" + /* extract m5 */ + "movq %%r10, %q5\n" + /* extract m6 */ + "movq %%r8, %q6\n" + : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); + + /* Reduce 385 bits into 258. */ + __asm__ __volatile__( + /* Preload */ + "movq %q9, %%r11\n" + "movq %q10, %%r12\n" + "movq %q11, %%r13\n" + /* Initialize (r8,r9,r10) */ + "movq %q5, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += m4 * c0 */ + "movq %12, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract p0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += m1 */ + "addq %q6, %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += m5 * c0 */ + "movq %12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += m4 * c1 */ + "movq %13, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract p1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += m2 */ + "addq %q7, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m6 * c0 */ + "movq %12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m5 * c1 */ + "movq %13, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m4 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract p2 */ + "movq %%r10, %q2\n" + /* (r8,r9) += m3 */ + "addq %q8, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += m6 * c1 */ + "movq %13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* (r8,r9) += m5 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + /* extract p3 */ + "movq %%r8, %q3\n" + /* (r9) += m6 */ + "addq %%r13, %%r9\n" + /* extract p4 */ + "movq %%r9, %q4\n" + : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) + : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); + + /* Reduce 258 bits into 256. */ + __asm__ __volatile__( + /* Preload */ + "movq %q5, %%r10\n" + /* (rax,rdx) = p4 * c0 */ + "movq %7, %%rax\n" + "mulq %%r10\n" + /* (rax,rdx) += p0 */ + "addq %q1, %%rax\n" + "adcq $0, %%rdx\n" + /* extract r0 */ + "movq %%rax, 0(%q6)\n" + /* Move to (r8,r9) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p1 */ + "addq %q2, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += p4 * c1 */ + "movq %8, %%rax\n" + "mulq %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* Extract r1 */ + "movq %%r8, 8(%q6)\n" + "xorq %%r8, %%r8\n" + /* (r9,r8) += p4 */ + "addq %%r10, %%r9\n" + "adcq $0, %%r8\n" + /* (r9,r8) += p2 */ + "addq %q3, %%r9\n" + "adcq $0, %%r8\n" + /* Extract r2 */ + "movq %%r9, 16(%q6)\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p3 */ + "addq %q4, %%r8\n" + "adcq $0, %%r9\n" + /* Extract r3 */ + "movq %%r8, 24(%q6)\n" + /* Extract c */ + "movq %%r9, %q0\n" + : "=g"(c) + : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); +#else + uint128_t c; + uint64_t c0, c1, c2; + uint64_t n0 = l[4], n1 = l[5], n2 = l[6], n3 = l[7]; + uint64_t m0, m1, m2, m3, m4, m5; + uint32_t m6; + uint64_t p0, p1, p2, p3; + uint32_t p4; + + /* Reduce 512 bits into 385. */ + /* m[0..6] = l[0..3] + n[0..3] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + sumadd(n0); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + sumadd(n1); + extract(m3); + muladd(n3, SECP256K1_N_C_1); + sumadd(n2); + extract(m4); + sumadd_fast(n3); + extract_fast(m5); + VERIFY_CHECK(c0 <= 1); + m6 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..4] = m[0..3] + m[4..6] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m4, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m5, SECP256K1_N_C_0); + muladd(m4, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m6, SECP256K1_N_C_0); + muladd(m5, SECP256K1_N_C_1); + sumadd(m4); + extract(p2); + sumadd_fast(m3); + muladd_fast(m6, SECP256K1_N_C_1); + sumadd_fast(m5); + extract_fast(p3); + p4 = c0 + m6; + VERIFY_CHECK(p4 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..3] = p[0..3] + p[4] * SECP256K1_N_C. */ + c = p0 + (uint128_t)SECP256K1_N_C_0 * p4; + r->d[0] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p1 + (uint128_t)SECP256K1_N_C_1 * p4; + r->d[1] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p2 + (uint128_t)p4; + r->d[2] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p3; + r->d[3] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; +#endif + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { +#ifdef USE_ASM_X86_64 + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r15\n" + "movq 8(%%rdi), %%rbx\n" + "movq 16(%%rdi), %%rcx\n" + "movq 0(%%rdx), %%r11\n" + "movq 8(%%rdx), %%r12\n" + "movq 16(%%rdx), %%r13\n" + "movq 24(%%rdx), %%r14\n" + /* (rax,rdx) = a0 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a0 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a1 * b0 */ + "movq %%rbx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a0 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * b1 */ + "movq %%rbx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a2 * b0 */ + "movq %%rcx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += a0 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Preload a3 */ + "movq 24(%%rdi), %%r15\n" + /* (r10,r8,r9) += a1 * b2 */ + "movq %%rbx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a2 * b1 */ + "movq %%rcx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a3 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a1 * b3 */ + "movq %%rbx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * b2 */ + "movq %%rcx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a3 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a2 * b3 */ + "movq %%rcx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a3 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : "+d"(pb) + : "S"(l), "D"(a->d) + : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + extract(l[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + extract(l[5]); + muladd_fast(a->d[3], b->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint64_t l[8]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (64 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (64 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (64 - n)); + r->d[3] = (r->d[3] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = 0; + r1->d[3] = 0; + r2->d[0] = a->d[2]; + r2->d[1] = a->d[3]; + r2->d[2] = 0; + r2->d[3] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint64_t l[8]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 6; + shiftlow = shift & 0x3F; + shifthigh = 64 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 384 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 320 ? (l[3 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h new file mode 100644 index 000000000..1319664f6 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32.h @@ -0,0 +1,19 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint32_t d[8]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h new file mode 100644 index 000000000..aae4f35c0 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_8x32_impl.h @@ -0,0 +1,721 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint32_t)0xD0364141UL) +#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) +#define SECP256K1_N_2 ((uint32_t)0xAF48A03BUL) +#define SECP256K1_N_3 ((uint32_t)0xBAAEDCE6UL) +#define SECP256K1_N_4 ((uint32_t)0xFFFFFFFEUL) +#define SECP256K1_N_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_7 ((uint32_t)0xFFFFFFFFUL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (~SECP256K1_N_2) +#define SECP256K1_N_C_3 (~SECP256K1_N_3) +#define SECP256K1_N_C_4 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint32_t)0x681B20A0UL) +#define SECP256K1_N_H_1 ((uint32_t)0xDFE92F46UL) +#define SECP256K1_N_H_2 ((uint32_t)0x57A4501DUL) +#define SECP256K1_N_H_3 ((uint32_t)0x5D576E73UL) +#define SECP256K1_N_H_4 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); + return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 5 == offset >> 5) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 5) + 1 < 8); + return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & ((((uint32_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ + no |= (a->d[6] < SECP256K1_N_6); /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_5); /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_4); + yes |= (a->d[4] > SECP256K1_N_4) & ~no; + no |= (a->d[3] < SECP256K1_N_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_3) & ~no; + no |= (a->d[2] < SECP256K1_N_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_t overflow) { + uint64_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint64_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[3] + overflow * SECP256K1_N_C_3; + r->d[3] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[4] + overflow * SECP256K1_N_C_4; + r->d[4] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[5]; + r->d[5] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[6]; + r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[7]; + r->d[7] = t & 0xFFFFFFFFUL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint64_t t = (uint64_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[4] + b->d[4]; + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[5] + b->d[5]; + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[6] + b->d[6]; + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[7] + b->d[7]; + r->d[7] = t & 0xFFFFFFFFULL; t >>= 32; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint64_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[2] + (((uint32_t)((bit >> 5) == 2)) << (bit & 0x1F)); + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[3] + (((uint32_t)((bit >> 5) == 3)) << (bit & 0x1F)); + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[4] + (((uint32_t)((bit >> 5) == 4)) << (bit & 0x1F)); + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[5] + (((uint32_t)((bit >> 5) == 5)) << (bit & 0x1F)); + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[6] + (((uint32_t)((bit >> 5) == 6)) << (bit & 0x1F)); + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[7] + (((uint32_t)((bit >> 5) == 7)) << (bit & 0x1F)); + r->d[7] = t & 0xFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 32) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; + r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; + r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; + r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; + r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; + r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; + r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; + r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; + bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; + bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; + bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; + bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; + bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; + bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; + bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(a) == 0); + uint64_t t = (uint64_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[4]) + SECP256K1_N_4; + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[5]) + SECP256K1_N_5; + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[6]) + SECP256K1_N_6; + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; + r->d[7] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_H_7); + yes |= (a->d[7] > SECP256K1_N_H_7) & ~no; + no |= (a->d[6] < SECP256K1_N_H_6) & ~yes; /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_H_5) & ~yes; /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_H_4) & ~yes; /* No need for a > check. */ + no |= (a->d[3] < SECP256K1_N_H_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_H_2) & ~no; + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint32_t mask = !flag - 1; + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); + uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); + r->d[7] = t & nonzero; + return 2 * (mask == 0) - 1; +} + + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c1 += th; /* overflow is handled on the next line */ \ + c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ + uint32_t tl, th; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + c0 += tl; /* overflow is handled on the next line */ \ + th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c1 += th; /* never overflows by contract (verified in the next line) */ \ + VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ + uint32_t tl, th, th2, tl2; \ + { \ + uint64_t t = (uint64_t)a * b; \ + th = t >> 32; /* at most 0xFFFFFFFE */ \ + tl = t; \ + } \ + th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ + c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ + tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ + th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ + c0 += tl2; /* overflow is handled on the next line */ \ + th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ + c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ + c1 += th2; /* overflow is handled on the next line */ \ + c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ + unsigned int over; \ + c0 += (a); /* overflow is handled on the next line */ \ + over = (c0 < (a)) ? 1 : 0; \ + c1 += over; /* overflow is handled on the next line */ \ + c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ + c0 += (a); /* overflow is handled on the next line */ \ + c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ + VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ + VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. */ +#define extract(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = c2; \ + c2 = 0; \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ + (n) = c0; \ + c0 = c1; \ + c1 = 0; \ + VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { + uint64_t c; + uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; + uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; + uint32_t p0, p1, p2, p3, p4, p5, p6, p7, p8; + + /* 96 bit accumulator. */ + uint32_t c0, c1, c2; + + /* Reduce 512 bits into 385. */ + /* m[0..12] = l[0..7] + n[0..7] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + muladd(n0, SECP256K1_N_C_2); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + muladd(n1, SECP256K1_N_C_2); + muladd(n0, SECP256K1_N_C_3); + extract(m3); + sumadd(l[4]); + muladd(n4, SECP256K1_N_C_0); + muladd(n3, SECP256K1_N_C_1); + muladd(n2, SECP256K1_N_C_2); + muladd(n1, SECP256K1_N_C_3); + sumadd(n0); + extract(m4); + sumadd(l[5]); + muladd(n5, SECP256K1_N_C_0); + muladd(n4, SECP256K1_N_C_1); + muladd(n3, SECP256K1_N_C_2); + muladd(n2, SECP256K1_N_C_3); + sumadd(n1); + extract(m5); + sumadd(l[6]); + muladd(n6, SECP256K1_N_C_0); + muladd(n5, SECP256K1_N_C_1); + muladd(n4, SECP256K1_N_C_2); + muladd(n3, SECP256K1_N_C_3); + sumadd(n2); + extract(m6); + sumadd(l[7]); + muladd(n7, SECP256K1_N_C_0); + muladd(n6, SECP256K1_N_C_1); + muladd(n5, SECP256K1_N_C_2); + muladd(n4, SECP256K1_N_C_3); + sumadd(n3); + extract(m7); + muladd(n7, SECP256K1_N_C_1); + muladd(n6, SECP256K1_N_C_2); + muladd(n5, SECP256K1_N_C_3); + sumadd(n4); + extract(m8); + muladd(n7, SECP256K1_N_C_2); + muladd(n6, SECP256K1_N_C_3); + sumadd(n5); + extract(m9); + muladd(n7, SECP256K1_N_C_3); + sumadd(n6); + extract(m10); + sumadd_fast(n7); + extract_fast(m11); + VERIFY_CHECK(c0 <= 1); + m12 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..8] = m[0..7] + m[8..12] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m8, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m9, SECP256K1_N_C_0); + muladd(m8, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m10, SECP256K1_N_C_0); + muladd(m9, SECP256K1_N_C_1); + muladd(m8, SECP256K1_N_C_2); + extract(p2); + sumadd(m3); + muladd(m11, SECP256K1_N_C_0); + muladd(m10, SECP256K1_N_C_1); + muladd(m9, SECP256K1_N_C_2); + muladd(m8, SECP256K1_N_C_3); + extract(p3); + sumadd(m4); + muladd(m12, SECP256K1_N_C_0); + muladd(m11, SECP256K1_N_C_1); + muladd(m10, SECP256K1_N_C_2); + muladd(m9, SECP256K1_N_C_3); + sumadd(m8); + extract(p4); + sumadd(m5); + muladd(m12, SECP256K1_N_C_1); + muladd(m11, SECP256K1_N_C_2); + muladd(m10, SECP256K1_N_C_3); + sumadd(m9); + extract(p5); + sumadd(m6); + muladd(m12, SECP256K1_N_C_2); + muladd(m11, SECP256K1_N_C_3); + sumadd(m10); + extract(p6); + sumadd_fast(m7); + muladd_fast(m12, SECP256K1_N_C_3); + sumadd_fast(m11); + extract_fast(p7); + p8 = c0 + m12; + VERIFY_CHECK(p8 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..7] = p[0..7] + p[8] * SECP256K1_N_C. */ + c = p0 + (uint64_t)SECP256K1_N_C_0 * p8; + r->d[0] = c & 0xFFFFFFFFUL; c >>= 32; + c += p1 + (uint64_t)SECP256K1_N_C_1 * p8; + r->d[1] = c & 0xFFFFFFFFUL; c >>= 32; + c += p2 + (uint64_t)SECP256K1_N_C_2 * p8; + r->d[2] = c & 0xFFFFFFFFUL; c >>= 32; + c += p3 + (uint64_t)SECP256K1_N_C_3 * p8; + r->d[3] = c & 0xFFFFFFFFUL; c >>= 32; + c += p4 + (uint64_t)p8; + r->d[4] = c & 0xFFFFFFFFUL; c >>= 32; + c += p5; + r->d[5] = c & 0xFFFFFFFFUL; c >>= 32; + c += p6; + r->d[6] = c & 0xFFFFFFFFUL; c >>= 32; + c += p7; + r->d[7] = c & 0xFFFFFFFFUL; c >>= 32; + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7] * b[0..7]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[0], b->d[4]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + muladd(a->d[4], b->d[0]); + extract(l[4]); + muladd(a->d[0], b->d[5]); + muladd(a->d[1], b->d[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + muladd(a->d[4], b->d[1]); + muladd(a->d[5], b->d[0]); + extract(l[5]); + muladd(a->d[0], b->d[6]); + muladd(a->d[1], b->d[5]); + muladd(a->d[2], b->d[4]); + muladd(a->d[3], b->d[3]); + muladd(a->d[4], b->d[2]); + muladd(a->d[5], b->d[1]); + muladd(a->d[6], b->d[0]); + extract(l[6]); + muladd(a->d[0], b->d[7]); + muladd(a->d[1], b->d[6]); + muladd(a->d[2], b->d[5]); + muladd(a->d[3], b->d[4]); + muladd(a->d[4], b->d[3]); + muladd(a->d[5], b->d[2]); + muladd(a->d[6], b->d[1]); + muladd(a->d[7], b->d[0]); + extract(l[7]); + muladd(a->d[1], b->d[7]); + muladd(a->d[2], b->d[6]); + muladd(a->d[3], b->d[5]); + muladd(a->d[4], b->d[4]); + muladd(a->d[5], b->d[3]); + muladd(a->d[6], b->d[2]); + muladd(a->d[7], b->d[1]); + extract(l[8]); + muladd(a->d[2], b->d[7]); + muladd(a->d[3], b->d[6]); + muladd(a->d[4], b->d[5]); + muladd(a->d[5], b->d[4]); + muladd(a->d[6], b->d[3]); + muladd(a->d[7], b->d[2]); + extract(l[9]); + muladd(a->d[3], b->d[7]); + muladd(a->d[4], b->d[6]); + muladd(a->d[5], b->d[5]); + muladd(a->d[6], b->d[4]); + muladd(a->d[7], b->d[3]); + extract(l[10]); + muladd(a->d[4], b->d[7]); + muladd(a->d[5], b->d[6]); + muladd(a->d[6], b->d[5]); + muladd(a->d[7], b->d[4]); + extract(l[11]); + muladd(a->d[5], b->d[7]); + muladd(a->d[6], b->d[6]); + muladd(a->d[7], b->d[5]); + extract(l[12]); + muladd(a->d[6], b->d[7]); + muladd(a->d[7], b->d[6]); + extract(l[13]); + muladd_fast(a->d[7], b->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint32_t l[16]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (32 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (32 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (32 - n)); + r->d[3] = (r->d[3] >> n) + (r->d[4] << (32 - n)); + r->d[4] = (r->d[4] >> n) + (r->d[5] << (32 - n)); + r->d[5] = (r->d[5] >> n) + (r->d[6] << (32 - n)); + r->d[6] = (r->d[6] >> n) + (r->d[7] << (32 - n)); + r->d[7] = (r->d[7] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = a->d[2]; + r1->d[3] = a->d[3]; + r1->d[4] = 0; + r1->d[5] = 0; + r1->d[6] = 0; + r1->d[7] = 0; + r2->d[0] = a->d[4]; + r2->d[1] = a->d[5]; + r2->d[2] = a->d[6]; + r2->d[3] = a->d[7]; + r2->d[4] = 0; + r2->d[5] = 0; + r2->d[6] = 0; + r2->d[7] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint32_t l[16]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 5; + shiftlow = shift & 0x1F; + shifthigh = 32 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 480 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 480 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 448 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 416 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 416 ? (l[3 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[4 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[4] = shift < 384 ? (l[4 + shiftlimbs] >> shiftlow | (shift < 352 && shiftlow ? (l[5 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[6] = shift < 320 ? (l[6 + shiftlimbs] >> shiftlow | (shift < 288 && shiftlow ? (l[7 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[7] = shift < 288 ? (l[7 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h new file mode 100644 index 000000000..f5b237640 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_impl.h @@ -0,0 +1,370 @@ +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_IMPL_H_ +#define _SECP256K1_SCALAR_IMPL_H_ + +#include "group.h" +#include "scalar.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low_impl.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64_impl.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32_impl.h" +#else +#error "Please select scalar implementation" +#endif + +#ifndef USE_NUM_NONE +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) { + unsigned char c[32]; + secp256k1_scalar_get_b32(c, a); + secp256k1_num_set_bin(r, c, 32); +} + +/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r) { +#if defined(EXHAUSTIVE_TEST_ORDER) + static const unsigned char order[32] = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER + }; +#else + static const unsigned char order[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; +#endif + secp256k1_num_set_bin(r, order, 32); +} +#endif + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(EXHAUSTIVE_TEST_ORDER) + int i; + *r = 0; + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) + if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) + *r = i; + /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus + * have a composite group order; fix it in exhaustive_tests.c). */ + VERIFY_CHECK(*r != 0); +} +#else + secp256k1_scalar *t; + int i; + /* First compute x ^ (2^N - 1) for some values of N. */ + secp256k1_scalar x2, x3, x4, x6, x7, x8, x15, x30, x60, x120, x127; + + secp256k1_scalar_sqr(&x2, x); + secp256k1_scalar_mul(&x2, &x2, x); + + secp256k1_scalar_sqr(&x3, &x2); + secp256k1_scalar_mul(&x3, &x3, x); + + secp256k1_scalar_sqr(&x4, &x3); + secp256k1_scalar_mul(&x4, &x4, x); + + secp256k1_scalar_sqr(&x6, &x4); + secp256k1_scalar_sqr(&x6, &x6); + secp256k1_scalar_mul(&x6, &x6, &x2); + + secp256k1_scalar_sqr(&x7, &x6); + secp256k1_scalar_mul(&x7, &x7, x); + + secp256k1_scalar_sqr(&x8, &x7); + secp256k1_scalar_mul(&x8, &x8, x); + + secp256k1_scalar_sqr(&x15, &x8); + for (i = 0; i < 6; i++) { + secp256k1_scalar_sqr(&x15, &x15); + } + secp256k1_scalar_mul(&x15, &x15, &x7); + + secp256k1_scalar_sqr(&x30, &x15); + for (i = 0; i < 14; i++) { + secp256k1_scalar_sqr(&x30, &x30); + } + secp256k1_scalar_mul(&x30, &x30, &x15); + + secp256k1_scalar_sqr(&x60, &x30); + for (i = 0; i < 29; i++) { + secp256k1_scalar_sqr(&x60, &x60); + } + secp256k1_scalar_mul(&x60, &x60, &x30); + + secp256k1_scalar_sqr(&x120, &x60); + for (i = 0; i < 59; i++) { + secp256k1_scalar_sqr(&x120, &x120); + } + secp256k1_scalar_mul(&x120, &x120, &x60); + + secp256k1_scalar_sqr(&x127, &x120); + for (i = 0; i < 6; i++) { + secp256k1_scalar_sqr(&x127, &x127); + } + secp256k1_scalar_mul(&x127, &x127, &x7); + + /* Then accumulate the final result (t starts at x127). */ + t = &x127; + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 5; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 4; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x4); /* 1111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 4; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 10; i++) { /* 0000000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x3); /* 111 */ + for (i = 0; i < 9; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x8); /* 11111111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x4); /* 1111 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 5; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 4; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 2; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 8; i++) { /* 000000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 3; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, &x2); /* 11 */ + for (i = 0; i < 3; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 6; i++) { /* 00000 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(t, t, x); /* 1 */ + for (i = 0; i < 8; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); + } + secp256k1_scalar_mul(r, t, &x6); /* 111111 */ +} + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(a->d[0] & 1); +} +#endif + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(USE_SCALAR_INV_BUILTIN) + secp256k1_scalar_inverse(r, x); +#elif defined(USE_SCALAR_INV_NUM) + unsigned char b[32]; + secp256k1_num n, m; + secp256k1_scalar t = *x; + secp256k1_scalar_get_b32(b, &t); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_scalar_order_get_num(&m); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + secp256k1_scalar_set_b32(r, b, NULL); + /* Verify that the inverse was computed correctly, without GMP code. */ + secp256k1_scalar_mul(&t, &t, r); + CHECK(secp256k1_scalar_is_one(&t)); +#else +#error "Please select scalar inverse implementation" +#endif +} + +#ifdef USE_ENDOMORPHISM +#if defined(EXHAUSTIVE_TEST_ORDER) +/** + * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the + * full case we don't bother making k1 and k2 be small, we just want them to be + * nontrivial to get full test coverage for the exhaustive tests. We therefore + * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER; + *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; +} +#else +/** + * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where + * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a, + * 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72} + * + * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm + * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 + * and k2 have a small size. + * It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are: + * + * - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3} + * - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8} + * - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * + * The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives + * k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and + * compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2. + * + * g1, g2 are precomputed constants used to replace division with a rounded multiplication + * when decomposing the scalar for an endomorphism-based point multiplication. + * + * The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve + * Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5. + * + * The derivation is described in the paper "Efficient Software Implementation of Public-Key + * Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez), + * Section 4.3 (here we use a somewhat higher-precision estimate): + * d = a1*b2 - b1*a2 + * g1 = round((2^272)*b2/d) + * g2 = round((2^272)*b1/d) + * + * (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found + * as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda'). + * + * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order). + */ + +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + secp256k1_scalar c1, c2; + static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST( + 0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL, + 0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL + ); + static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL + ); + static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL + ); + static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, + 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL + ); + static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, + 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL + ); + VERIFY_CHECK(r1 != a); + VERIFY_CHECK(r2 != a); + /* these _var calls are constant time since the shift amount is constant */ + secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272); + secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272); + secp256k1_scalar_mul(&c1, &c1, &minus_b1); + secp256k1_scalar_mul(&c2, &c2, &minus_b2); + secp256k1_scalar_add(r2, &c1, &c2); + secp256k1_scalar_mul(r1, r2, &minus_lambda); + secp256k1_scalar_add(r1, r1, a); +} +#endif +#endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h new file mode 100644 index 000000000..5574c44c7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low.h @@ -0,0 +1,15 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_ +#define _SECP256K1_SCALAR_REPR_ + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef uint32_t secp256k1_scalar; + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h new file mode 100644 index 000000000..4f94441f4 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/scalar_low_impl.h @@ -0,0 +1,114 @@ +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCALAR_REPR_IMPL_H_ +#define _SECP256K1_SCALAR_REPR_IMPL_H_ + +#include "scalar.h" + +#include + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(*a & 1); +} + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + if (offset < 32) + return ((*a >> offset) & ((((uint32_t)1) << count) - 1)); + else + return 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + return secp256k1_scalar_get_bits(a, offset, count); +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; } + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + return *r < *b; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + if (flag && bit < 32) + *r += (1 << bit); +#ifdef VERIFY + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; + int i; + *r = 0; + for (i = 0; i < 32; i++) { + *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + } + /* just deny overflow, it basically always happens */ + if (overflow) *overflow = 0; +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + memset(bin, 0, 32); + bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return *a == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + if (*a == 0) { + *r = 0; + } else { + *r = EXHAUSTIVE_TEST_ORDER - *a; + } +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return *a == 1; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + return *a > EXHAUSTIVE_TEST_ORDER / 2; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + if (flag) secp256k1_scalar_negate(r, r); + return flag ? -1 : 1; +} + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = *r & ((1 << n) - 1); + *r >>= n; + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r1 = *a; + *r2 = 0; +} + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return *a == *b; +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c new file mode 100755 index 000000000..7d637bfad --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/secp256k1.c @@ -0,0 +1,559 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#include "include/secp256k1.h" + +#include "util.h" +#include "num_impl.h" +#include "field_impl.h" +#include "scalar_impl.h" +#include "group_impl.h" +#include "ecmult_impl.h" +#include "ecmult_const_impl.h" +#include "ecmult_gen_impl.h" +#include "ecdsa_impl.h" +#include "eckey_impl.h" +#include "hash_impl.h" + +#define ARG_CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + secp256k1_callback_call(&ctx->illegal_callback, #cond); \ + return 0; \ + } \ +} while(0) + +static void default_illegal_callback_fn(const char* str, void* data) { + fprintf(stderr, "[libsecp256k1] illegal argument: %s\n", str); + abort(); +} + +static const secp256k1_callback default_illegal_callback = { + default_illegal_callback_fn, + NULL +}; + +static void default_error_callback_fn(const char* str, void* data) { + fprintf(stderr, "[libsecp256k1] internal consistency check failed: %s\n", str); + abort(); +} + +static const secp256k1_callback default_error_callback = { + default_error_callback_fn, + NULL +}; + + +struct secp256k1_context_struct { + secp256k1_ecmult_context ecmult_ctx; + secp256k1_ecmult_gen_context ecmult_gen_ctx; + secp256k1_callback illegal_callback; + secp256k1_callback error_callback; +}; + +secp256k1_context* secp256k1_context_create(unsigned int flags) { + secp256k1_context* ret = (secp256k1_context*)checked_malloc(&default_error_callback, sizeof(secp256k1_context)); + ret->illegal_callback = default_illegal_callback; + ret->error_callback = default_error_callback; + + if (EXPECT((flags & SECP256K1_FLAGS_TYPE_MASK) != SECP256K1_FLAGS_TYPE_CONTEXT, 0)) { + secp256k1_callback_call(&ret->illegal_callback, + "Invalid flags"); + free(ret); + return NULL; + } + + secp256k1_ecmult_context_init(&ret->ecmult_ctx); + secp256k1_ecmult_gen_context_init(&ret->ecmult_gen_ctx); + + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_SIGN) { + secp256k1_ecmult_gen_context_build(&ret->ecmult_gen_ctx, &ret->error_callback); + } + if (flags & SECP256K1_FLAGS_BIT_CONTEXT_VERIFY) { + secp256k1_ecmult_context_build(&ret->ecmult_ctx, &ret->error_callback); + } + + return ret; +} + +secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) { + secp256k1_context* ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, sizeof(secp256k1_context)); + ret->illegal_callback = ctx->illegal_callback; + ret->error_callback = ctx->error_callback; + secp256k1_ecmult_context_clone(&ret->ecmult_ctx, &ctx->ecmult_ctx, &ctx->error_callback); + secp256k1_ecmult_gen_context_clone(&ret->ecmult_gen_ctx, &ctx->ecmult_gen_ctx, &ctx->error_callback); + return ret; +} + +void secp256k1_context_destroy(secp256k1_context* ctx) { + if (ctx != NULL) { + secp256k1_ecmult_context_clear(&ctx->ecmult_ctx); + secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx); + + free(ctx); + } +} + +void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_illegal_callback_fn; + } + ctx->illegal_callback.fn = fun; + ctx->illegal_callback.data = data; +} + +void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) { + if (fun == NULL) { + fun = default_error_callback_fn; + } + ctx->error_callback.fn = fun; + ctx->error_callback.data = data; +} + +static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { + if (sizeof(secp256k1_ge_storage) == 64) { + /* When the secp256k1_ge_storage type is exactly 64 byte, use its + * representation inside secp256k1_pubkey, as conversion is very fast. + * Note that secp256k1_pubkey_save must use the same representation. */ + secp256k1_ge_storage s; + memcpy(&s, &pubkey->data[0], 64); + secp256k1_ge_from_storage(ge, &s); + } else { + /* Otherwise, fall back to 32-byte big endian for X and Y. */ + secp256k1_fe x, y; + secp256k1_fe_set_b32(&x, pubkey->data); + secp256k1_fe_set_b32(&y, pubkey->data + 32); + secp256k1_ge_set_xy(ge, &x, &y); + } + ARG_CHECK(!secp256k1_fe_is_zero(&ge->x)); + return 1; +} + +static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { + if (sizeof(secp256k1_ge_storage) == 64) { + secp256k1_ge_storage s; + secp256k1_ge_to_storage(&s, ge); + memcpy(&pubkey->data[0], &s, 64); + } else { + VERIFY_CHECK(!secp256k1_ge_is_infinity(ge)); + secp256k1_fe_normalize_var(&ge->x); + secp256k1_fe_normalize_var(&ge->y); + secp256k1_fe_get_b32(pubkey->data, &ge->x); + secp256k1_fe_get_b32(pubkey->data + 32, &ge->y); + } +} + +int secp256k1_ec_pubkey_parse(const secp256k1_context* ctx, secp256k1_pubkey* pubkey, const unsigned char *input, size_t inputlen) { + secp256k1_ge Q; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(input != NULL); + if (!secp256k1_eckey_pubkey_parse(&Q, input, inputlen)) { + return 0; + } + secp256k1_pubkey_save(pubkey, &Q); + secp256k1_ge_clear(&Q); + return 1; +} + +int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_pubkey* pubkey, unsigned int flags) { + secp256k1_ge Q; + size_t len; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(*outputlen >= ((flags & SECP256K1_FLAGS_BIT_COMPRESSION) ? 33 : 65)); + len = *outputlen; + *outputlen = 0; + ARG_CHECK(output != NULL); + memset(output, 0, len); + ARG_CHECK(pubkey != NULL); + ARG_CHECK((flags & SECP256K1_FLAGS_TYPE_MASK) == SECP256K1_FLAGS_TYPE_COMPRESSION); + if (secp256k1_pubkey_load(ctx, &Q, pubkey)) { + ret = secp256k1_eckey_pubkey_serialize(&Q, output, &len, flags & SECP256K1_FLAGS_BIT_COMPRESSION); + if (ret) { + *outputlen = len; + } + } + return ret; +} + +static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) { + (void)ctx; + if (sizeof(secp256k1_scalar) == 32) { + /* When the secp256k1_scalar type is exactly 32 byte, use its + * representation inside secp256k1_ecdsa_signature, as conversion is very fast. + * Note that secp256k1_ecdsa_signature_save must use the same representation. */ + memcpy(r, &sig->data[0], 32); + memcpy(s, &sig->data[32], 32); + } else { + secp256k1_scalar_set_b32(r, &sig->data[0], NULL); + secp256k1_scalar_set_b32(s, &sig->data[32], NULL); + } +} + +static void secp256k1_ecdsa_signature_save(secp256k1_ecdsa_signature* sig, const secp256k1_scalar* r, const secp256k1_scalar* s) { + if (sizeof(secp256k1_scalar) == 32) { + memcpy(&sig->data[0], r, 32); + memcpy(&sig->data[32], s, 32); + } else { + secp256k1_scalar_get_b32(&sig->data[0], r); + secp256k1_scalar_get_b32(&sig->data[32], s); + } +} + +int secp256k1_ecdsa_signature_parse_der(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input, size_t inputlen) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input != NULL); + + if (secp256k1_ecdsa_sig_parse(&r, &s, input, inputlen)) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + return 1; + } else { + memset(sig, 0, sizeof(*sig)); + return 0; + } +} + +int secp256k1_ecdsa_signature_parse_compact(const secp256k1_context* ctx, secp256k1_ecdsa_signature* sig, const unsigned char *input64) { + secp256k1_scalar r, s; + int ret = 1; + int overflow = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(input64 != NULL); + + secp256k1_scalar_set_b32(&r, &input64[0], &overflow); + ret &= !overflow; + secp256k1_scalar_set_b32(&s, &input64[32], &overflow); + ret &= !overflow; + if (ret) { + secp256k1_ecdsa_signature_save(sig, &r, &s); + } else { + memset(sig, 0, sizeof(*sig)); + } + return ret; +} + +int secp256k1_ecdsa_signature_serialize_der(const secp256k1_context* ctx, unsigned char *output, size_t *outputlen, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output != NULL); + ARG_CHECK(outputlen != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return secp256k1_ecdsa_sig_serialize(output, outputlen, &r, &s); +} + +int secp256k1_ecdsa_signature_serialize_compact(const secp256k1_context* ctx, unsigned char *output64, const secp256k1_ecdsa_signature* sig) { + secp256k1_scalar r, s; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(output64 != NULL); + ARG_CHECK(sig != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + secp256k1_scalar_get_b32(&output64[0], &r); + secp256k1_scalar_get_b32(&output64[32], &s); + return 1; +} + +int secp256k1_ecdsa_signature_normalize(const secp256k1_context* ctx, secp256k1_ecdsa_signature *sigout, const secp256k1_ecdsa_signature *sigin) { + secp256k1_scalar r, s; + int ret = 0; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sigin != NULL); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, sigin); + ret = secp256k1_scalar_is_high(&s); + if (sigout != NULL) { + if (ret) { + secp256k1_scalar_negate(&s, &s); + } + secp256k1_ecdsa_signature_save(sigout, &r, &s); + } + + return ret; +} + +int secp256k1_ecdsa_verify(const secp256k1_context* ctx, const secp256k1_ecdsa_signature *sig, const unsigned char *msg32, const secp256k1_pubkey *pubkey) { + secp256k1_ge q; + secp256k1_scalar r, s; + secp256k1_scalar m; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(pubkey != NULL); + + secp256k1_scalar_set_b32(&m, msg32, NULL); + secp256k1_ecdsa_signature_load(ctx, &r, &s, sig); + return (!secp256k1_scalar_is_high(&s) && + secp256k1_pubkey_load(ctx, &q, pubkey) && + secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &r, &s, &q, &m)); +} + +static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + unsigned char keydata[112]; + int keylen = 64; + secp256k1_rfc6979_hmac_sha256_t rng; + unsigned int i; + /* We feed a byte array to the PRNG as input, consisting of: + * - the private key (32 bytes) and message (32 bytes), see RFC 6979 3.2d. + * - optionally 32 extra bytes of data, see RFC 6979 3.6 Additional Data. + * - optionally 16 extra bytes with the algorithm name. + * Because the arguments have distinct fixed lengths it is not possible for + * different argument mixtures to emulate each other and result in the same + * nonces. + */ + memcpy(keydata, key32, 32); + memcpy(keydata + 32, msg32, 32); + if (data != NULL) { + memcpy(keydata + 64, data, 32); + keylen = 96; + } + if (algo16 != NULL) { + memcpy(keydata + keylen, algo16, 16); + keylen += 16; + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, keylen); + memset(keydata, 0, sizeof(keydata)); + for (i = 0; i <= counter; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + return 1; +} + +const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; +const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; + +int secp256k1_ecdsa_sign(const secp256k1_context* ctx, secp256k1_ecdsa_signature *signature, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, const void* noncedata) { + secp256k1_scalar r, s; + secp256k1_scalar sec, non, msg; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(signature != NULL); + ARG_CHECK(seckey != NULL); + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_default; + } + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (!overflow && !secp256k1_scalar_is_zero(&sec)) { + unsigned char nonce32[32]; + unsigned int count = 0; + secp256k1_scalar_set_b32(&msg, msg32, NULL); + while (1) { + ret = noncefp(nonce32, msg32, seckey, NULL, (void*)noncedata, count); + if (!ret) { + break; + } + secp256k1_scalar_set_b32(&non, nonce32, &overflow); + if (!overflow && !secp256k1_scalar_is_zero(&non)) { + if (secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, &r, &s, &sec, &msg, &non, NULL)) { + break; + } + } + count++; + } + memset(nonce32, 0, 32); + secp256k1_scalar_clear(&msg); + secp256k1_scalar_clear(&non); + secp256k1_scalar_clear(&sec); + } + if (ret) { + secp256k1_ecdsa_signature_save(signature, &r, &s); + } else { + memset(signature, 0, sizeof(*signature)); + } + return ret; +} + +int secp256k1_ec_seckey_verify(const secp256k1_context* ctx, const unsigned char *seckey) { + secp256k1_scalar sec; + int ret; + int overflow; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + ret = !overflow && !secp256k1_scalar_is_zero(&sec); + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_pubkey_create(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *seckey) { + secp256k1_gej pj; + secp256k1_ge p; + secp256k1_scalar sec; + int overflow; + int ret = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(pubkey != NULL); + memset(pubkey, 0, sizeof(*pubkey)); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(seckey != NULL); + + secp256k1_scalar_set_b32(&sec, seckey, &overflow); + ret = (!overflow) & (!secp256k1_scalar_is_zero(&sec)); + if (ret) { + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &sec); + secp256k1_ge_set_gej(&p, &pj); + secp256k1_pubkey_save(pubkey, &p); + } + secp256k1_scalar_clear(&sec); + return ret; +} + +int secp256k1_ec_privkey_tweak_add(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar term; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + secp256k1_scalar_set_b32(&sec, seckey, NULL); + + ret = !overflow && secp256k1_eckey_privkey_tweak_add(&sec, &term); + memset(seckey, 0, 32); + if (ret) { + secp256k1_scalar_get_b32(seckey, &sec); + } + + secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&term); + return ret; +} + +int secp256k1_ec_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar term; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&term, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_add(&ctx->ecmult_ctx, &p, &term)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_ec_privkey_tweak_mul(const secp256k1_context* ctx, unsigned char *seckey, const unsigned char *tweak) { + secp256k1_scalar factor; + secp256k1_scalar sec; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(seckey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&factor, tweak, &overflow); + secp256k1_scalar_set_b32(&sec, seckey, NULL); + ret = !overflow && secp256k1_eckey_privkey_tweak_mul(&sec, &factor); + memset(seckey, 0, 32); + if (ret) { + secp256k1_scalar_get_b32(seckey, &sec); + } + + secp256k1_scalar_clear(&sec); + secp256k1_scalar_clear(&factor); + return ret; +} + +int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const unsigned char *tweak) { + secp256k1_ge p; + secp256k1_scalar factor; + int ret = 0; + int overflow = 0; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkey != NULL); + ARG_CHECK(tweak != NULL); + + secp256k1_scalar_set_b32(&factor, tweak, &overflow); + ret = !overflow && secp256k1_pubkey_load(ctx, &p, pubkey); + memset(pubkey, 0, sizeof(*pubkey)); + if (ret) { + if (secp256k1_eckey_pubkey_tweak_mul(&ctx->ecmult_ctx, &p, &factor)) { + secp256k1_pubkey_save(pubkey, &p); + } else { + ret = 0; + } + } + + return ret; +} + +int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + return 1; +} + +int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey *pubnonce, const secp256k1_pubkey * const *pubnonces, size_t n) { + size_t i; + secp256k1_gej Qj; + secp256k1_ge Q; + + ARG_CHECK(pubnonce != NULL); + memset(pubnonce, 0, sizeof(*pubnonce)); + ARG_CHECK(n >= 1); + ARG_CHECK(pubnonces != NULL); + + secp256k1_gej_set_infinity(&Qj); + + for (i = 0; i < n; i++) { + secp256k1_pubkey_load(ctx, &Q, pubnonces[i]); + secp256k1_gej_add_ge(&Qj, &Qj, &Q); + } + if (secp256k1_gej_is_infinity(&Qj)) { + return 0; + } + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(pubnonce, &Q); + return 1; +} + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORR +# include "modules/schnorr/main_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/main_impl.h" +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h new file mode 100644 index 000000000..f8efa93c7 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand.h @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_TESTRAND_H_ +#define _SECP256K1_TESTRAND_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +/* A non-cryptographic RNG used only for test infrastructure. */ + +/** Seed the pseudorandom number generator for testing. */ +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16); + +/** Generate a pseudorandom number in the range [0..2**32-1]. */ +static uint32_t secp256k1_rand32(void); + +/** Generate a pseudorandom number in the range [0..2**bits-1]. Bits must be 1 or + * more. */ +static uint32_t secp256k1_rand_bits(int bits); + +/** Generate a pseudorandom number in the range [0..range-1]. */ +static uint32_t secp256k1_rand_int(uint32_t range); + +/** Generate a pseudorandom 32-byte array. */ +static void secp256k1_rand256(unsigned char *b32); + +/** Generate a pseudorandom 32-byte array with long sequences of zero and one bits. */ +static void secp256k1_rand256_test(unsigned char *b32); + +/** Generate pseudorandom bytes with long sequences of zero and one bits. */ +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len); + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h new file mode 100644 index 000000000..15c7b9f12 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/testrand_impl.h @@ -0,0 +1,110 @@ +/********************************************************************** + * Copyright (c) 2013-2015 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_TESTRAND_IMPL_H_ +#define _SECP256K1_TESTRAND_IMPL_H_ + +#include +#include + +#include "testrand.h" +#include "hash.h" + +static secp256k1_rfc6979_hmac_sha256_t secp256k1_test_rng; +static uint32_t secp256k1_test_rng_precomputed[8]; +static int secp256k1_test_rng_precomputed_used = 8; +static uint64_t secp256k1_test_rng_integer; +static int secp256k1_test_rng_integer_bits_left = 0; + +SECP256K1_INLINE static void secp256k1_rand_seed(const unsigned char *seed16) { + secp256k1_rfc6979_hmac_sha256_initialize(&secp256k1_test_rng, seed16, 16); +} + +SECP256K1_INLINE static uint32_t secp256k1_rand32(void) { + if (secp256k1_test_rng_precomputed_used == 8) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, (unsigned char*)(&secp256k1_test_rng_precomputed[0]), sizeof(secp256k1_test_rng_precomputed)); + secp256k1_test_rng_precomputed_used = 0; + } + return secp256k1_test_rng_precomputed[secp256k1_test_rng_precomputed_used++]; +} + +static uint32_t secp256k1_rand_bits(int bits) { + uint32_t ret; + if (secp256k1_test_rng_integer_bits_left < bits) { + secp256k1_test_rng_integer |= (((uint64_t)secp256k1_rand32()) << secp256k1_test_rng_integer_bits_left); + secp256k1_test_rng_integer_bits_left += 32; + } + ret = secp256k1_test_rng_integer; + secp256k1_test_rng_integer >>= bits; + secp256k1_test_rng_integer_bits_left -= bits; + ret &= ((~((uint32_t)0)) >> (32 - bits)); + return ret; +} + +static uint32_t secp256k1_rand_int(uint32_t range) { + /* We want a uniform integer between 0 and range-1, inclusive. + * B is the smallest number such that range <= 2**B. + * two mechanisms implemented here: + * - generate B bits numbers until one below range is found, and return it + * - find the largest multiple M of range that is <= 2**(B+A), generate B+A + * bits numbers until one below M is found, and return it modulo range + * The second mechanism consumes A more bits of entropy in every iteration, + * but may need fewer iterations due to M being closer to 2**(B+A) then + * range is to 2**B. The array below (indexed by B) contains a 0 when the + * first mechanism is to be used, and the number A otherwise. + */ + static const int addbits[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 1, 0}; + uint32_t trange, mult; + int bits = 0; + if (range <= 1) { + return 0; + } + trange = range - 1; + while (trange > 0) { + trange >>= 1; + bits++; + } + if (addbits[bits]) { + bits = bits + addbits[bits]; + mult = ((~((uint32_t)0)) >> (32 - bits)) / range; + trange = range * mult; + } else { + trange = range; + mult = 1; + } + while(1) { + uint32_t x = secp256k1_rand_bits(bits); + if (x < trange) { + return (mult == 1) ? x : (x % range); + } + } +} + +static void secp256k1_rand256(unsigned char *b32) { + secp256k1_rfc6979_hmac_sha256_generate(&secp256k1_test_rng, b32, 32); +} + +static void secp256k1_rand_bytes_test(unsigned char *bytes, size_t len) { + size_t bits = 0; + memset(bytes, 0, len); + while (bits < len * 8) { + int now; + uint32_t val; + now = 1 + (secp256k1_rand_bits(6) * secp256k1_rand_bits(5) + 16) / 31; + val = secp256k1_rand_bits(1); + while (now > 0 && bits < len * 8) { + bytes[bits / 8] |= val << (bits % 8); + now--; + bits++; + } + } +} + +static void secp256k1_rand256_test(unsigned char *b32) { + secp256k1_rand_bytes_test(b32, 32); +} + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c new file mode 100644 index 000000000..9ae7d3028 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests.c @@ -0,0 +1,4525 @@ +/********************************************************************** + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include + +#include "secp256k1.c" +#include "include/secp256k1.h" +#include "testrand_impl.h" + +#ifdef ENABLE_OPENSSL_TESTS +#include "openssl/bn.h" +#include "openssl/ec.h" +#include "openssl/ecdsa.h" +#include "openssl/obj_mac.h" +#endif + +#include "contrib/lax_der_parsing.c" +#include "contrib/lax_der_privatekey_parsing.c" + +#if !defined(VG_CHECK) +# if defined(VALGRIND) +# include +# define VG_UNDEF(x,y) VALGRIND_MAKE_MEM_UNDEFINED((x),(y)) +# define VG_CHECK(x,y) VALGRIND_CHECK_MEM_IS_DEFINED((x),(y)) +# else +# define VG_UNDEF(x,y) +# define VG_CHECK(x,y) +# endif +#endif + +static int count = 64; +static secp256k1_context *ctx = NULL; + +static void counting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts. */ + int32_t *p; + (void)str; + p = data; + (*p)++; +} + +static void uncounting_illegal_callback_fn(const char* str, void* data) { + /* Dummy callback function that just counts (backwards). */ + int32_t *p; + (void)str; + p = data; + (*p)--; +} + +void random_field_element_test(secp256k1_fe *fe) { + do { + unsigned char b32[32]; + secp256k1_rand256_test(b32); + if (secp256k1_fe_set_b32(fe, b32)) { + break; + } + } while(1); +} + +void random_field_element_magnitude(secp256k1_fe *fe) { + secp256k1_fe zero; + int n = secp256k1_rand_int(9); + secp256k1_fe_normalize(fe); + if (n == 0) { + return; + } + secp256k1_fe_clear(&zero); + secp256k1_fe_negate(&zero, &zero, 0); + secp256k1_fe_mul_int(&zero, n - 1); + secp256k1_fe_add(fe, &zero); + VERIFY_CHECK(fe->magnitude == n); +} + +void random_group_element_test(secp256k1_ge *ge) { + secp256k1_fe fe; + do { + random_field_element_test(&fe); + if (secp256k1_ge_set_xo_var(ge, &fe, secp256k1_rand_bits(1))) { + secp256k1_fe_normalize(&ge->y); + break; + } + } while(1); +} + +void random_group_element_jacobian_test(secp256k1_gej *gej, const secp256k1_ge *ge) { + secp256k1_fe z2, z3; + do { + random_field_element_test(&gej->z); + if (!secp256k1_fe_is_zero(&gej->z)) { + break; + } + } while(1); + secp256k1_fe_sqr(&z2, &gej->z); + secp256k1_fe_mul(&z3, &z2, &gej->z); + secp256k1_fe_mul(&gej->x, &ge->x, &z2); + secp256k1_fe_mul(&gej->y, &ge->y, &z3); + gej->infinity = ge->infinity; +} + +void random_scalar_order_test(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256_test(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +void random_scalar_order(secp256k1_scalar *num) { + do { + unsigned char b32[32]; + int overflow = 0; + secp256k1_rand256(b32); + secp256k1_scalar_set_b32(num, b32, &overflow); + if (overflow || secp256k1_scalar_is_zero(num)) { + continue; + } + break; + } while(1); +} + +void run_context_tests(void) { + secp256k1_pubkey pubkey; + secp256k1_ecdsa_signature sig; + unsigned char ctmp[32]; + int32_t ecount; + int32_t ecount2; + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar msg, key, nonce; + secp256k1_scalar sigr, sigs; + + ecount = 0; + ecount2 = 10; + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount2); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, NULL); + CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + + /*** clone and destroy all of them to make sure cloning was complete ***/ + { + secp256k1_context *ctx_tmp; + + ctx_tmp = none; none = secp256k1_context_clone(none); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = sign; sign = secp256k1_context_clone(sign); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = vrfy; vrfy = secp256k1_context_clone(vrfy); secp256k1_context_destroy(ctx_tmp); + ctx_tmp = both; both = secp256k1_context_clone(both); secp256k1_context_destroy(ctx_tmp); + } + + /* Verify that the error callback makes it across the clone. */ + CHECK(vrfy->error_callback.fn != sign->error_callback.fn); + /* And that it resets back to default. */ + secp256k1_context_set_error_callback(sign, NULL, NULL); + CHECK(vrfy->error_callback.fn == sign->error_callback.fn); + + /*** attempt to use them ***/ + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&both->ecmult_gen_ctx, &pubj, &key); + secp256k1_ge_set_gej(&pub, &pubj); + + /* Verify context-type checking illegal-argument errors. */ + memset(ctmp, 1, 32); + CHECK(secp256k1_ec_pubkey_create(vrfy, &pubkey, ctmp) == 0); + CHECK(ecount == 1); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(sign, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ecdsa_sign(vrfy, &sig, ctmp, ctmp, NULL, NULL) == 0); + CHECK(ecount == 2); + VG_UNDEF(&sig, sizeof(sig)); + CHECK(secp256k1_ecdsa_sign(sign, &sig, ctmp, ctmp, NULL, NULL) == 1); + VG_CHECK(&sig, sizeof(sig)); + CHECK(ecount2 == 10); + CHECK(secp256k1_ecdsa_verify(sign, &sig, ctmp, &pubkey) == 0); + CHECK(ecount2 == 11); + CHECK(secp256k1_ecdsa_verify(vrfy, &sig, ctmp, &pubkey) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_add(sign, &pubkey, ctmp) == 0); + CHECK(ecount2 == 12); + CHECK(secp256k1_ec_pubkey_tweak_add(vrfy, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_ec_pubkey_tweak_mul(sign, &pubkey, ctmp) == 0); + CHECK(ecount2 == 13); + CHECK(secp256k1_ec_pubkey_tweak_mul(vrfy, &pubkey, ctmp) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_context_randomize(vrfy, ctmp) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_context_randomize(sign, NULL) == 1); + CHECK(ecount2 == 13); + secp256k1_context_set_illegal_callback(vrfy, NULL, NULL); + secp256k1_context_set_illegal_callback(sign, NULL, NULL); + + /* This shouldn't leak memory, due to already-set tests. */ + secp256k1_ecmult_gen_context_build(&sign->ecmult_gen_ctx, NULL); + secp256k1_ecmult_context_build(&vrfy->ecmult_ctx, NULL); + + /* obtain a working nonce */ + do { + random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + + /* try signing */ + CHECK(secp256k1_ecdsa_sig_sign(&sign->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + CHECK(secp256k1_ecdsa_sig_sign(&both->ecmult_gen_ctx, &sigr, &sigs, &key, &msg, &nonce, NULL)); + + /* try verifying */ + CHECK(secp256k1_ecdsa_sig_verify(&vrfy->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + CHECK(secp256k1_ecdsa_sig_verify(&both->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + + /* cleanup */ + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); + /* Defined as no-op. */ + secp256k1_context_destroy(NULL); +} + +/***** HASH TESTS *****/ + +void run_sha256_tests(void) { + static const char *inputs[8] = { + "", "abc", "message digest", "secure hash algorithm", "SHA256 is considered to be safe", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "For this sample, this 63-byte string will be used as input data", + "This is exactly 64 bytes long, not counting the terminating byte" + }; + static const unsigned char outputs[8][32] = { + {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}, + {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}, + {0xf7, 0x84, 0x6f, 0x55, 0xcf, 0x23, 0xe1, 0x4e, 0xeb, 0xea, 0xb5, 0xb4, 0xe1, 0x55, 0x0c, 0xad, 0x5b, 0x50, 0x9e, 0x33, 0x48, 0xfb, 0xc4, 0xef, 0xa3, 0xa1, 0x41, 0x3d, 0x39, 0x3c, 0xb6, 0x50}, + {0xf3, 0x0c, 0xeb, 0x2b, 0xb2, 0x82, 0x9e, 0x79, 0xe4, 0xca, 0x97, 0x53, 0xd3, 0x5a, 0x8e, 0xcc, 0x00, 0x26, 0x2d, 0x16, 0x4c, 0xc0, 0x77, 0x08, 0x02, 0x95, 0x38, 0x1c, 0xbd, 0x64, 0x3f, 0x0d}, + {0x68, 0x19, 0xd9, 0x15, 0xc7, 0x3f, 0x4d, 0x1e, 0x77, 0xe4, 0xe1, 0xb5, 0x2d, 0x1f, 0xa0, 0xf9, 0xcf, 0x9b, 0xea, 0xea, 0xd3, 0x93, 0x9f, 0x15, 0x87, 0x4b, 0xd9, 0x88, 0xe2, 0xa2, 0x36, 0x30}, + {0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1}, + {0xf0, 0x8a, 0x78, 0xcb, 0xba, 0xee, 0x08, 0x2b, 0x05, 0x2a, 0xe0, 0x70, 0x8f, 0x32, 0xfa, 0x1e, 0x50, 0xc5, 0xc4, 0x21, 0xaa, 0x77, 0x2b, 0xa5, 0xdb, 0xb4, 0x06, 0xa2, 0xea, 0x6b, 0xe3, 0x42}, + {0xab, 0x64, 0xef, 0xf7, 0xe8, 0x8e, 0x2e, 0x46, 0x16, 0x5e, 0x29, 0xf2, 0xbc, 0xe4, 0x18, 0x26, 0xbd, 0x4c, 0x7b, 0x35, 0x52, 0xf6, 0xb3, 0x82, 0xa9, 0xe7, 0xd3, 0xaf, 0x47, 0xc2, 0x45, 0xf8} + }; + int i; + for (i = 0; i < 8; i++) { + unsigned char out[32]; + secp256k1_sha256_t hasher; + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_int(strlen(inputs[i])); + secp256k1_sha256_initialize(&hasher); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + secp256k1_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +void run_hmac_sha256_tests(void) { + static const char *keys[6] = { + "\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", + "\x4a\x65\x66\x65", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa", + "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + }; + static const char *inputs[6] = { + "\x48\x69\x20\x54\x68\x65\x72\x65", + "\x77\x68\x61\x74\x20\x64\x6f\x20\x79\x61\x20\x77\x61\x6e\x74\x20\x66\x6f\x72\x20\x6e\x6f\x74\x68\x69\x6e\x67\x3f", + "\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd\xdd", + "\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd\xcd", + "\x54\x65\x73\x74\x20\x55\x73\x69\x6e\x67\x20\x4c\x61\x72\x67\x65\x72\x20\x54\x68\x61\x6e\x20\x42\x6c\x6f\x63\x6b\x2d\x53\x69\x7a\x65\x20\x4b\x65\x79\x20\x2d\x20\x48\x61\x73\x68\x20\x4b\x65\x79\x20\x46\x69\x72\x73\x74", + "\x54\x68\x69\x73\x20\x69\x73\x20\x61\x20\x74\x65\x73\x74\x20\x75\x73\x69\x6e\x67\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x6b\x65\x79\x20\x61\x6e\x64\x20\x61\x20\x6c\x61\x72\x67\x65\x72\x20\x74\x68\x61\x6e\x20\x62\x6c\x6f\x63\x6b\x2d\x73\x69\x7a\x65\x20\x64\x61\x74\x61\x2e\x20\x54\x68\x65\x20\x6b\x65\x79\x20\x6e\x65\x65\x64\x73\x20\x74\x6f\x20\x62\x65\x20\x68\x61\x73\x68\x65\x64\x20\x62\x65\x66\x6f\x72\x65\x20\x62\x65\x69\x6e\x67\x20\x75\x73\x65\x64\x20\x62\x79\x20\x74\x68\x65\x20\x48\x4d\x41\x43\x20\x61\x6c\x67\x6f\x72\x69\x74\x68\x6d\x2e" + }; + static const unsigned char outputs[6][32] = { + {0xb0, 0x34, 0x4c, 0x61, 0xd8, 0xdb, 0x38, 0x53, 0x5c, 0xa8, 0xaf, 0xce, 0xaf, 0x0b, 0xf1, 0x2b, 0x88, 0x1d, 0xc2, 0x00, 0xc9, 0x83, 0x3d, 0xa7, 0x26, 0xe9, 0x37, 0x6c, 0x2e, 0x32, 0xcf, 0xf7}, + {0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43}, + {0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8, 0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8, 0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe}, + {0x82, 0x55, 0x8a, 0x38, 0x9a, 0x44, 0x3c, 0x0e, 0xa4, 0xcc, 0x81, 0x98, 0x99, 0xf2, 0x08, 0x3a, 0x85, 0xf0, 0xfa, 0xa3, 0xe5, 0x78, 0xf8, 0x07, 0x7a, 0x2e, 0x3f, 0xf4, 0x67, 0x29, 0x66, 0x5b}, + {0x60, 0xe4, 0x31, 0x59, 0x1e, 0xe0, 0xb6, 0x7f, 0x0d, 0x8a, 0x26, 0xaa, 0xcb, 0xf5, 0xb7, 0x7f, 0x8e, 0x0b, 0xc6, 0x21, 0x37, 0x28, 0xc5, 0x14, 0x05, 0x46, 0x04, 0x0f, 0x0e, 0xe3, 0x7f, 0x54}, + {0x9b, 0x09, 0xff, 0xa7, 0x1b, 0x94, 0x2f, 0xcb, 0x27, 0x63, 0x5f, 0xbc, 0xd5, 0xb0, 0xe9, 0x44, 0xbf, 0xdc, 0x63, 0x64, 0x4f, 0x07, 0x13, 0x93, 0x8a, 0x7f, 0x51, 0x53, 0x5c, 0x3a, 0x35, 0xe2} + }; + int i; + for (i = 0; i < 6; i++) { + secp256k1_hmac_sha256_t hasher; + unsigned char out[32]; + secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), strlen(inputs[i])); + secp256k1_hmac_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + if (strlen(inputs[i]) > 0) { + int split = secp256k1_rand_int(strlen(inputs[i])); + secp256k1_hmac_sha256_initialize(&hasher, (const unsigned char*)(keys[i]), strlen(keys[i])); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i]), split); + secp256k1_hmac_sha256_write(&hasher, (const unsigned char*)(inputs[i] + split), strlen(inputs[i]) - split); + secp256k1_hmac_sha256_finalize(&hasher, out); + CHECK(memcmp(out, outputs[i], 32) == 0); + } + } +} + +void run_rfc6979_hmac_sha256_tests(void) { + static const unsigned char key1[65] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, 0x4b, 0xf5, 0x12, 0x2f, 0x34, 0x45, 0x54, 0xc5, 0x3b, 0xde, 0x2e, 0xbb, 0x8c, 0xd2, 0xb7, 0xe3, 0xd1, 0x60, 0x0a, 0xd6, 0x31, 0xc3, 0x85, 0xa5, 0xd7, 0xcc, 0xe2, 0x3c, 0x77, 0x85, 0x45, 0x9a, 0}; + static const unsigned char out1[3][32] = { + {0x4f, 0xe2, 0x95, 0x25, 0xb2, 0x08, 0x68, 0x09, 0x15, 0x9a, 0xcd, 0xf0, 0x50, 0x6e, 0xfb, 0x86, 0xb0, 0xec, 0x93, 0x2c, 0x7b, 0xa4, 0x42, 0x56, 0xab, 0x32, 0x1e, 0x42, 0x1e, 0x67, 0xe9, 0xfb}, + {0x2b, 0xf0, 0xff, 0xf1, 0xd3, 0xc3, 0x78, 0xa2, 0x2d, 0xc5, 0xde, 0x1d, 0x85, 0x65, 0x22, 0x32, 0x5c, 0x65, 0xb5, 0x04, 0x49, 0x1a, 0x0c, 0xbd, 0x01, 0xcb, 0x8f, 0x3a, 0xa6, 0x7f, 0xfd, 0x4a}, + {0xf5, 0x28, 0xb4, 0x10, 0xcb, 0x54, 0x1f, 0x77, 0x00, 0x0d, 0x7a, 0xfb, 0x6c, 0x5b, 0x53, 0xc5, 0xc4, 0x71, 0xea, 0xb4, 0x3e, 0x46, 0x6d, 0x9a, 0xc5, 0x19, 0x0c, 0x39, 0xc8, 0x2f, 0xd8, 0x2e} + }; + + static const unsigned char key2[64] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}; + static const unsigned char out2[3][32] = { + {0x9c, 0x23, 0x6c, 0x16, 0x5b, 0x82, 0xae, 0x0c, 0xd5, 0x90, 0x65, 0x9e, 0x10, 0x0b, 0x6b, 0xab, 0x30, 0x36, 0xe7, 0xba, 0x8b, 0x06, 0x74, 0x9b, 0xaf, 0x69, 0x81, 0xe1, 0x6f, 0x1a, 0x2b, 0x95}, + {0xdf, 0x47, 0x10, 0x61, 0x62, 0x5b, 0xc0, 0xea, 0x14, 0xb6, 0x82, 0xfe, 0xee, 0x2c, 0x9c, 0x02, 0xf2, 0x35, 0xda, 0x04, 0x20, 0x4c, 0x1d, 0x62, 0xa1, 0x53, 0x6c, 0x6e, 0x17, 0xae, 0xd7, 0xa9}, + {0x75, 0x97, 0x88, 0x7c, 0xbd, 0x76, 0x32, 0x1f, 0x32, 0xe3, 0x04, 0x40, 0x67, 0x9a, 0x22, 0xcf, 0x7f, 0x8d, 0x9d, 0x2e, 0xac, 0x39, 0x0e, 0x58, 0x1f, 0xea, 0x09, 0x1c, 0xe2, 0x02, 0xba, 0x94} + }; + + secp256k1_rfc6979_hmac_sha256_t rng; + unsigned char out[32]; + int i; + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 64); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out1[i], 32) == 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key1, 65); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out1[i], 32) != 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); + + secp256k1_rfc6979_hmac_sha256_initialize(&rng, key2, 64); + for (i = 0; i < 3; i++) { + secp256k1_rfc6979_hmac_sha256_generate(&rng, out, 32); + CHECK(memcmp(out, out2[i], 32) == 0); + } + secp256k1_rfc6979_hmac_sha256_finalize(&rng); +} + +/***** RANDOM TESTS *****/ + +void test_rand_bits(int rand32, int bits) { + /* (1-1/2^B)^rounds[B] < 1/10^9, so rounds is the number of iterations to + * get a false negative chance below once in a billion */ + static const unsigned int rounds[7] = {1, 30, 73, 156, 322, 653, 1316}; + /* We try multiplying the results with various odd numbers, which shouldn't + * influence the uniform distribution modulo a power of 2. */ + static const uint32_t mults[6] = {1, 3, 21, 289, 0x9999, 0x80402011}; + /* We only select up to 6 bits from the output to analyse */ + unsigned int usebits = bits > 6 ? 6 : bits; + unsigned int maxshift = bits - usebits; + /* For each of the maxshift+1 usebits-bit sequences inside a bits-bit + number, track all observed outcomes, one per bit in a uint64_t. */ + uint64_t x[6][27] = {{0}}; + unsigned int i, shift, m; + /* Multiply the output of all rand calls with the odd number m, which + should not change the uniformity of its distribution. */ + for (i = 0; i < rounds[usebits]; i++) { + uint32_t r = (rand32 ? secp256k1_rand32() : secp256k1_rand_bits(bits)); + CHECK((((uint64_t)r) >> bits) == 0); + for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { + uint32_t rm = r * mults[m]; + for (shift = 0; shift <= maxshift; shift++) { + x[m][shift] |= (((uint64_t)1) << ((rm >> shift) & ((1 << usebits) - 1))); + } + } + } + for (m = 0; m < sizeof(mults) / sizeof(mults[0]); m++) { + for (shift = 0; shift <= maxshift; shift++) { + /* Test that the lower usebits bits of x[shift] are 1 */ + CHECK(((~x[m][shift]) << (64 - (1 << usebits))) == 0); + } + } +} + +/* Subrange must be a whole divisor of range, and at most 64 */ +void test_rand_int(uint32_t range, uint32_t subrange) { + /* (1-1/subrange)^rounds < 1/10^9 */ + int rounds = (subrange * 2073) / 100; + int i; + uint64_t x = 0; + CHECK((range % subrange) == 0); + for (i = 0; i < rounds; i++) { + uint32_t r = secp256k1_rand_int(range); + CHECK(r < range); + r = r % subrange; + x |= (((uint64_t)1) << r); + } + /* Test that the lower subrange bits of x are 1. */ + CHECK(((~x) << (64 - subrange)) == 0); +} + +void run_rand_bits(void) { + size_t b; + test_rand_bits(1, 32); + for (b = 1; b <= 32; b++) { + test_rand_bits(0, b); + } +} + +void run_rand_int(void) { + static const uint32_t ms[] = {1, 3, 17, 1000, 13771, 999999, 33554432}; + static const uint32_t ss[] = {1, 3, 6, 9, 13, 31, 64}; + unsigned int m, s; + for (m = 0; m < sizeof(ms) / sizeof(ms[0]); m++) { + for (s = 0; s < sizeof(ss) / sizeof(ss[0]); s++) { + test_rand_int(ms[m] * ss[s], ss[s]); + } + } +} + +/***** NUM TESTS *****/ + +#ifndef USE_NUM_NONE +void random_num_negate(secp256k1_num *num) { + if (secp256k1_rand_bits(1)) { + secp256k1_num_negate(num); + } +} + +void random_num_order_test(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order_test(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +void random_num_order(secp256k1_num *num) { + secp256k1_scalar sc; + random_scalar_order(&sc); + secp256k1_scalar_get_num(num, &sc); +} + +void test_num_negate(void) { + secp256k1_num n1; + secp256k1_num n2; + random_num_order_test(&n1); /* n1 = R */ + random_num_negate(&n1); + secp256k1_num_copy(&n2, &n1); /* n2 = R */ + secp256k1_num_sub(&n1, &n2, &n1); /* n1 = n2-n1 = 0 */ + CHECK(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); /* n1 = R */ + secp256k1_num_negate(&n1); /* n1 = -R */ + CHECK(!secp256k1_num_is_zero(&n1)); + secp256k1_num_add(&n1, &n2, &n1); /* n1 = n2+n1 = 0 */ + CHECK(secp256k1_num_is_zero(&n1)); + secp256k1_num_copy(&n1, &n2); /* n1 = R */ + secp256k1_num_negate(&n1); /* n1 = -R */ + CHECK(secp256k1_num_is_neg(&n1) != secp256k1_num_is_neg(&n2)); + secp256k1_num_negate(&n1); /* n1 = R */ + CHECK(secp256k1_num_eq(&n1, &n2)); +} + +void test_num_add_sub(void) { + int i; + secp256k1_scalar s; + secp256k1_num n1; + secp256k1_num n2; + secp256k1_num n1p2, n2p1, n1m2, n2m1; + random_num_order_test(&n1); /* n1 = R1 */ + if (secp256k1_rand_bits(1)) { + random_num_negate(&n1); + } + random_num_order_test(&n2); /* n2 = R2 */ + if (secp256k1_rand_bits(1)) { + random_num_negate(&n2); + } + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = R1 + R2 */ + secp256k1_num_add(&n2p1, &n2, &n1); /* n2p1 = R2 + R1 */ + secp256k1_num_sub(&n1m2, &n1, &n2); /* n1m2 = R1 - R2 */ + secp256k1_num_sub(&n2m1, &n2, &n1); /* n2m1 = R2 - R1 */ + CHECK(secp256k1_num_eq(&n1p2, &n2p1)); + CHECK(!secp256k1_num_eq(&n1p2, &n1m2)); + secp256k1_num_negate(&n2m1); /* n2m1 = -R2 + R1 */ + CHECK(secp256k1_num_eq(&n2m1, &n1m2)); + CHECK(!secp256k1_num_eq(&n2m1, &n1)); + secp256k1_num_add(&n2m1, &n2m1, &n2); /* n2m1 = -R2 + R1 + R2 = R1 */ + CHECK(secp256k1_num_eq(&n2m1, &n1)); + CHECK(!secp256k1_num_eq(&n2p1, &n1)); + secp256k1_num_sub(&n2p1, &n2p1, &n2); /* n2p1 = R2 + R1 - R2 = R1 */ + CHECK(secp256k1_num_eq(&n2p1, &n1)); + + /* check is_one */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&n1, &s); + CHECK(secp256k1_num_is_one(&n1)); + /* check that 2^n + 1 is never 1 */ + secp256k1_scalar_get_num(&n2, &s); + for (i = 0; i < 250; ++i) { + secp256k1_num_add(&n1, &n1, &n1); /* n1 *= 2 */ + secp256k1_num_add(&n1p2, &n1, &n2); /* n1p2 = n1 + 1 */ + CHECK(!secp256k1_num_is_one(&n1p2)); + } +} + +void test_num_mod(void) { + int i; + secp256k1_scalar s; + secp256k1_num order, n; + + /* check that 0 mod anything is 0 */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_set_int(&s, 0); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that anything mod 1 is 0 */ + secp256k1_scalar_set_int(&s, 1); + secp256k1_scalar_get_num(&order, &s); + secp256k1_scalar_get_num(&n, &s); + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); + + /* check that increasing the number past 2^256 does not break this */ + random_scalar_order_test(&s); + secp256k1_scalar_get_num(&n, &s); + /* multiply by 2^8, which'll test this case with high probability */ + for (i = 0; i < 8; ++i) { + secp256k1_num_add(&n, &n, &n); + } + secp256k1_num_mod(&n, &order); + CHECK(secp256k1_num_is_zero(&n)); +} + +void test_num_jacobi(void) { + secp256k1_scalar sqr; + secp256k1_scalar small; + secp256k1_scalar five; /* five is not a quadratic residue */ + secp256k1_num order, n; + int i; + /* squares mod 5 are 1, 4 */ + const int jacobi5[10] = { 0, 1, -1, -1, 1, 0, 1, -1, -1, 1 }; + + /* check some small values with 5 as the order */ + secp256k1_scalar_set_int(&five, 5); + secp256k1_scalar_get_num(&order, &five); + for (i = 0; i < 10; ++i) { + secp256k1_scalar_set_int(&small, i); + secp256k1_scalar_get_num(&n, &small); + CHECK(secp256k1_num_jacobi(&n, &order) == jacobi5[i]); + } + + /** test large values with 5 as group order */ + secp256k1_scalar_get_num(&order, &five); + /* we first need a scalar which is not a multiple of 5 */ + do { + secp256k1_num fiven; + random_scalar_order_test(&sqr); + secp256k1_scalar_get_num(&fiven, &five); + secp256k1_scalar_get_num(&n, &sqr); + secp256k1_num_mod(&n, &fiven); + } while (secp256k1_num_is_zero(&n)); + /* next force it to be a residue. 2 is a nonresidue mod 5 so we can + * just multiply by two, i.e. add the number to itself */ + if (secp256k1_num_jacobi(&n, &order) == -1) { + secp256k1_num_add(&n, &n, &n); + } + + /* test residue */ + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_num_add(&n, &n, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + + /** test with secp group order as order */ + secp256k1_scalar_order_get_num(&order); + random_scalar_order_test(&sqr); + secp256k1_scalar_sqr(&sqr, &sqr); + /* test residue */ + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); + /* test nonresidue */ + secp256k1_scalar_mul(&sqr, &sqr, &five); + secp256k1_scalar_get_num(&n, &sqr); + CHECK(secp256k1_num_jacobi(&n, &order) == -1); + /* test multiple of the order*/ + CHECK(secp256k1_num_jacobi(&order, &order) == 0); + + /* check one less than the order */ + secp256k1_scalar_set_int(&small, 1); + secp256k1_scalar_get_num(&n, &small); + secp256k1_num_sub(&n, &order, &n); + CHECK(secp256k1_num_jacobi(&n, &order) == 1); /* sage confirms this is 1 */ +} + +void run_num_smalltests(void) { + int i; + for (i = 0; i < 100*count; i++) { + test_num_negate(); + test_num_add_sub(); + test_num_mod(); + test_num_jacobi(); + } +} +#endif + +/***** SCALAR TESTS *****/ + +void scalar_test(void) { + secp256k1_scalar s; + secp256k1_scalar s1; + secp256k1_scalar s2; +#ifndef USE_NUM_NONE + secp256k1_num snum, s1num, s2num; + secp256k1_num order, half_order; +#endif + unsigned char c[32]; + + /* Set 's' to a random scalar, with value 'snum'. */ + random_scalar_order_test(&s); + + /* Set 's1' to a random scalar, with value 's1num'. */ + random_scalar_order_test(&s1); + + /* Set 's2' to a random scalar, with value 'snum2', and byte array representation 'c'. */ + random_scalar_order_test(&s2); + secp256k1_scalar_get_b32(c, &s2); + +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&snum, &s); + secp256k1_scalar_get_num(&s1num, &s1); + secp256k1_scalar_get_num(&s2num, &s2); + + secp256k1_scalar_order_get_num(&order); + half_order = order; + secp256k1_num_shift(&half_order, 1); +#endif + + { + int i; + /* Test that fetching groups of 4 bits from a scalar and recursing n(i)=16*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + secp256k1_scalar_set_int(&n, 0); + for (i = 0; i < 256; i += 4) { + secp256k1_scalar t; + int j; + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits(&s, 256 - 4 - i, 4)); + for (j = 0; j < 4; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + + { + /* Test that fetching groups of randomly-sized bits from a scalar and recursing n(i)=b*n(i-1)+p(i) reconstructs it. */ + secp256k1_scalar n; + int i = 0; + secp256k1_scalar_set_int(&n, 0); + while (i < 256) { + secp256k1_scalar t; + int j; + int now = secp256k1_rand_int(15) + 1; + if (now + i > 256) { + now = 256 - i; + } + secp256k1_scalar_set_int(&t, secp256k1_scalar_get_bits_var(&s, 256 - now - i, now)); + for (j = 0; j < now; j++) { + secp256k1_scalar_add(&n, &n, &n); + } + secp256k1_scalar_add(&n, &n, &t); + i += now; + } + CHECK(secp256k1_scalar_eq(&n, &s)); + } + +#ifndef USE_NUM_NONE + { + /* Test that adding the scalars together is equal to adding their numbers together modulo the order. */ + secp256k1_num rnum; + secp256k1_num r2num; + secp256k1_scalar r; + secp256k1_num_add(&rnum, &snum, &s2num); + secp256k1_num_mod(&rnum, &order); + secp256k1_scalar_add(&r, &s, &s2); + secp256k1_scalar_get_num(&r2num, &r); + CHECK(secp256k1_num_eq(&rnum, &r2num)); + } + + { + /* Test that multiplying the scalars is equal to multiplying their numbers modulo the order. */ + secp256k1_scalar r; + secp256k1_num r2num; + secp256k1_num rnum; + secp256k1_num_mul(&rnum, &snum, &s2num); + secp256k1_num_mod(&rnum, &order); + secp256k1_scalar_mul(&r, &s, &s2); + secp256k1_scalar_get_num(&r2num, &r); + CHECK(secp256k1_num_eq(&rnum, &r2num)); + /* The result can only be zero if at least one of the factors was zero. */ + CHECK(secp256k1_scalar_is_zero(&r) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_zero(&s2))); + /* The results can only be equal to one of the factors if that factor was zero, or the other factor was one. */ + CHECK(secp256k1_num_eq(&rnum, &snum) == (secp256k1_scalar_is_zero(&s) || secp256k1_scalar_is_one(&s2))); + CHECK(secp256k1_num_eq(&rnum, &s2num) == (secp256k1_scalar_is_zero(&s2) || secp256k1_scalar_is_one(&s))); + } + + { + secp256k1_scalar neg; + secp256k1_num negnum; + secp256k1_num negnum2; + /* Check that comparison with zero matches comparison with zero on the number. */ + CHECK(secp256k1_num_is_zero(&snum) == secp256k1_scalar_is_zero(&s)); + /* Check that comparison with the half order is equal to testing for high scalar. */ + CHECK(secp256k1_scalar_is_high(&s) == (secp256k1_num_cmp(&snum, &half_order) > 0)); + secp256k1_scalar_negate(&neg, &s); + secp256k1_num_sub(&negnum, &order, &snum); + secp256k1_num_mod(&negnum, &order); + /* Check that comparison with the half order is equal to testing for high scalar after negation. */ + CHECK(secp256k1_scalar_is_high(&neg) == (secp256k1_num_cmp(&negnum, &half_order) > 0)); + /* Negating should change the high property, unless the value was already zero. */ + CHECK((secp256k1_scalar_is_high(&s) == secp256k1_scalar_is_high(&neg)) == secp256k1_scalar_is_zero(&s)); + secp256k1_scalar_get_num(&negnum2, &neg); + /* Negating a scalar should be equal to (order - n) mod order on the number. */ + CHECK(secp256k1_num_eq(&negnum, &negnum2)); + secp256k1_scalar_add(&neg, &neg, &s); + /* Adding a number to its negation should result in zero. */ + CHECK(secp256k1_scalar_is_zero(&neg)); + secp256k1_scalar_negate(&neg, &neg); + /* Negating zero should still result in zero. */ + CHECK(secp256k1_scalar_is_zero(&neg)); + } + + { + /* Test secp256k1_scalar_mul_shift_var. */ + secp256k1_scalar r; + secp256k1_num one; + secp256k1_num rnum; + secp256k1_num rnum2; + unsigned char cone[1] = {0x01}; + unsigned int shift = 256 + secp256k1_rand_int(257); + secp256k1_scalar_mul_shift_var(&r, &s1, &s2, shift); + secp256k1_num_mul(&rnum, &s1num, &s2num); + secp256k1_num_shift(&rnum, shift - 1); + secp256k1_num_set_bin(&one, cone, 1); + secp256k1_num_add(&rnum, &rnum, &one); + secp256k1_num_shift(&rnum, 1); + secp256k1_scalar_get_num(&rnum2, &r); + CHECK(secp256k1_num_eq(&rnum, &rnum2)); + } + + { + /* test secp256k1_scalar_shr_int */ + secp256k1_scalar r; + int i; + random_scalar_order_test(&r); + for (i = 0; i < 100; ++i) { + int low; + int shift = 1 + secp256k1_rand_int(15); + int expected = r.d[0] % (1 << shift); + low = secp256k1_scalar_shr_int(&r, shift); + CHECK(expected == low); + } + } +#endif + + { + /* Test that scalar inverses are equal to the inverse of their number modulo the order. */ + if (!secp256k1_scalar_is_zero(&s)) { + secp256k1_scalar inv; +#ifndef USE_NUM_NONE + secp256k1_num invnum; + secp256k1_num invnum2; +#endif + secp256k1_scalar_inverse(&inv, &s); +#ifndef USE_NUM_NONE + secp256k1_num_mod_inverse(&invnum, &snum, &order); + secp256k1_scalar_get_num(&invnum2, &inv); + CHECK(secp256k1_num_eq(&invnum, &invnum2)); +#endif + secp256k1_scalar_mul(&inv, &inv, &s); + /* Multiplying a scalar with its inverse must result in one. */ + CHECK(secp256k1_scalar_is_one(&inv)); + secp256k1_scalar_inverse(&inv, &inv); + /* Inverting one must result in one. */ + CHECK(secp256k1_scalar_is_one(&inv)); +#ifndef USE_NUM_NONE + secp256k1_scalar_get_num(&invnum, &inv); + CHECK(secp256k1_num_is_one(&invnum)); +#endif + } + } + + { + /* Test commutativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + secp256k1_scalar r1, r2; + secp256k1_scalar b; + int i; + /* Test add_bit. */ + int bit = secp256k1_rand_bits(8); + secp256k1_scalar_set_int(&b, 1); + CHECK(secp256k1_scalar_is_one(&b)); + for (i = 0; i < bit; i++) { + secp256k1_scalar_add(&b, &b, &b); + } + r1 = s1; + r2 = s1; + if (!secp256k1_scalar_add(&r1, &r1, &b)) { + /* No overflow happened. */ + secp256k1_scalar_cadd_bit(&r2, bit, 1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + /* cadd is a noop when flag is zero */ + secp256k1_scalar_cadd_bit(&r2, bit, 0); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + } + + { + /* Test commutativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r2, &s2, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of add. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_add(&r1, &r1, &s); + secp256k1_scalar_add(&r2, &s2, &s); + secp256k1_scalar_add(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test associativity of mul. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_mul(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s2, &s); + secp256k1_scalar_mul(&r2, &s1, &r2); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test distributitivity of mul over add. */ + secp256k1_scalar r1, r2, t; + secp256k1_scalar_add(&r1, &s1, &s2); + secp256k1_scalar_mul(&r1, &r1, &s); + secp256k1_scalar_mul(&r2, &s1, &s); + secp256k1_scalar_mul(&t, &s2, &s); + secp256k1_scalar_add(&r2, &r2, &t); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test square. */ + secp256k1_scalar r1, r2; + secp256k1_scalar_sqr(&r1, &s1); + secp256k1_scalar_mul(&r2, &s1, &s1); + CHECK(secp256k1_scalar_eq(&r1, &r2)); + } + + { + /* Test multiplicative identity. */ + secp256k1_scalar r1, v1; + secp256k1_scalar_set_int(&v1,1); + secp256k1_scalar_mul(&r1, &s1, &v1); + CHECK(secp256k1_scalar_eq(&r1, &s1)); + } + + { + /* Test additive identity. */ + secp256k1_scalar r1, v0; + secp256k1_scalar_set_int(&v0,0); + secp256k1_scalar_add(&r1, &s1, &v0); + CHECK(secp256k1_scalar_eq(&r1, &s1)); + } + + { + /* Test zero product property. */ + secp256k1_scalar r1, v0; + secp256k1_scalar_set_int(&v0,0); + secp256k1_scalar_mul(&r1, &s1, &v0); + CHECK(secp256k1_scalar_eq(&r1, &v0)); + } + +} + +void run_scalar_tests(void) { + int i; + for (i = 0; i < 128 * count; i++) { + scalar_test(); + } + + { + /* (-1)+1 should be zero. */ + secp256k1_scalar s, o; + secp256k1_scalar_set_int(&s, 1); + CHECK(secp256k1_scalar_is_one(&s)); + secp256k1_scalar_negate(&o, &s); + secp256k1_scalar_add(&o, &o, &s); + CHECK(secp256k1_scalar_is_zero(&o)); + secp256k1_scalar_negate(&o, &o); + CHECK(secp256k1_scalar_is_zero(&o)); + } + +#ifndef USE_NUM_NONE + { + /* A scalar with value of the curve order should be 0. */ + secp256k1_num order; + secp256k1_scalar zero; + unsigned char bin[32]; + int overflow = 0; + secp256k1_scalar_order_get_num(&order); + secp256k1_num_get_bin(bin, 32, &order); + secp256k1_scalar_set_b32(&zero, bin, &overflow); + CHECK(overflow == 1); + CHECK(secp256k1_scalar_is_zero(&zero)); + } +#endif + + { + /* Does check_overflow check catch all ones? */ + static const secp256k1_scalar overflowed = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL + ); + CHECK(secp256k1_scalar_check_overflow(&overflowed)); + } + + { + /* Static test vectors. + * These were reduced from ~10^12 random vectors based on comparison-decision + * and edge-case coverage on 32-bit and 64-bit implementations. + * The responses were generated with Sage 5.9. + */ + secp256k1_scalar x; + secp256k1_scalar y; + secp256k1_scalar z; + secp256k1_scalar zz; + secp256k1_scalar one; + secp256k1_scalar r1; + secp256k1_scalar r2; +#if defined(USE_SCALAR_INV_NUM) + secp256k1_scalar zzv; +#endif + int overflow; + unsigned char chal[33][2][32] = { + {{0xff, 0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff}}, + {{0xef, 0xff, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x80, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x3f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xe0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1e, 0xf8, 0xff, 0xff, 0xff, 0xfd, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, + 0x00, 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0xe0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0xff, + 0xf3, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, + 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x80, 0xff, 0xff, 0x3f, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xdf, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0x00, 0x0f, 0xfc, 0x9f, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x0f, 0xfc, 0xff, 0x7f, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0x00, 0x00, 0xf8, 0xff, 0x0f, 0xc0, 0xff, 0xff, + 0xff, 0x1f, 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x07, 0x80, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xf7, 0xff, 0xff, 0xef, 0xff, 0xff, 0xff, 0x00, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x00, 0xf8, 0xff, 0x03, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0xc0, 0xff, 0x0f, 0xfc, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x3f, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0x8f, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x80, 0xff, 0x7f}, + {0xff, 0xcf, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, + 0x00, 0xc0, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xbf, 0xff, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xff, 0xff, + 0xff, 0xff, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0x01, 0xfc, 0xff, 0x01, 0x00, 0xfe, 0xff}, + {0xff, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7f, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xf8, 0xff, 0x01, 0x00, 0xf0, 0xff, 0xff, + 0xe0, 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0x3f, 0xf0, 0xff, 0xff, 0x3f, + 0x00, 0x00, 0xf8, 0x07, 0x00, 0x00, 0x00, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x0f, 0x7e, 0x00, 0x00}}, + {{0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x1f, 0x00, 0x00, 0xfe, 0x07, 0x00}, + {0x00, 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xfb, 0xff, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60}}, + {{0xff, 0x01, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, + 0x80, 0x7f, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0x1f, 0x00, 0xf0, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00}}, + {{0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf1, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, + 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xc0, 0xff, 0xff, 0xcf, 0xff, 0x1f, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00}, + {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x3f, 0x00, 0x00, 0x80, + 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0xff, 0x7f, 0xf8, 0xff, 0xff, 0x1f, 0x00, 0xfe}}, + {{0xff, 0xff, 0xff, 0x3f, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0x03, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0x01, 0x80, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0xc0, + 0xff, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x01, 0xff, 0xff, 0xff}}, + {{0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x7e, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x07, 0x00, + 0x80, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, + {{0xff, 0xff, 0xf0, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x00, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0xff, 0xff, + 0xff, 0xff, 0x3f, 0x00, 0xf8, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xf1, 0x7f, 0x00}}, + {{0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0xff, 0xff, 0x00}, + {0x00, 0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, + 0x00, 0x00, 0xfc, 0xff, 0xff, 0x01, 0xff, 0xff}}, + {{0x00, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0x00, 0x00, 0x80, 0xff, 0x03, 0xe0, 0x01, + 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00}, + {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xff, 0xff, 0xf0, 0x07, 0x00, 0x3c, 0x80, + 0xff, 0xff, 0xff, 0xff, 0xfc, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x07, 0xe0, 0xff, 0x00, 0x00, 0x00}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, + 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80}, + {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x0c, 0x80, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0x7f, 0xfe, 0xff, 0x1f, + 0x00, 0xfe, 0xff, 0x03, 0x00, 0x00, 0xfe, 0xff}}, + {{0xff, 0xff, 0x81, 0xff, 0xff, 0xff, 0xff, 0x00, + 0x80, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x83, + 0xff, 0xff, 0x00, 0x00, 0x80, 0x00, 0x00, 0x80, + 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0xf0}, + {0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, + 0xf8, 0x07, 0x00, 0x80, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xc7, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff}}, + {{0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}, + {0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0x82, 0xc9, 0xfa, 0xb0, 0x68, 0x04, 0xa0, 0x00, + 0xff, 0xff, 0xff, 0xff, 0xff, 0x6f, 0x03, 0xfb, + 0xfa, 0x8a, 0x7d, 0xdf, 0x13, 0x86, 0xe2, 0x03}} + }; + unsigned char res[33][2][32] = { + {{0x0c, 0x3b, 0x0a, 0xca, 0x8d, 0x1a, 0x2f, 0xb9, + 0x8a, 0x7b, 0x53, 0x5a, 0x1f, 0xc5, 0x22, 0xa1, + 0x07, 0x2a, 0x48, 0xea, 0x02, 0xeb, 0xb3, 0xd6, + 0x20, 0x1e, 0x86, 0xd0, 0x95, 0xf6, 0x92, 0x35}, + {0xdc, 0x90, 0x7a, 0x07, 0x2e, 0x1e, 0x44, 0x6d, + 0xf8, 0x15, 0x24, 0x5b, 0x5a, 0x96, 0x37, 0x9c, + 0x37, 0x7b, 0x0d, 0xac, 0x1b, 0x65, 0x58, 0x49, + 0x43, 0xb7, 0x31, 0xbb, 0xa7, 0xf4, 0x97, 0x15}}, + {{0xf1, 0xf7, 0x3a, 0x50, 0xe6, 0x10, 0xba, 0x22, + 0x43, 0x4d, 0x1f, 0x1f, 0x7c, 0x27, 0xca, 0x9c, + 0xb8, 0xb6, 0xa0, 0xfc, 0xd8, 0xc0, 0x05, 0x2f, + 0xf7, 0x08, 0xe1, 0x76, 0xdd, 0xd0, 0x80, 0xc8}, + {0xe3, 0x80, 0x80, 0xb8, 0xdb, 0xe3, 0xa9, 0x77, + 0x00, 0xb0, 0xf5, 0x2e, 0x27, 0xe2, 0x68, 0xc4, + 0x88, 0xe8, 0x04, 0xc1, 0x12, 0xbf, 0x78, 0x59, + 0xe6, 0xa9, 0x7c, 0xe1, 0x81, 0xdd, 0xb9, 0xd5}}, + {{0x96, 0xe2, 0xee, 0x01, 0xa6, 0x80, 0x31, 0xef, + 0x5c, 0xd0, 0x19, 0xb4, 0x7d, 0x5f, 0x79, 0xab, + 0xa1, 0x97, 0xd3, 0x7e, 0x33, 0xbb, 0x86, 0x55, + 0x60, 0x20, 0x10, 0x0d, 0x94, 0x2d, 0x11, 0x7c}, + {0xcc, 0xab, 0xe0, 0xe8, 0x98, 0x65, 0x12, 0x96, + 0x38, 0x5a, 0x1a, 0xf2, 0x85, 0x23, 0x59, 0x5f, + 0xf9, 0xf3, 0xc2, 0x81, 0x70, 0x92, 0x65, 0x12, + 0x9c, 0x65, 0x1e, 0x96, 0x00, 0xef, 0xe7, 0x63}}, + {{0xac, 0x1e, 0x62, 0xc2, 0x59, 0xfc, 0x4e, 0x5c, + 0x83, 0xb0, 0xd0, 0x6f, 0xce, 0x19, 0xf6, 0xbf, + 0xa4, 0xb0, 0xe0, 0x53, 0x66, 0x1f, 0xbf, 0xc9, + 0x33, 0x47, 0x37, 0xa9, 0x3d, 0x5d, 0xb0, 0x48}, + {0x86, 0xb9, 0x2a, 0x7f, 0x8e, 0xa8, 0x60, 0x42, + 0x26, 0x6d, 0x6e, 0x1c, 0xa2, 0xec, 0xe0, 0xe5, + 0x3e, 0x0a, 0x33, 0xbb, 0x61, 0x4c, 0x9f, 0x3c, + 0xd1, 0xdf, 0x49, 0x33, 0xcd, 0x72, 0x78, 0x18}}, + {{0xf7, 0xd3, 0xcd, 0x49, 0x5c, 0x13, 0x22, 0xfb, + 0x2e, 0xb2, 0x2f, 0x27, 0xf5, 0x8a, 0x5d, 0x74, + 0xc1, 0x58, 0xc5, 0xc2, 0x2d, 0x9f, 0x52, 0xc6, + 0x63, 0x9f, 0xba, 0x05, 0x76, 0x45, 0x7a, 0x63}, + {0x8a, 0xfa, 0x55, 0x4d, 0xdd, 0xa3, 0xb2, 0xc3, + 0x44, 0xfd, 0xec, 0x72, 0xde, 0xef, 0xc0, 0x99, + 0xf5, 0x9f, 0xe2, 0x52, 0xb4, 0x05, 0x32, 0x58, + 0x57, 0xc1, 0x8f, 0xea, 0xc3, 0x24, 0x5b, 0x94}}, + {{0x05, 0x83, 0xee, 0xdd, 0x64, 0xf0, 0x14, 0x3b, + 0xa0, 0x14, 0x4a, 0x3a, 0x41, 0x82, 0x7c, 0xa7, + 0x2c, 0xaa, 0xb1, 0x76, 0xbb, 0x59, 0x64, 0x5f, + 0x52, 0xad, 0x25, 0x29, 0x9d, 0x8f, 0x0b, 0xb0}, + {0x7e, 0xe3, 0x7c, 0xca, 0xcd, 0x4f, 0xb0, 0x6d, + 0x7a, 0xb2, 0x3e, 0xa0, 0x08, 0xb9, 0xa8, 0x2d, + 0xc2, 0xf4, 0x99, 0x66, 0xcc, 0xac, 0xd8, 0xb9, + 0x72, 0x2a, 0x4a, 0x3e, 0x0f, 0x7b, 0xbf, 0xf4}}, + {{0x8c, 0x9c, 0x78, 0x2b, 0x39, 0x61, 0x7e, 0xf7, + 0x65, 0x37, 0x66, 0x09, 0x38, 0xb9, 0x6f, 0x70, + 0x78, 0x87, 0xff, 0xcf, 0x93, 0xca, 0x85, 0x06, + 0x44, 0x84, 0xa7, 0xfe, 0xd3, 0xa4, 0xe3, 0x7e}, + {0xa2, 0x56, 0x49, 0x23, 0x54, 0xa5, 0x50, 0xe9, + 0x5f, 0xf0, 0x4d, 0xe7, 0xdc, 0x38, 0x32, 0x79, + 0x4f, 0x1c, 0xb7, 0xe4, 0xbb, 0xf8, 0xbb, 0x2e, + 0x40, 0x41, 0x4b, 0xcc, 0xe3, 0x1e, 0x16, 0x36}}, + {{0x0c, 0x1e, 0xd7, 0x09, 0x25, 0x40, 0x97, 0xcb, + 0x5c, 0x46, 0xa8, 0xda, 0xef, 0x25, 0xd5, 0xe5, + 0x92, 0x4d, 0xcf, 0xa3, 0xc4, 0x5d, 0x35, 0x4a, + 0xe4, 0x61, 0x92, 0xf3, 0xbf, 0x0e, 0xcd, 0xbe}, + {0xe4, 0xaf, 0x0a, 0xb3, 0x30, 0x8b, 0x9b, 0x48, + 0x49, 0x43, 0xc7, 0x64, 0x60, 0x4a, 0x2b, 0x9e, + 0x95, 0x5f, 0x56, 0xe8, 0x35, 0xdc, 0xeb, 0xdc, + 0xc7, 0xc4, 0xfe, 0x30, 0x40, 0xc7, 0xbf, 0xa4}}, + {{0xd4, 0xa0, 0xf5, 0x81, 0x49, 0x6b, 0xb6, 0x8b, + 0x0a, 0x69, 0xf9, 0xfe, 0xa8, 0x32, 0xe5, 0xe0, + 0xa5, 0xcd, 0x02, 0x53, 0xf9, 0x2c, 0xe3, 0x53, + 0x83, 0x36, 0xc6, 0x02, 0xb5, 0xeb, 0x64, 0xb8}, + {0x1d, 0x42, 0xb9, 0xf9, 0xe9, 0xe3, 0x93, 0x2c, + 0x4c, 0xee, 0x6c, 0x5a, 0x47, 0x9e, 0x62, 0x01, + 0x6b, 0x04, 0xfe, 0xa4, 0x30, 0x2b, 0x0d, 0x4f, + 0x71, 0x10, 0xd3, 0x55, 0xca, 0xf3, 0x5e, 0x80}}, + {{0x77, 0x05, 0xf6, 0x0c, 0x15, 0x9b, 0x45, 0xe7, + 0xb9, 0x11, 0xb8, 0xf5, 0xd6, 0xda, 0x73, 0x0c, + 0xda, 0x92, 0xea, 0xd0, 0x9d, 0xd0, 0x18, 0x92, + 0xce, 0x9a, 0xaa, 0xee, 0x0f, 0xef, 0xde, 0x30}, + {0xf1, 0xf1, 0xd6, 0x9b, 0x51, 0xd7, 0x77, 0x62, + 0x52, 0x10, 0xb8, 0x7a, 0x84, 0x9d, 0x15, 0x4e, + 0x07, 0xdc, 0x1e, 0x75, 0x0d, 0x0c, 0x3b, 0xdb, + 0x74, 0x58, 0x62, 0x02, 0x90, 0x54, 0x8b, 0x43}}, + {{0xa6, 0xfe, 0x0b, 0x87, 0x80, 0x43, 0x67, 0x25, + 0x57, 0x5d, 0xec, 0x40, 0x50, 0x08, 0xd5, 0x5d, + 0x43, 0xd7, 0xe0, 0xaa, 0xe0, 0x13, 0xb6, 0xb0, + 0xc0, 0xd4, 0xe5, 0x0d, 0x45, 0x83, 0xd6, 0x13}, + {0x40, 0x45, 0x0a, 0x92, 0x31, 0xea, 0x8c, 0x60, + 0x8c, 0x1f, 0xd8, 0x76, 0x45, 0xb9, 0x29, 0x00, + 0x26, 0x32, 0xd8, 0xa6, 0x96, 0x88, 0xe2, 0xc4, + 0x8b, 0xdb, 0x7f, 0x17, 0x87, 0xcc, 0xc8, 0xf2}}, + {{0xc2, 0x56, 0xe2, 0xb6, 0x1a, 0x81, 0xe7, 0x31, + 0x63, 0x2e, 0xbb, 0x0d, 0x2f, 0x81, 0x67, 0xd4, + 0x22, 0xe2, 0x38, 0x02, 0x25, 0x97, 0xc7, 0x88, + 0x6e, 0xdf, 0xbe, 0x2a, 0xa5, 0x73, 0x63, 0xaa}, + {0x50, 0x45, 0xe2, 0xc3, 0xbd, 0x89, 0xfc, 0x57, + 0xbd, 0x3c, 0xa3, 0x98, 0x7e, 0x7f, 0x36, 0x38, + 0x92, 0x39, 0x1f, 0x0f, 0x81, 0x1a, 0x06, 0x51, + 0x1f, 0x8d, 0x6a, 0xff, 0x47, 0x16, 0x06, 0x9c}}, + {{0x33, 0x95, 0xa2, 0x6f, 0x27, 0x5f, 0x9c, 0x9c, + 0x64, 0x45, 0xcb, 0xd1, 0x3c, 0xee, 0x5e, 0x5f, + 0x48, 0xa6, 0xaf, 0xe3, 0x79, 0xcf, 0xb1, 0xe2, + 0xbf, 0x55, 0x0e, 0xa2, 0x3b, 0x62, 0xf0, 0xe4}, + {0x14, 0xe8, 0x06, 0xe3, 0xbe, 0x7e, 0x67, 0x01, + 0xc5, 0x21, 0x67, 0xd8, 0x54, 0xb5, 0x7f, 0xa4, + 0xf9, 0x75, 0x70, 0x1c, 0xfd, 0x79, 0xdb, 0x86, + 0xad, 0x37, 0x85, 0x83, 0x56, 0x4e, 0xf0, 0xbf}}, + {{0xbc, 0xa6, 0xe0, 0x56, 0x4e, 0xef, 0xfa, 0xf5, + 0x1d, 0x5d, 0x3f, 0x2a, 0x5b, 0x19, 0xab, 0x51, + 0xc5, 0x8b, 0xdd, 0x98, 0x28, 0x35, 0x2f, 0xc3, + 0x81, 0x4f, 0x5c, 0xe5, 0x70, 0xb9, 0xeb, 0x62}, + {0xc4, 0x6d, 0x26, 0xb0, 0x17, 0x6b, 0xfe, 0x6c, + 0x12, 0xf8, 0xe7, 0xc1, 0xf5, 0x2f, 0xfa, 0x91, + 0x13, 0x27, 0xbd, 0x73, 0xcc, 0x33, 0x31, 0x1c, + 0x39, 0xe3, 0x27, 0x6a, 0x95, 0xcf, 0xc5, 0xfb}}, + {{0x30, 0xb2, 0x99, 0x84, 0xf0, 0x18, 0x2a, 0x6e, + 0x1e, 0x27, 0xed, 0xa2, 0x29, 0x99, 0x41, 0x56, + 0xe8, 0xd4, 0x0d, 0xef, 0x99, 0x9c, 0xf3, 0x58, + 0x29, 0x55, 0x1a, 0xc0, 0x68, 0xd6, 0x74, 0xa4}, + {0x07, 0x9c, 0xe7, 0xec, 0xf5, 0x36, 0x73, 0x41, + 0xa3, 0x1c, 0xe5, 0x93, 0x97, 0x6a, 0xfd, 0xf7, + 0x53, 0x18, 0xab, 0xaf, 0xeb, 0x85, 0xbd, 0x92, + 0x90, 0xab, 0x3c, 0xbf, 0x30, 0x82, 0xad, 0xf6}}, + {{0xc6, 0x87, 0x8a, 0x2a, 0xea, 0xc0, 0xa9, 0xec, + 0x6d, 0xd3, 0xdc, 0x32, 0x23, 0xce, 0x62, 0x19, + 0xa4, 0x7e, 0xa8, 0xdd, 0x1c, 0x33, 0xae, 0xd3, + 0x4f, 0x62, 0x9f, 0x52, 0xe7, 0x65, 0x46, 0xf4}, + {0x97, 0x51, 0x27, 0x67, 0x2d, 0xa2, 0x82, 0x87, + 0x98, 0xd3, 0xb6, 0x14, 0x7f, 0x51, 0xd3, 0x9a, + 0x0b, 0xd0, 0x76, 0x81, 0xb2, 0x4f, 0x58, 0x92, + 0xa4, 0x86, 0xa1, 0xa7, 0x09, 0x1d, 0xef, 0x9b}}, + {{0xb3, 0x0f, 0x2b, 0x69, 0x0d, 0x06, 0x90, 0x64, + 0xbd, 0x43, 0x4c, 0x10, 0xe8, 0x98, 0x1c, 0xa3, + 0xe1, 0x68, 0xe9, 0x79, 0x6c, 0x29, 0x51, 0x3f, + 0x41, 0xdc, 0xdf, 0x1f, 0xf3, 0x60, 0xbe, 0x33}, + {0xa1, 0x5f, 0xf7, 0x1d, 0xb4, 0x3e, 0x9b, 0x3c, + 0xe7, 0xbd, 0xb6, 0x06, 0xd5, 0x60, 0x06, 0x6d, + 0x50, 0xd2, 0xf4, 0x1a, 0x31, 0x08, 0xf2, 0xea, + 0x8e, 0xef, 0x5f, 0x7d, 0xb6, 0xd0, 0xc0, 0x27}}, + {{0x62, 0x9a, 0xd9, 0xbb, 0x38, 0x36, 0xce, 0xf7, + 0x5d, 0x2f, 0x13, 0xec, 0xc8, 0x2d, 0x02, 0x8a, + 0x2e, 0x72, 0xf0, 0xe5, 0x15, 0x9d, 0x72, 0xae, + 0xfc, 0xb3, 0x4f, 0x02, 0xea, 0xe1, 0x09, 0xfe}, + {0x00, 0x00, 0x00, 0x00, 0xfa, 0x0a, 0x3d, 0xbc, + 0xad, 0x16, 0x0c, 0xb6, 0xe7, 0x7c, 0x8b, 0x39, + 0x9a, 0x43, 0xbb, 0xe3, 0xc2, 0x55, 0x15, 0x14, + 0x75, 0xac, 0x90, 0x9b, 0x7f, 0x9a, 0x92, 0x00}}, + {{0x8b, 0xac, 0x70, 0x86, 0x29, 0x8f, 0x00, 0x23, + 0x7b, 0x45, 0x30, 0xaa, 0xb8, 0x4c, 0xc7, 0x8d, + 0x4e, 0x47, 0x85, 0xc6, 0x19, 0xe3, 0x96, 0xc2, + 0x9a, 0xa0, 0x12, 0xed, 0x6f, 0xd7, 0x76, 0x16}, + {0x45, 0xaf, 0x7e, 0x33, 0xc7, 0x7f, 0x10, 0x6c, + 0x7c, 0x9f, 0x29, 0xc1, 0xa8, 0x7e, 0x15, 0x84, + 0xe7, 0x7d, 0xc0, 0x6d, 0xab, 0x71, 0x5d, 0xd0, + 0x6b, 0x9f, 0x97, 0xab, 0xcb, 0x51, 0x0c, 0x9f}}, + {{0x9e, 0xc3, 0x92, 0xb4, 0x04, 0x9f, 0xc8, 0xbb, + 0xdd, 0x9e, 0xc6, 0x05, 0xfd, 0x65, 0xec, 0x94, + 0x7f, 0x2c, 0x16, 0xc4, 0x40, 0xac, 0x63, 0x7b, + 0x7d, 0xb8, 0x0c, 0xe4, 0x5b, 0xe3, 0xa7, 0x0e}, + {0x43, 0xf4, 0x44, 0xe8, 0xcc, 0xc8, 0xd4, 0x54, + 0x33, 0x37, 0x50, 0xf2, 0x87, 0x42, 0x2e, 0x00, + 0x49, 0x60, 0x62, 0x02, 0xfd, 0x1a, 0x7c, 0xdb, + 0x29, 0x6c, 0x6d, 0x54, 0x53, 0x08, 0xd1, 0xc8}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0x28, 0x56, 0xac, 0x0e, 0x4f, 0x98, 0x09, 0xf0, + 0x49, 0xfa, 0x7f, 0x84, 0xac, 0x7e, 0x50, 0x5b, + 0x17, 0x43, 0x14, 0x89, 0x9c, 0x53, 0xa8, 0x94, + 0x30, 0xf2, 0x11, 0x4d, 0x92, 0x14, 0x27, 0xe8}, + {0x39, 0x7a, 0x84, 0x56, 0x79, 0x9d, 0xec, 0x26, + 0x2c, 0x53, 0xc1, 0x94, 0xc9, 0x8d, 0x9e, 0x9d, + 0x32, 0x1f, 0xdd, 0x84, 0x04, 0xe8, 0xe2, 0x0a, + 0x6b, 0xbe, 0xbb, 0x42, 0x40, 0x67, 0x30, 0x6c}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x73, 0x2f, 0xc9, 0xbe, 0xbd}, + {0x27, 0x59, 0xc7, 0x35, 0x60, 0x71, 0xa6, 0xf1, + 0x79, 0xa5, 0xfd, 0x79, 0x16, 0xf3, 0x41, 0xf0, + 0x57, 0xb4, 0x02, 0x97, 0x32, 0xe7, 0xde, 0x59, + 0xe2, 0x2d, 0x9b, 0x11, 0xea, 0x2c, 0x35, 0x92}}, + {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40}, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}, + {{0x1c, 0xc4, 0xf7, 0xda, 0x0f, 0x65, 0xca, 0x39, + 0x70, 0x52, 0x92, 0x8e, 0xc3, 0xc8, 0x15, 0xea, + 0x7f, 0x10, 0x9e, 0x77, 0x4b, 0x6e, 0x2d, 0xdf, + 0xe8, 0x30, 0x9d, 0xda, 0xe8, 0x9a, 0x65, 0xae}, + {0x02, 0xb0, 0x16, 0xb1, 0x1d, 0xc8, 0x57, 0x7b, + 0xa2, 0x3a, 0xa2, 0xa3, 0x38, 0x5c, 0x8f, 0xeb, + 0x66, 0x37, 0x91, 0xa8, 0x5f, 0xef, 0x04, 0xf6, + 0x59, 0x75, 0xe1, 0xee, 0x92, 0xf6, 0x0e, 0x30}}, + {{0x8d, 0x76, 0x14, 0xa4, 0x14, 0x06, 0x9f, 0x9a, + 0xdf, 0x4a, 0x85, 0xa7, 0x6b, 0xbf, 0x29, 0x6f, + 0xbc, 0x34, 0x87, 0x5d, 0xeb, 0xbb, 0x2e, 0xa9, + 0xc9, 0x1f, 0x58, 0xd6, 0x9a, 0x82, 0xa0, 0x56}, + {0xd4, 0xb9, 0xdb, 0x88, 0x1d, 0x04, 0xe9, 0x93, + 0x8d, 0x3f, 0x20, 0xd5, 0x86, 0xa8, 0x83, 0x07, + 0xdb, 0x09, 0xd8, 0x22, 0x1f, 0x7f, 0xf1, 0x71, + 0xc8, 0xe7, 0x5d, 0x47, 0xaf, 0x8b, 0x72, 0xe9}}, + {{0x83, 0xb9, 0x39, 0xb2, 0xa4, 0xdf, 0x46, 0x87, + 0xc2, 0xb8, 0xf1, 0xe6, 0x4c, 0xd1, 0xe2, 0xa9, + 0xe4, 0x70, 0x30, 0x34, 0xbc, 0x52, 0x7c, 0x55, + 0xa6, 0xec, 0x80, 0xa4, 0xe5, 0xd2, 0xdc, 0x73}, + {0x08, 0xf1, 0x03, 0xcf, 0x16, 0x73, 0xe8, 0x7d, + 0xb6, 0x7e, 0x9b, 0xc0, 0xb4, 0xc2, 0xa5, 0x86, + 0x02, 0x77, 0xd5, 0x27, 0x86, 0xa5, 0x15, 0xfb, + 0xae, 0x9b, 0x8c, 0xa9, 0xf9, 0xf8, 0xa8, 0x4a}}, + {{0x8b, 0x00, 0x49, 0xdb, 0xfa, 0xf0, 0x1b, 0xa2, + 0xed, 0x8a, 0x9a, 0x7a, 0x36, 0x78, 0x4a, 0xc7, + 0xf7, 0xad, 0x39, 0xd0, 0x6c, 0x65, 0x7a, 0x41, + 0xce, 0xd6, 0xd6, 0x4c, 0x20, 0x21, 0x6b, 0xc7}, + {0xc6, 0xca, 0x78, 0x1d, 0x32, 0x6c, 0x6c, 0x06, + 0x91, 0xf2, 0x1a, 0xe8, 0x43, 0x16, 0xea, 0x04, + 0x3c, 0x1f, 0x07, 0x85, 0xf7, 0x09, 0x22, 0x08, + 0xba, 0x13, 0xfd, 0x78, 0x1e, 0x3f, 0x6f, 0x62}}, + {{0x25, 0x9b, 0x7c, 0xb0, 0xac, 0x72, 0x6f, 0xb2, + 0xe3, 0x53, 0x84, 0x7a, 0x1a, 0x9a, 0x98, 0x9b, + 0x44, 0xd3, 0x59, 0xd0, 0x8e, 0x57, 0x41, 0x40, + 0x78, 0xa7, 0x30, 0x2f, 0x4c, 0x9c, 0xb9, 0x68}, + {0xb7, 0x75, 0x03, 0x63, 0x61, 0xc2, 0x48, 0x6e, + 0x12, 0x3d, 0xbf, 0x4b, 0x27, 0xdf, 0xb1, 0x7a, + 0xff, 0x4e, 0x31, 0x07, 0x83, 0xf4, 0x62, 0x5b, + 0x19, 0xa5, 0xac, 0xa0, 0x32, 0x58, 0x0d, 0xa7}}, + {{0x43, 0x4f, 0x10, 0xa4, 0xca, 0xdb, 0x38, 0x67, + 0xfa, 0xae, 0x96, 0xb5, 0x6d, 0x97, 0xff, 0x1f, + 0xb6, 0x83, 0x43, 0xd3, 0xa0, 0x2d, 0x70, 0x7a, + 0x64, 0x05, 0x4c, 0xa7, 0xc1, 0xa5, 0x21, 0x51}, + {0xe4, 0xf1, 0x23, 0x84, 0xe1, 0xb5, 0x9d, 0xf2, + 0xb8, 0x73, 0x8b, 0x45, 0x2b, 0x35, 0x46, 0x38, + 0x10, 0x2b, 0x50, 0xf8, 0x8b, 0x35, 0xcd, 0x34, + 0xc8, 0x0e, 0xf6, 0xdb, 0x09, 0x35, 0xf0, 0xda}}, + {{0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}, + {0xdb, 0x21, 0x5c, 0x8d, 0x83, 0x1d, 0xb3, 0x34, + 0xc7, 0x0e, 0x43, 0xa1, 0x58, 0x79, 0x67, 0x13, + 0x1e, 0x86, 0x5d, 0x89, 0x63, 0xe6, 0x0a, 0x46, + 0x5c, 0x02, 0x97, 0x1b, 0x62, 0x43, 0x86, 0xf5}} + }; + secp256k1_scalar_set_int(&one, 1); + for (i = 0; i < 33; i++) { + secp256k1_scalar_set_b32(&x, chal[i][0], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&y, chal[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r1, res[i][0], &overflow); + CHECK(!overflow); + secp256k1_scalar_set_b32(&r2, res[i][1], &overflow); + CHECK(!overflow); + secp256k1_scalar_mul(&z, &x, &y); + CHECK(!secp256k1_scalar_check_overflow(&z)); + CHECK(secp256k1_scalar_eq(&r1, &z)); + if (!secp256k1_scalar_is_zero(&y)) { + secp256k1_scalar_inverse(&zz, &y); + CHECK(!secp256k1_scalar_check_overflow(&zz)); +#if defined(USE_SCALAR_INV_NUM) + secp256k1_scalar_inverse_var(&zzv, &y); + CHECK(secp256k1_scalar_eq(&zzv, &zz)); +#endif + secp256k1_scalar_mul(&z, &z, &zz); + CHECK(!secp256k1_scalar_check_overflow(&z)); + CHECK(secp256k1_scalar_eq(&x, &z)); + secp256k1_scalar_mul(&zz, &zz, &y); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&one, &zz)); + } + secp256k1_scalar_mul(&z, &x, &x); + CHECK(!secp256k1_scalar_check_overflow(&z)); + secp256k1_scalar_sqr(&zz, &x); + CHECK(!secp256k1_scalar_check_overflow(&zz)); + CHECK(secp256k1_scalar_eq(&zz, &z)); + CHECK(secp256k1_scalar_eq(&r2, &zz)); + } + } +} + +/***** FIELD TESTS *****/ + +void random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} + +void random_fe_test(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256_test(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} + +void random_fe_non_zero(secp256k1_fe *nz) { + int tries = 10; + while (--tries >= 0) { + random_fe(nz); + secp256k1_fe_normalize(nz); + if (!secp256k1_fe_is_zero(nz)) { + break; + } + } + /* Infinitesimal probability of spurious failure here */ + CHECK(tries >= 0); +} + +void random_fe_non_square(secp256k1_fe *ns) { + secp256k1_fe r; + random_fe_non_zero(ns); + if (secp256k1_fe_sqrt(&r, ns)) { + secp256k1_fe_negate(ns, ns, 1); + } +} + +int check_fe_equal(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe an = *a; + secp256k1_fe bn = *b; + secp256k1_fe_normalize_weak(&an); + secp256k1_fe_normalize_var(&bn); + return secp256k1_fe_equal_var(&an, &bn); +} + +int check_fe_inverse(const secp256k1_fe *a, const secp256k1_fe *ai) { + secp256k1_fe x; + secp256k1_fe one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_fe_mul(&x, a, ai); + return check_fe_equal(&x, &one); +} + +void run_field_convert(void) { + static const unsigned char b32[32] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40 + }; + static const secp256k1_fe_storage fes = SECP256K1_FE_STORAGE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + static const secp256k1_fe fe = SECP256K1_FE_CONST( + 0x00010203UL, 0x04050607UL, 0x11121314UL, 0x15161718UL, + 0x22232425UL, 0x26272829UL, 0x33343536UL, 0x37383940UL + ); + secp256k1_fe fe2; + unsigned char b322[32]; + secp256k1_fe_storage fes2; + /* Check conversions to fe. */ + CHECK(secp256k1_fe_set_b32(&fe2, b32)); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + secp256k1_fe_from_storage(&fe2, &fes); + CHECK(secp256k1_fe_equal_var(&fe, &fe2)); + /* Check conversion from fe. */ + secp256k1_fe_get_b32(b322, &fe); + CHECK(memcmp(b322, b32, 32) == 0); + secp256k1_fe_to_storage(&fes2, &fe); + CHECK(memcmp(&fes2, &fes, sizeof(fes)) == 0); +} + +int fe_memcmp(const secp256k1_fe *a, const secp256k1_fe *b) { + secp256k1_fe t = *b; +#ifdef VERIFY + t.magnitude = a->magnitude; + t.normalized = a->normalized; +#endif + return memcmp(a, &t, sizeof(secp256k1_fe)); +} + +void run_field_misc(void) { + secp256k1_fe x; + secp256k1_fe y; + secp256k1_fe z; + secp256k1_fe q; + secp256k1_fe fe5 = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 5); + int i, j; + for (i = 0; i < 5*count; i++) { + secp256k1_fe_storage xs, ys, zs; + random_fe(&x); + random_fe_non_zero(&y); + /* Test the fe equality and comparison operations. */ + CHECK(secp256k1_fe_cmp_var(&x, &x) == 0); + CHECK(secp256k1_fe_equal_var(&x, &x)); + z = x; + secp256k1_fe_add(&z,&y); + /* Test fe conditional move; z is not normalized here. */ + q = x; + secp256k1_fe_cmov(&x, &z, 0); + VERIFY_CHECK(!x.normalized && x.magnitude == z.magnitude); + secp256k1_fe_cmov(&x, &x, 1); + CHECK(fe_memcmp(&x, &z) != 0); + CHECK(fe_memcmp(&x, &q) == 0); + secp256k1_fe_cmov(&q, &z, 1); + VERIFY_CHECK(!q.normalized && q.magnitude == z.magnitude); + CHECK(fe_memcmp(&q, &z) == 0); + secp256k1_fe_normalize_var(&x); + secp256k1_fe_normalize_var(&z); + CHECK(!secp256k1_fe_equal_var(&x, &z)); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (i&1)); + VERIFY_CHECK(q.normalized && q.magnitude == 1); + for (j = 0; j < 6; j++) { + secp256k1_fe_negate(&z, &z, j+1); + secp256k1_fe_normalize_var(&q); + secp256k1_fe_cmov(&q, &z, (j&1)); + VERIFY_CHECK(!q.normalized && q.magnitude == (j+2)); + } + secp256k1_fe_normalize_var(&z); + /* Test storage conversion and conditional moves. */ + secp256k1_fe_to_storage(&xs, &x); + secp256k1_fe_to_storage(&ys, &y); + secp256k1_fe_to_storage(&zs, &z); + secp256k1_fe_storage_cmov(&zs, &xs, 0); + secp256k1_fe_storage_cmov(&zs, &zs, 1); + CHECK(memcmp(&xs, &zs, sizeof(xs)) != 0); + secp256k1_fe_storage_cmov(&ys, &xs, 1); + CHECK(memcmp(&xs, &ys, sizeof(xs)) == 0); + secp256k1_fe_from_storage(&x, &xs); + secp256k1_fe_from_storage(&y, &ys); + secp256k1_fe_from_storage(&z, &zs); + /* Test that mul_int, mul, and add agree. */ + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&y, &x); + z = x; + secp256k1_fe_mul_int(&z, 3); + CHECK(check_fe_equal(&y, &z)); + secp256k1_fe_add(&y, &x); + secp256k1_fe_add(&z, &x); + CHECK(check_fe_equal(&z, &y)); + z = x; + secp256k1_fe_mul_int(&z, 5); + secp256k1_fe_mul(&q, &x, &fe5); + CHECK(check_fe_equal(&z, &q)); + secp256k1_fe_negate(&x, &x, 1); + secp256k1_fe_add(&z, &x); + secp256k1_fe_add(&q, &x); + CHECK(check_fe_equal(&y, &z)); + CHECK(check_fe_equal(&q, &y)); + } +} + +void run_field_inv(void) { + secp256k1_fe x, xi, xii; + int i; + for (i = 0; i < 10*count; i++) { + random_fe_non_zero(&x); + secp256k1_fe_inv(&xi, &x); + CHECK(check_fe_inverse(&x, &xi)); + secp256k1_fe_inv(&xii, &xi); + CHECK(check_fe_equal(&x, &xii)); + } +} + +void run_field_inv_var(void) { + secp256k1_fe x, xi, xii; + int i; + for (i = 0; i < 10*count; i++) { + random_fe_non_zero(&x); + secp256k1_fe_inv_var(&xi, &x); + CHECK(check_fe_inverse(&x, &xi)); + secp256k1_fe_inv_var(&xii, &xi); + CHECK(check_fe_equal(&x, &xii)); + } +} + +void run_field_inv_all_var(void) { + secp256k1_fe x[16], xi[16], xii[16]; + int i; + /* Check it's safe to call for 0 elements */ + secp256k1_fe_inv_all_var(xi, x, 0); + for (i = 0; i < count; i++) { + size_t j; + size_t len = secp256k1_rand_int(15) + 1; + for (j = 0; j < len; j++) { + random_fe_non_zero(&x[j]); + } + secp256k1_fe_inv_all_var(xi, x, len); + for (j = 0; j < len; j++) { + CHECK(check_fe_inverse(&x[j], &xi[j])); + } + secp256k1_fe_inv_all_var(xii, xi, len); + for (j = 0; j < len; j++) { + CHECK(check_fe_equal(&x[j], &xii[j])); + } + } +} + +void run_sqr(void) { + secp256k1_fe x, s; + + { + int i; + secp256k1_fe_set_int(&x, 1); + secp256k1_fe_negate(&x, &x, 1); + + for (i = 1; i <= 512; ++i) { + secp256k1_fe_mul_int(&x, 2); + secp256k1_fe_normalize(&x); + secp256k1_fe_sqr(&s, &x); + } + } +} + +void test_sqrt(const secp256k1_fe *a, const secp256k1_fe *k) { + secp256k1_fe r1, r2; + int v = secp256k1_fe_sqrt(&r1, a); + CHECK((v == 0) == (k == NULL)); + + if (k != NULL) { + /* Check that the returned root is +/- the given known answer */ + secp256k1_fe_negate(&r2, &r1, 1); + secp256k1_fe_add(&r1, k); secp256k1_fe_add(&r2, k); + secp256k1_fe_normalize(&r1); secp256k1_fe_normalize(&r2); + CHECK(secp256k1_fe_is_zero(&r1) || secp256k1_fe_is_zero(&r2)); + } +} + +void run_sqrt(void) { + secp256k1_fe ns, x, s, t; + int i; + + /* Check sqrt(0) is 0 */ + secp256k1_fe_set_int(&x, 0); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + + /* Check sqrt of small squares (and their negatives) */ + for (i = 1; i <= 100; i++) { + secp256k1_fe_set_int(&x, i); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + } + + /* Consistency checks for large random values */ + for (i = 0; i < 10; i++) { + int j; + random_fe_non_square(&ns); + for (j = 0; j < count; j++) { + random_fe(&x); + secp256k1_fe_sqr(&s, &x); + test_sqrt(&s, &x); + secp256k1_fe_negate(&t, &s, 1); + test_sqrt(&t, NULL); + secp256k1_fe_mul(&t, &s, &ns); + test_sqrt(&t, NULL); + } + } +} + +/***** GROUP TESTS *****/ + +void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +} + +/* This compares jacobian points including their Z, not just their geometric meaning. */ +int gej_xyz_equals_gej(const secp256k1_gej *a, const secp256k1_gej *b) { + secp256k1_gej a2; + secp256k1_gej b2; + int ret = 1; + ret &= a->infinity == b->infinity; + if (ret && !a->infinity) { + a2 = *a; + b2 = *b; + secp256k1_fe_normalize(&a2.x); + secp256k1_fe_normalize(&a2.y); + secp256k1_fe_normalize(&a2.z); + secp256k1_fe_normalize(&b2.x); + secp256k1_fe_normalize(&b2.y); + secp256k1_fe_normalize(&b2.z); + ret &= secp256k1_fe_cmp_var(&a2.x, &b2.x) == 0; + ret &= secp256k1_fe_cmp_var(&a2.y, &b2.y) == 0; + ret &= secp256k1_fe_cmp_var(&a2.z, &b2.z) == 0; + } + return ret; +} + +void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { + secp256k1_fe z2s; + secp256k1_fe u1, u2, s1, s2; + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ + secp256k1_fe_sqr(&z2s, &b->z); + secp256k1_fe_mul(&u1, &a->x, &z2s); + u2 = b->x; secp256k1_fe_normalize_weak(&u2); + secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); + s2 = b->y; secp256k1_fe_normalize_weak(&s2); + CHECK(secp256k1_fe_equal_var(&u1, &u2)); + CHECK(secp256k1_fe_equal_var(&s1, &s2)); +} + +void test_ge(void) { + int i, i1; +#ifdef USE_ENDOMORPHISM + int runs = 6; +#else + int runs = 4; +#endif + /* Points: (infinity, p1, p1, -p1, -p1, p2, p2, -p2, -p2, p3, p3, -p3, -p3, p4, p4, -p4, -p4). + * The second in each pair of identical points uses a random Z coordinate in the Jacobian form. + * All magnitudes are randomized. + * All 17*17 combinations of points are added to each other, using all applicable methods. + * + * When the endomorphism code is compiled in, p5 = lambda*p1 and p6 = lambda^2*p1 are added as well. + */ + secp256k1_ge *ge = (secp256k1_ge *)malloc(sizeof(secp256k1_ge) * (1 + 4 * runs)); + secp256k1_gej *gej = (secp256k1_gej *)malloc(sizeof(secp256k1_gej) * (1 + 4 * runs)); + secp256k1_fe *zinv = (secp256k1_fe *)malloc(sizeof(secp256k1_fe) * (1 + 4 * runs)); + secp256k1_fe zf; + secp256k1_fe zfi2, zfi3; + + secp256k1_gej_set_infinity(&gej[0]); + secp256k1_ge_clear(&ge[0]); + secp256k1_ge_set_gej_var(&ge[0], &gej[0]); + for (i = 0; i < runs; i++) { + int j; + secp256k1_ge g; + random_group_element_test(&g); +#ifdef USE_ENDOMORPHISM + if (i >= runs - 2) { + secp256k1_ge_mul_lambda(&g, &ge[1]); + } + if (i >= runs - 1) { + secp256k1_ge_mul_lambda(&g, &g); + } +#endif + ge[1 + 4 * i] = g; + ge[2 + 4 * i] = g; + secp256k1_ge_neg(&ge[3 + 4 * i], &g); + secp256k1_ge_neg(&ge[4 + 4 * i], &g); + secp256k1_gej_set_ge(&gej[1 + 4 * i], &ge[1 + 4 * i]); + random_group_element_jacobian_test(&gej[2 + 4 * i], &ge[2 + 4 * i]); + secp256k1_gej_set_ge(&gej[3 + 4 * i], &ge[3 + 4 * i]); + random_group_element_jacobian_test(&gej[4 + 4 * i], &ge[4 + 4 * i]); + for (j = 0; j < 4; j++) { + random_field_element_magnitude(&ge[1 + j + 4 * i].x); + random_field_element_magnitude(&ge[1 + j + 4 * i].y); + random_field_element_magnitude(&gej[1 + j + 4 * i].x); + random_field_element_magnitude(&gej[1 + j + 4 * i].y); + random_field_element_magnitude(&gej[1 + j + 4 * i].z); + } + } + + /* Compute z inverses. */ + { + secp256k1_fe *zs = malloc(sizeof(secp256k1_fe) * (1 + 4 * runs)); + for (i = 0; i < 4 * runs + 1; i++) { + if (i == 0) { + /* The point at infinity does not have a meaningful z inverse. Any should do. */ + do { + random_field_element_test(&zs[i]); + } while(secp256k1_fe_is_zero(&zs[i])); + } else { + zs[i] = gej[i].z; + } + } + secp256k1_fe_inv_all_var(zinv, zs, 4 * runs + 1); + free(zs); + } + + /* Generate random zf, and zfi2 = 1/zf^2, zfi3 = 1/zf^3 */ + do { + random_field_element_test(&zf); + } while(secp256k1_fe_is_zero(&zf)); + random_field_element_magnitude(&zf); + secp256k1_fe_inv_var(&zfi3, &zf); + secp256k1_fe_sqr(&zfi2, &zfi3); + secp256k1_fe_mul(&zfi3, &zfi3, &zfi2); + + for (i1 = 0; i1 < 1 + 4 * runs; i1++) { + int i2; + for (i2 = 0; i2 < 1 + 4 * runs; i2++) { + /* Compute reference result using gej + gej (var). */ + secp256k1_gej refj, resj; + secp256k1_ge ref; + secp256k1_fe zr; + secp256k1_gej_add_var(&refj, &gej[i1], &gej[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + /* Check Z ratio. */ + if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&refj)) { + secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zrz, &refj.z)); + } + secp256k1_ge_set_gej_var(&ref, &refj); + + /* Test gej + ge with Z ratio result (var). */ + secp256k1_gej_add_ge_var(&resj, &gej[i1], &ge[i2], secp256k1_gej_is_infinity(&gej[i1]) ? NULL : &zr); + ge_equals_gej(&ref, &resj); + if (!secp256k1_gej_is_infinity(&gej[i1]) && !secp256k1_gej_is_infinity(&resj)) { + secp256k1_fe zrz; secp256k1_fe_mul(&zrz, &zr, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zrz, &resj.z)); + } + + /* Test gej + ge (var, with additional Z factor). */ + { + secp256k1_ge ge2_zfi = ge[i2]; /* the second term with x and y rescaled for z = 1/zf */ + secp256k1_fe_mul(&ge2_zfi.x, &ge2_zfi.x, &zfi2); + secp256k1_fe_mul(&ge2_zfi.y, &ge2_zfi.y, &zfi3); + random_field_element_magnitude(&ge2_zfi.x); + random_field_element_magnitude(&ge2_zfi.y); + secp256k1_gej_add_zinv_var(&resj, &gej[i1], &ge2_zfi, &zf); + ge_equals_gej(&ref, &resj); + } + + /* Test gej + ge (const). */ + if (i2 != 0) { + /* secp256k1_gej_add_ge does not support its second argument being infinity. */ + secp256k1_gej_add_ge(&resj, &gej[i1], &ge[i2]); + ge_equals_gej(&ref, &resj); + } + + /* Test doubling (var). */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 == ((i2 + 3)%4)/2)) { + secp256k1_fe zr2; + /* Normal doubling with Z ratio result. */ + secp256k1_gej_double_var(&resj, &gej[i1], &zr2); + ge_equals_gej(&ref, &resj); + /* Check Z ratio. */ + secp256k1_fe_mul(&zr2, &zr2, &gej[i1].z); + CHECK(secp256k1_fe_equal_var(&zr2, &resj.z)); + /* Normal doubling. */ + secp256k1_gej_double_var(&resj, &gej[i2], NULL); + ge_equals_gej(&ref, &resj); + } + + /* Test adding opposites. */ + if ((i1 == 0 && i2 == 0) || ((i1 + 3)/4 == (i2 + 3)/4 && ((i1 + 3)%4)/2 != ((i2 + 3)%4)/2)) { + CHECK(secp256k1_ge_is_infinity(&ref)); + } + + /* Test adding infinity. */ + if (i1 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i1])); + CHECK(secp256k1_gej_is_infinity(&gej[i1])); + ge_equals_gej(&ref, &gej[i2]); + } + if (i2 == 0) { + CHECK(secp256k1_ge_is_infinity(&ge[i2])); + CHECK(secp256k1_gej_is_infinity(&gej[i2])); + ge_equals_gej(&ref, &gej[i1]); + } + } + } + + /* Test adding all points together in random order equals infinity. */ + { + secp256k1_gej sum = SECP256K1_GEJ_CONST_INFINITY; + secp256k1_gej *gej_shuffled = (secp256k1_gej *)malloc((4 * runs + 1) * sizeof(secp256k1_gej)); + for (i = 0; i < 4 * runs + 1; i++) { + gej_shuffled[i] = gej[i]; + } + for (i = 0; i < 4 * runs + 1; i++) { + int swap = i + secp256k1_rand_int(4 * runs + 1 - i); + if (swap != i) { + secp256k1_gej t = gej_shuffled[i]; + gej_shuffled[i] = gej_shuffled[swap]; + gej_shuffled[swap] = t; + } + } + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_gej_add_var(&sum, &sum, &gej_shuffled[i], NULL); + } + CHECK(secp256k1_gej_is_infinity(&sum)); + free(gej_shuffled); + } + + /* Test batch gej -> ge conversion with and without known z ratios. */ + { + secp256k1_fe *zr = (secp256k1_fe *)malloc((4 * runs + 1) * sizeof(secp256k1_fe)); + secp256k1_ge *ge_set_table = (secp256k1_ge *)malloc((4 * runs + 1) * sizeof(secp256k1_ge)); + secp256k1_ge *ge_set_all = (secp256k1_ge *)malloc((4 * runs + 1) * sizeof(secp256k1_ge)); + for (i = 0; i < 4 * runs + 1; i++) { + /* Compute gej[i + 1].z / gez[i].z (with gej[n].z taken to be 1). */ + if (i < 4 * runs) { + secp256k1_fe_mul(&zr[i + 1], &zinv[i], &gej[i + 1].z); + } + } + secp256k1_ge_set_table_gej_var(ge_set_table, gej, zr, 4 * runs + 1); + secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1, &ctx->error_callback); + for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_fe s; + random_fe_non_zero(&s); + secp256k1_gej_rescale(&gej[i], &s); + ge_equals_gej(&ge_set_table[i], &gej[i]); + ge_equals_gej(&ge_set_all[i], &gej[i]); + } + free(ge_set_table); + free(ge_set_all); + free(zr); + } + + free(ge); + free(gej); + free(zinv); +} + +void test_add_neg_y_diff_x(void) { + /* The point of this test is to check that we can add two points + * whose y-coordinates are negatives of each other but whose x + * coordinates differ. If the x-coordinates were the same, these + * points would be negatives of each other and their sum is + * infinity. This is cool because it "covers up" any degeneracy + * in the addition algorithm that would cause the xy coordinates + * of the sum to be wrong (since infinity has no xy coordinates). + * HOWEVER, if the x-coordinates are different, infinity is the + * wrong answer, and such degeneracies are exposed. This is the + * root of https://github.com/bitcoin-core/secp256k1/issues/257 + * which this test is a regression test for. + * + * These points were generated in sage as + * # secp256k1 params + * F = FiniteField (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F) + * C = EllipticCurve ([F (0), F (7)]) + * G = C.lift_x(0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798) + * N = FiniteField(G.order()) + * + * # endomorphism values (lambda is 1^{1/3} in N, beta is 1^{1/3} in F) + * x = polygen(N) + * lam = (1 - x^3).roots()[1][0] + * + * # random "bad pair" + * P = C.random_element() + * Q = -int(lam) * P + * print " P: %x %x" % P.xy() + * print " Q: %x %x" % Q.xy() + * print "P + Q: %x %x" % (P + Q).xy() + */ + secp256k1_gej aj = SECP256K1_GEJ_CONST( + 0x8d24cd95, 0x0a355af1, 0x3c543505, 0x44238d30, + 0x0643d79f, 0x05a59614, 0x2f8ec030, 0xd58977cb, + 0x001e337a, 0x38093dcd, 0x6c0f386d, 0x0b1293a8, + 0x4d72c879, 0xd7681924, 0x44e6d2f3, 0x9190117d + ); + secp256k1_gej bj = SECP256K1_GEJ_CONST( + 0xc7b74206, 0x1f788cd9, 0xabd0937d, 0x164a0d86, + 0x95f6ff75, 0xf19a4ce9, 0xd013bd7b, 0xbf92d2a7, + 0xffe1cc85, 0xc7f6c232, 0x93f0c792, 0xf4ed6c57, + 0xb28d3786, 0x2897e6db, 0xbb192d0b, 0x6e6feab2 + ); + secp256k1_gej sumj = SECP256K1_GEJ_CONST( + 0x671a63c0, 0x3efdad4c, 0x389a7798, 0x24356027, + 0xb3d69010, 0x278625c3, 0x5c86d390, 0x184a8f7a, + 0x5f6409c2, 0x2ce01f2b, 0x511fd375, 0x25071d08, + 0xda651801, 0x70e95caf, 0x8f0d893c, 0xbed8fbbe + ); + secp256k1_ge b; + secp256k1_gej resj; + secp256k1_ge res; + secp256k1_ge_set_gej(&b, &bj); + + secp256k1_gej_add_var(&resj, &aj, &bj, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge(&resj, &aj, &b); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); + + secp256k1_gej_add_ge_var(&resj, &aj, &b, NULL); + secp256k1_ge_set_gej(&res, &resj); + ge_equals_gej(&res, &sumj); +} + +void run_ge(void) { + int i; + for (i = 0; i < count * 32; i++) { + test_ge(); + } + test_add_neg_y_diff_x(); +} + +void test_ec_combine(void) { + secp256k1_scalar sum = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_pubkey data[6]; + const secp256k1_pubkey* d[6]; + secp256k1_pubkey sd; + secp256k1_pubkey sd2; + secp256k1_gej Qj; + secp256k1_ge Q; + int i; + for (i = 1; i <= 6; i++) { + secp256k1_scalar s; + random_scalar_order_test(&s); + secp256k1_scalar_add(&sum, &sum, &s); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &s); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&data[i - 1], &Q); + d[i - 1] = &data[i - 1]; + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &Qj, &sum); + secp256k1_ge_set_gej(&Q, &Qj); + secp256k1_pubkey_save(&sd, &Q); + CHECK(secp256k1_ec_pubkey_combine(ctx, &sd2, d, i) == 1); + CHECK(memcmp(&sd, &sd2, sizeof(sd)) == 0); + } +} + +void run_ec_combine(void) { + int i; + for (i = 0; i < count * 8; i++) { + test_ec_combine(); + } +} + +void test_group_decompress(const secp256k1_fe* x) { + /* The input itself, normalized. */ + secp256k1_fe fex = *x; + secp256k1_fe fez; + /* Results of set_xquad_var, set_xo_var(..., 0), set_xo_var(..., 1). */ + secp256k1_ge ge_quad, ge_even, ge_odd; + secp256k1_gej gej_quad; + /* Return values of the above calls. */ + int res_quad, res_even, res_odd; + + secp256k1_fe_normalize_var(&fex); + + res_quad = secp256k1_ge_set_xquad(&ge_quad, &fex); + res_even = secp256k1_ge_set_xo_var(&ge_even, &fex, 0); + res_odd = secp256k1_ge_set_xo_var(&ge_odd, &fex, 1); + + CHECK(res_quad == res_even); + CHECK(res_quad == res_odd); + + if (res_quad) { + secp256k1_fe_normalize_var(&ge_quad.x); + secp256k1_fe_normalize_var(&ge_odd.x); + secp256k1_fe_normalize_var(&ge_even.x); + secp256k1_fe_normalize_var(&ge_quad.y); + secp256k1_fe_normalize_var(&ge_odd.y); + secp256k1_fe_normalize_var(&ge_even.y); + + /* No infinity allowed. */ + CHECK(!ge_quad.infinity); + CHECK(!ge_even.infinity); + CHECK(!ge_odd.infinity); + + /* Check that the x coordinates check out. */ + CHECK(secp256k1_fe_equal_var(&ge_quad.x, x)); + CHECK(secp256k1_fe_equal_var(&ge_even.x, x)); + CHECK(secp256k1_fe_equal_var(&ge_odd.x, x)); + + /* Check that the Y coordinate result in ge_quad is a square. */ + CHECK(secp256k1_fe_is_quad_var(&ge_quad.y)); + + /* Check odd/even Y in ge_odd, ge_even. */ + CHECK(secp256k1_fe_is_odd(&ge_odd.y)); + CHECK(!secp256k1_fe_is_odd(&ge_even.y)); + + /* Check secp256k1_gej_has_quad_y_var. */ + secp256k1_gej_set_ge(&gej_quad, &ge_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + do { + random_fe_test(&fez); + } while (secp256k1_fe_is_zero(&fez)); + secp256k1_gej_rescale(&gej_quad, &fez); + CHECK(!secp256k1_gej_has_quad_y_var(&gej_quad)); + secp256k1_gej_neg(&gej_quad, &gej_quad); + CHECK(secp256k1_gej_has_quad_y_var(&gej_quad)); + } +} + +void run_group_decompress(void) { + int i; + for (i = 0; i < count * 4; i++) { + secp256k1_fe fe; + random_fe_test(&fe); + test_group_decompress(&fe); + } +} + +/***** ECMULT TESTS *****/ + +void run_ecmult_chain(void) { + /* random starting point A (on the curve) */ + secp256k1_gej a = SECP256K1_GEJ_CONST( + 0x8b30bbe9, 0xae2a9906, 0x96b22f67, 0x0709dff3, + 0x727fd8bc, 0x04d3362c, 0x6c7bf458, 0xe2846004, + 0xa357ae91, 0x5c4a6528, 0x1309edf2, 0x0504740f, + 0x0eb33439, 0x90216b4f, 0x81063cb6, 0x5f2f7e0f + ); + /* two random initial factors xn and gn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x84cc5452, 0xf7fde1ed, 0xb4d38a8c, 0xe9b1b84c, + 0xcef31f14, 0x6e569be9, 0x705d357a, 0x42985407 + ); + secp256k1_scalar gn = SECP256K1_SCALAR_CONST( + 0xa1e58d22, 0x553dcd42, 0xb2398062, 0x5d4c57a9, + 0x6e9323d4, 0x2b3152e5, 0xca2c3990, 0xedc7c9de + ); + /* two small multipliers to be applied to xn and gn in every iteration: */ + static const secp256k1_scalar xf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x1337); + static const secp256k1_scalar gf = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0x7113); + /* accumulators with the resulting coefficients to A and G */ + secp256k1_scalar ae = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_scalar ge = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + /* actual points */ + secp256k1_gej x; + secp256k1_gej x2; + int i; + + /* the point being computed */ + x = a; + for (i = 0; i < 200*count; i++) { + /* in each iteration, compute X = xn*X + gn*G; */ + secp256k1_ecmult(&ctx->ecmult_ctx, &x, &x, &xn, &gn); + /* also compute ae and ge: the actual accumulated factors for A and G */ + /* if X was (ae*A+ge*G), xn*X + gn*G results in (xn*ae*A + (xn*ge+gn)*G) */ + secp256k1_scalar_mul(&ae, &ae, &xn); + secp256k1_scalar_mul(&ge, &ge, &xn); + secp256k1_scalar_add(&ge, &ge, &gn); + /* modify xn and gn */ + secp256k1_scalar_mul(&xn, &xn, &xf); + secp256k1_scalar_mul(&gn, &gn, &gf); + + /* verify */ + if (i == 19999) { + /* expected result after 19999 iterations */ + secp256k1_gej rp = SECP256K1_GEJ_CONST( + 0xD6E96687, 0xF9B10D09, 0x2A6F3543, 0x9D86CEBE, + 0xA4535D0D, 0x409F5358, 0x6440BD74, 0xB933E830, + 0xB95CBCA2, 0xC77DA786, 0x539BE8FD, 0x53354D2D, + 0x3B4F566A, 0xE6580454, 0x07ED6015, 0xEE1B2A88 + ); + + secp256k1_gej_neg(&rp, &rp); + secp256k1_gej_add_var(&rp, &rp, &x, NULL); + CHECK(secp256k1_gej_is_infinity(&rp)); + } + } + /* redo the computation, but directly with the resulting ae and ge coefficients: */ + secp256k1_ecmult(&ctx->ecmult_ctx, &x2, &a, &ae, &ge); + secp256k1_gej_neg(&x2, &x2); + secp256k1_gej_add_var(&x2, &x2, &x, NULL); + CHECK(secp256k1_gej_is_infinity(&x2)); +} + +void test_point_times_order(const secp256k1_gej *point) { + /* X * (point + G) + (order-X) * (pointer + G) = 0 */ + secp256k1_scalar x; + secp256k1_scalar nx; + secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_gej res1, res2; + secp256k1_ge res3; + unsigned char pub[65]; + size_t psize = 65; + random_scalar_order_test(&x); + secp256k1_scalar_negate(&nx, &x); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &x, &x); /* calc res1 = x * point + x * G; */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res2, point, &nx, &nx); /* calc res2 = (order - x) * point + (order - x) * G; */ + secp256k1_gej_add_var(&res1, &res1, &res2, NULL); + CHECK(secp256k1_gej_is_infinity(&res1)); + CHECK(secp256k1_gej_is_valid_var(&res1) == 0); + secp256k1_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + CHECK(secp256k1_ge_is_valid_var(&res3) == 0); + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 0) == 0); + psize = 65; + CHECK(secp256k1_eckey_pubkey_serialize(&res3, pub, &psize, 1) == 0); + /* check zero/one edge cases */ + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &zero); + secp256k1_ge_set_gej(&res3, &res1); + CHECK(secp256k1_ge_is_infinity(&res3)); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &one, &zero); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_gej(&res3, point); + secp256k1_ecmult(&ctx->ecmult_ctx, &res1, point, &zero, &one); + secp256k1_ge_set_gej(&res3, &res1); + ge_equals_ge(&res3, &secp256k1_ge_const_g); +} + +void run_point_times_order(void) { + int i; + secp256k1_fe x = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 2); + static const secp256k1_fe xr = SECP256K1_FE_CONST( + 0x7603CB59, 0xB0EF6C63, 0xFE608479, 0x2A0C378C, + 0xDB3233A8, 0x0F8A9A09, 0xA877DEAD, 0x31B38C45 + ); + for (i = 0; i < 500; i++) { + secp256k1_ge p; + if (secp256k1_ge_set_xo_var(&p, &x, 1)) { + secp256k1_gej j; + CHECK(secp256k1_ge_is_valid_var(&p)); + secp256k1_gej_set_ge(&j, &p); + CHECK(secp256k1_gej_is_valid_var(&j)); + test_point_times_order(&j); + } + secp256k1_fe_sqr(&x, &x); + } + secp256k1_fe_normalize_var(&x); + CHECK(secp256k1_fe_equal_var(&x, &xr)); +} + +void ecmult_const_random_mult(void) { + /* random starting point A (on the curve) */ + secp256k1_ge a = SECP256K1_GE_CONST( + 0x6d986544, 0x57ff52b8, 0xcf1b8126, 0x5b802a5b, + 0xa97f9263, 0xb1e88044, 0x93351325, 0x91bc450a, + 0x535c59f7, 0x325e5d2b, 0xc391fbe8, 0x3c12787c, + 0x337e4a98, 0xe82a9011, 0x0123ba37, 0xdd769c7d + ); + /* random initial factor xn */ + secp256k1_scalar xn = SECP256K1_SCALAR_CONST( + 0x649d4f77, 0xc4242df7, 0x7f2079c9, 0x14530327, + 0xa31b876a, 0xd2d8ce2a, 0x2236d5c6, 0xd7b2029b + ); + /* expected xn * A (from sage) */ + secp256k1_ge expected_b = SECP256K1_GE_CONST( + 0x23773684, 0x4d209dc7, 0x098a786f, 0x20d06fcd, + 0x070a38bf, 0xc11ac651, 0x03004319, 0x1e2a8786, + 0xed8c3b8e, 0xc06dd57b, 0xd06ea66e, 0x45492b0f, + 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 + ); + secp256k1_gej b; + secp256k1_ecmult_const(&b, &a, &xn); + + CHECK(secp256k1_ge_is_valid_var(&a)); + ge_equals_gej(&expected_b, &b); +} + +void ecmult_const_commutativity(void) { + secp256k1_scalar a; + secp256k1_scalar b; + secp256k1_gej res1; + secp256k1_gej res2; + secp256k1_ge mid1; + secp256k1_ge mid2; + random_scalar_order_test(&a); + random_scalar_order_test(&b); + + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + secp256k1_ecmult_const(&res1, &mid1, &b); + secp256k1_ecmult_const(&res2, &mid2, &a); + secp256k1_ge_set_gej(&mid1, &res1); + secp256k1_ge_set_gej(&mid2, &res2); + ge_equals_ge(&mid1, &mid2); +} + +void ecmult_const_mult_zero_one(void) { + secp256k1_scalar zero = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 0); + secp256k1_scalar one = SECP256K1_SCALAR_CONST(0, 0, 0, 0, 0, 0, 0, 1); + secp256k1_scalar negone; + secp256k1_gej res1; + secp256k1_ge res2; + secp256k1_ge point; + secp256k1_scalar_negate(&negone, &one); + + random_group_element_test(&point); + secp256k1_ecmult_const(&res1, &point, &zero); + secp256k1_ge_set_gej(&res2, &res1); + CHECK(secp256k1_ge_is_infinity(&res2)); + secp256k1_ecmult_const(&res1, &point, &one); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); + secp256k1_ecmult_const(&res1, &point, &negone); + secp256k1_gej_neg(&res1, &res1); + secp256k1_ge_set_gej(&res2, &res1); + ge_equals_ge(&res2, &point); +} + +void ecmult_const_chain_multiply(void) { + /* Check known result (randomly generated test problem from sage) */ + const secp256k1_scalar scalar = SECP256K1_SCALAR_CONST( + 0x4968d524, 0x2abf9b7a, 0x466abbcf, 0x34b11b6d, + 0xcd83d307, 0x827bed62, 0x05fad0ce, 0x18fae63b + ); + const secp256k1_gej expected_point = SECP256K1_GEJ_CONST( + 0x5494c15d, 0x32099706, 0xc2395f94, 0x348745fd, + 0x757ce30e, 0x4e8c90fb, 0xa2bad184, 0xf883c69f, + 0x5d195d20, 0xe191bf7f, 0x1be3e55f, 0x56a80196, + 0x6071ad01, 0xf1462f66, 0xc997fa94, 0xdb858435 + ); + secp256k1_gej point; + secp256k1_ge res; + int i; + + secp256k1_gej_set_ge(&point, &secp256k1_ge_const_g); + for (i = 0; i < 100; ++i) { + secp256k1_ge tmp; + secp256k1_ge_set_gej(&tmp, &point); + secp256k1_ecmult_const(&point, &tmp, &scalar); + } + secp256k1_ge_set_gej(&res, &point); + ge_equals_gej(&res, &expected_point); +} + +void run_ecmult_const_tests(void) { + ecmult_const_mult_zero_one(); + ecmult_const_random_mult(); + ecmult_const_commutativity(); + ecmult_const_chain_multiply(); +} + +void test_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, two, t; + int wnaf[256]; + int zeroes = -1; + int i; + int bits; + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&two, 2); + bits = secp256k1_ecmult_wnaf(wnaf, 256, number, w); + CHECK(bits <= 256); + for (i = bits-1; i >= 0; i--) { + int v = wnaf[i]; + secp256k1_scalar_mul(&x, &x, &two); + if (v) { + CHECK(zeroes == -1 || zeroes >= w-1); /* check that distance between non-zero elements is at least w-1 */ + zeroes=0; + CHECK((v & 1) == 1); /* check non-zero elements are odd */ + CHECK(v <= (1 << (w-1)) - 1); /* check range below */ + CHECK(v >= -(1 << (w-1)) - 1); /* check range above */ + } else { + CHECK(zeroes != -1); /* check that no unnecessary zero padding exists */ + zeroes++; + } + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + CHECK(secp256k1_scalar_eq(&x, number)); /* check that wnaf represents number */ +} + +void test_constant_wnaf_negate(const secp256k1_scalar *number) { + secp256k1_scalar neg1 = *number; + secp256k1_scalar neg2 = *number; + int sign1 = 1; + int sign2 = 1; + + if (!secp256k1_scalar_get_bits(&neg1, 0, 1)) { + secp256k1_scalar_negate(&neg1, &neg1); + sign1 = -1; + } + sign2 = secp256k1_scalar_cond_negate(&neg2, secp256k1_scalar_is_even(&neg2)); + CHECK(sign1 == sign2); + CHECK(secp256k1_scalar_eq(&neg1, &neg2)); +} + +void test_constant_wnaf(const secp256k1_scalar *number, int w) { + secp256k1_scalar x, shift; + int wnaf[256] = {0}; + int i; + int skew; + secp256k1_scalar num = *number; + + secp256k1_scalar_set_int(&x, 0); + secp256k1_scalar_set_int(&shift, 1 << w); + /* With USE_ENDOMORPHISM on we only consider 128-bit numbers */ +#ifdef USE_ENDOMORPHISM + for (i = 0; i < 16; ++i) { + secp256k1_scalar_shr_int(&num, 8); + } +#endif + skew = secp256k1_wnaf_const(wnaf, num, w); + + for (i = WNAF_SIZE(w); i >= 0; --i) { + secp256k1_scalar t; + int v = wnaf[i]; + CHECK(v != 0); /* check nonzero */ + CHECK(v & 1); /* check parity */ + CHECK(v > -(1 << w)); /* check range above */ + CHECK(v < (1 << w)); /* check range below */ + + secp256k1_scalar_mul(&x, &x, &shift); + if (v >= 0) { + secp256k1_scalar_set_int(&t, v); + } else { + secp256k1_scalar_set_int(&t, -v); + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_add(&x, &x, &t); + } + /* Skew num because when encoding numbers as odd we use an offset */ + secp256k1_scalar_cadd_bit(&num, skew == 2, 1); + CHECK(secp256k1_scalar_eq(&x, &num)); +} + +void run_wnaf(void) { + int i; + secp256k1_scalar n = {{0}}; + + /* Sanity check: 1 and 2 are the smallest odd and even numbers and should + * have easier-to-diagnose failure modes */ + n.d[0] = 1; + test_constant_wnaf(&n, 4); + n.d[0] = 2; + test_constant_wnaf(&n, 4); + /* Random tests */ + for (i = 0; i < count; i++) { + random_scalar_order(&n); + test_wnaf(&n, 4+(i%10)); + test_constant_wnaf_negate(&n); + test_constant_wnaf(&n, 4 + (i % 10)); + } + secp256k1_scalar_set_int(&n, 0); + CHECK(secp256k1_scalar_cond_negate(&n, 1) == -1); + CHECK(secp256k1_scalar_is_zero(&n)); + CHECK(secp256k1_scalar_cond_negate(&n, 0) == 1); + CHECK(secp256k1_scalar_is_zero(&n)); +} + +void test_ecmult_constants(void) { + /* Test ecmult_gen() for [0..36) and [order-36..0). */ + secp256k1_scalar x; + secp256k1_gej r; + secp256k1_ge ng; + int i; + int j; + secp256k1_ge_neg(&ng, &secp256k1_ge_const_g); + for (i = 0; i < 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&secp256k1_ge_const_g, &r); + } + secp256k1_gej_add_ge(&r, &r, &ng); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } + for (i = 1; i <= 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_scalar_negate(&x, &x); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&ng, &r); + } + secp256k1_gej_add_ge(&r, &r, &secp256k1_ge_const_g); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } +} + +void run_ecmult_constants(void) { + test_ecmult_constants(); +} + +void test_ecmult_gen_blind(void) { + /* Test ecmult_gen() blinding and confirm that the blinding changes, the affine points match, and the z's don't match. */ + secp256k1_scalar key; + secp256k1_scalar b; + unsigned char seed32[32]; + secp256k1_gej pgej; + secp256k1_gej pgej2; + secp256k1_gej i; + secp256k1_ge pge; + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); + secp256k1_rand256(seed32); + b = ctx->ecmult_gen_ctx.blind; + i = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej2, &key); + CHECK(!gej_xyz_equals_gej(&pgej, &pgej2)); + CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial)); + secp256k1_ge_set_gej(&pge, &pgej); + ge_equals_gej(&pge, &pgej2); +} + +void test_ecmult_gen_blind_reset(void) { + /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ + secp256k1_scalar b; + secp256k1_gej initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + b = ctx->ecmult_gen_ctx.blind; + initial = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial)); +} + +void run_ecmult_gen_blind(void) { + int i; + test_ecmult_gen_blind_reset(); + for (i = 0; i < 10; i++) { + test_ecmult_gen_blind(); + } +} + +#ifdef USE_ENDOMORPHISM +/***** ENDOMORPHISH TESTS *****/ +void test_scalar_split(void) { + secp256k1_scalar full; + secp256k1_scalar s1, slam; + const unsigned char zero[32] = {0}; + unsigned char tmp[32]; + + random_scalar_order_test(&full); + secp256k1_scalar_split_lambda(&s1, &slam, &full); + + /* check that both are <= 128 bits in size */ + if (secp256k1_scalar_is_high(&s1)) { + secp256k1_scalar_negate(&s1, &s1); + } + if (secp256k1_scalar_is_high(&slam)) { + secp256k1_scalar_negate(&slam, &slam); + } + + secp256k1_scalar_get_b32(tmp, &s1); + CHECK(memcmp(zero, tmp, 16) == 0); + secp256k1_scalar_get_b32(tmp, &slam); + CHECK(memcmp(zero, tmp, 16) == 0); +} + +void run_endomorphism_tests(void) { + test_scalar_split(); +} +#endif + +void ec_pubkey_parse_pointtest(const unsigned char *input, int xvalid, int yvalid) { + unsigned char pubkeyc[65]; + secp256k1_pubkey pubkey; + secp256k1_ge ge; + size_t pubkeyclen; + int32_t ecount; + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + for (pubkeyclen = 3; pubkeyclen <= 65; pubkeyclen++) { + /* Smaller sizes are tested exhaustively elsewhere. */ + int32_t i; + memcpy(&pubkeyc[1], input, 64); + VG_UNDEF(&pubkeyc[pubkeyclen], 65 - pubkeyclen); + for (i = 0; i < 256; i++) { + /* Try all type bytes. */ + int xpass; + int ypass; + int ysign; + pubkeyc[0] = i; + /* What sign does this point have? */ + ysign = (input[63] & 1) + 2; + /* For the current type (i) do we expect parsing to work? Handled all of compressed/uncompressed/hybrid. */ + xpass = xvalid && (pubkeyclen == 33) && ((i & 254) == 2); + /* Do we expect a parse and re-serialize as uncompressed to give a matching y? */ + ypass = xvalid && yvalid && ((i & 4) == ((pubkeyclen == 65) << 2)) && + ((i == 4) || ((i & 251) == ysign)) && ((pubkeyclen == 33) || (pubkeyclen == 65)); + if (xpass || ypass) { + /* These cases must parse. */ + unsigned char pubkeyo[65]; + size_t outl; + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + ecount = 0; + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 33); + CHECK(memcmp(&pubkeyo[1], &pubkeyc[1], 32) == 0); + CHECK((pubkeyclen != 33) || (pubkeyo[0] == pubkeyc[0])); + if (ypass) { + /* This test isn't always done because we decode with alternative signs, so the y won't match. */ + CHECK(pubkeyo[0] == ysign); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + secp256k1_pubkey_save(&pubkey, &ge); + VG_CHECK(&pubkey, sizeof(pubkey)); + outl = 65; + VG_UNDEF(pubkeyo, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyo, &outl, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + VG_CHECK(pubkeyo, outl); + CHECK(outl == 65); + CHECK(pubkeyo[0] == 4); + CHECK(memcmp(&pubkeyo[1], input, 64) == 0); + } + CHECK(ecount == 0); + } else { + /* These cases must fail to parse. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + } + } + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); +} + +void run_ec_pubkey_parse_test(void) { +#define SECP256K1_EC_PARSE_TEST_NVALID (12) + const unsigned char valid[SECP256K1_EC_PARSE_TEST_NVALID][64] = { + { + /* Point with leading and trailing zeros in x and y serialization. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x52, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x64, 0xef, 0xa1, 0x7b, 0x77, 0x61, 0xe1, 0xe4, 0x27, 0x06, 0x98, 0x9f, 0xb4, 0x83, + 0xb8, 0xd2, 0xd4, 0x9b, 0xf7, 0x8f, 0xae, 0x98, 0x03, 0xf0, 0x99, 0xb8, 0x34, 0xed, 0xeb, 0x00 + }, + { + /* Point with x equal to a 3rd root of unity.*/ + 0x7a, 0xe9, 0x6a, 0x2b, 0x65, 0x7c, 0x07, 0x10, 0x6e, 0x64, 0x47, 0x9e, 0xac, 0x34, 0x34, 0xe9, + 0x9c, 0xf0, 0x49, 0x75, 0x12, 0xf5, 0x89, 0x95, 0xc1, 0x39, 0x6c, 0x28, 0x71, 0x95, 0x01, 0xee, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Point with largest x. (1/2) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c, + 0x0e, 0x99, 0x4b, 0x14, 0xea, 0x72, 0xf8, 0xc3, 0xeb, 0x95, 0xc7, 0x1e, 0xf6, 0x92, 0x57, 0x5e, + 0x77, 0x50, 0x58, 0x33, 0x2d, 0x7e, 0x52, 0xd0, 0x99, 0x5c, 0xf8, 0x03, 0x88, 0x71, 0xb6, 0x7d, + }, + { + /* Point with largest x. (2/2) */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2c, + 0xf1, 0x66, 0xb4, 0xeb, 0x15, 0x8d, 0x07, 0x3c, 0x14, 0x6a, 0x38, 0xe1, 0x09, 0x6d, 0xa8, 0xa1, + 0x88, 0xaf, 0xa7, 0xcc, 0xd2, 0x81, 0xad, 0x2f, 0x66, 0xa3, 0x07, 0xfb, 0x77, 0x8e, 0x45, 0xb2, + }, + { + /* Point with smallest x. (1/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Point with smallest x. (2/2) */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, + 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41, + }, + { + /* Point with largest y. (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with largest y. (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with largest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + }, + { + /* Point with smallest y. (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Point with smallest y. (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Point with smallest y. (3/3) */ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + } + }; +#define SECP256K1_EC_PARSE_TEST_NXVALID (4) + const unsigned char onlyxvalid[SECP256K1_EC_PARSE_TEST_NXVALID][64] = { + { + /* Valid if y overflow ignored (y = 1 mod p). (1/3) */ + 0x1f, 0xe1, 0xe5, 0xef, 0x3f, 0xce, 0xb5, 0xc1, 0x35, 0xab, 0x77, 0x41, 0x33, 0x3c, 0xe5, 0xa6, + 0xe8, 0x0d, 0x68, 0x16, 0x76, 0x53, 0xf6, 0xb2, 0xb2, 0x4b, 0xcb, 0xcf, 0xaa, 0xaf, 0xf5, 0x07, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (2/3) */ + 0xcb, 0xb0, 0xde, 0xab, 0x12, 0x57, 0x54, 0xf1, 0xfd, 0xb2, 0x03, 0x8b, 0x04, 0x34, 0xed, 0x9c, + 0xb3, 0xfb, 0x53, 0xab, 0x73, 0x53, 0x91, 0x12, 0x99, 0x94, 0xa5, 0x35, 0xd9, 0x25, 0xf6, 0x73, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* Valid if y overflow ignored (y = 1 mod p). (3/3)*/ + 0x14, 0x6d, 0x3b, 0x65, 0xad, 0xd9, 0xf5, 0x4c, 0xcc, 0xa2, 0x85, 0x33, 0xc8, 0x8e, 0x2c, 0xbc, + 0x63, 0xf7, 0x44, 0x3e, 0x16, 0x58, 0x78, 0x3a, 0xb4, 0x1f, 0x8e, 0xf9, 0x7c, 0x2a, 0x10, 0xb5, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + }, + { + /* x on curve, y is from y^2 = x^3 + 8. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03 + } + }; +#define SECP256K1_EC_PARSE_TEST_NINVALID (7) + const unsigned char invalid[SECP256K1_EC_PARSE_TEST_NINVALID][64] = { + { + /* x is third root of -8, y is -1 * (x^3+7); also on the curve for y^2 = x^3 + 9. */ + 0x0a, 0x2d, 0x2b, 0xa9, 0x35, 0x07, 0xf1, 0xdf, 0x23, 0x37, 0x70, 0xc2, 0xa7, 0x97, 0x96, 0x2c, + 0xc6, 0x1f, 0x6d, 0x15, 0xda, 0x14, 0xec, 0xd4, 0x7d, 0x8d, 0x27, 0xae, 0x1c, 0xd5, 0xf8, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + 0x42, 0x18, 0xf2, 0x0a, 0xe6, 0xc6, 0x46, 0xb3, 0x63, 0xdb, 0x68, 0x60, 0x58, 0x22, 0xfb, 0x14, + 0x26, 0x4c, 0xa8, 0xd2, 0x58, 0x7f, 0xdd, 0x6f, 0xbc, 0x75, 0x0d, 0x58, 0x7e, 0x76, 0xa7, 0xee, + }, + { + /* Valid if x overflow ignored (x = 1 mod p). */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x30, + 0xbd, 0xe7, 0x0d, 0xf5, 0x19, 0x39, 0xb9, 0x4c, 0x9c, 0x24, 0x97, 0x9f, 0xa7, 0xdd, 0x04, 0xeb, + 0xd9, 0xb3, 0x57, 0x2d, 0xa7, 0x80, 0x22, 0x90, 0x43, 0x8a, 0xf2, 0xa6, 0x81, 0x89, 0x54, 0x41, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + 0xf4, 0x84, 0x14, 0x5c, 0xb0, 0x14, 0x9b, 0x82, 0x5d, 0xff, 0x41, 0x2f, 0xa0, 0x52, 0xa8, 0x3f, + 0xcb, 0x72, 0xdb, 0x61, 0xd5, 0x6f, 0x37, 0x70, 0xce, 0x06, 0x6b, 0x73, 0x49, 0xa2, 0xaa, 0x28, + }, + { + /* x is -1, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 5. */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff, 0xfc, 0x2e, + 0x0b, 0x7b, 0xeb, 0xa3, 0x4f, 0xeb, 0x64, 0x7d, 0xa2, 0x00, 0xbe, 0xd0, 0x5f, 0xad, 0x57, 0xc0, + 0x34, 0x8d, 0x24, 0x9e, 0x2a, 0x90, 0xc8, 0x8f, 0x31, 0xf9, 0x94, 0x8b, 0xb6, 0x5d, 0x52, 0x07, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x53, 0x7e, 0xef, 0xdf, 0xc1, 0x60, 0x6a, 0x07, 0x27, 0xcd, 0x69, 0xb4, 0xa7, 0x33, 0x3d, + 0x38, 0xed, 0x44, 0xe3, 0x93, 0x2a, 0x71, 0x79, 0xee, 0xcb, 0x4b, 0x6f, 0xba, 0x93, 0x60, 0xdc, + }, + { + /* x is zero, y is the result of the sqrt ladder; also on the curve for y^2 = x^3 - 7. */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x70, 0xac, 0x81, 0x10, 0x20, 0x3e, 0x9f, 0x95, 0xf8, 0xd8, 0x32, 0x96, 0x4b, 0x58, 0xcc, 0xc2, + 0xc7, 0x12, 0xbb, 0x1c, 0x6c, 0xd5, 0x8e, 0x86, 0x11, 0x34, 0xb4, 0x8f, 0x45, 0x6c, 0x9b, 0x53 + } + }; + const unsigned char pubkeyc[66] = { + /* Serialization of G. */ + 0x04, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98, 0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, + 0xA8, 0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19, 0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, + 0xB8, 0x00 + }; + unsigned char sout[65]; + unsigned char shortkey[2]; + secp256k1_ge ge; + secp256k1_pubkey pubkey; + size_t len; + int32_t i; + int32_t ecount; + int32_t ecount2; + ecount = 0; + /* Nothing should be reading this far into pubkeyc. */ + VG_UNDEF(&pubkeyc[65], 1); + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + /* Zero length claimed, fail, zeroize, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(shortkey, 2); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 0) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* Length one claimed, fail, zeroize, no illegal arg error. */ + for (i = 0; i < 256 ; i++) { + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + shortkey[0] = i; + VG_UNDEF(&shortkey[1], 1); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 1) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + /* Length two claimed, fail, zeroize, no illegal arg error. */ + for (i = 0; i < 65536 ; i++) { + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + shortkey[0] = i & 255; + shortkey[1] = i >> 8; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, shortkey, 2) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + } + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + /* 33 bytes claimed on otherwise valid input starting with 0x04, fail, zeroize output, no illegal arg error. */ + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 33) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* NULL pubkey, illegal arg error. Pubkey isn't rewritten before this step, since it's NULL into the parser. */ + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, pubkeyc, 65) == 0); + CHECK(ecount == 2); + /* NULL input string. Illegal arg and zeroize output. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, NULL, 65) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 1); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 2); + /* 64 bytes claimed on input starting with 0x04, fail, zeroize output, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 64) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* 66 bytes claimed, fail, zeroize output, no illegal arg error. */ + memset(&pubkey, 0xfe, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 66) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 0); + CHECK(ecount == 1); + /* Valid parse. */ + memset(&pubkey, 0, sizeof(pubkey)); + ecount = 0; + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, 65) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(ecount == 0); + VG_UNDEF(&ge, sizeof(ge)); + CHECK(secp256k1_pubkey_load(ctx, &ge, &pubkey) == 1); + VG_CHECK(&ge.x, sizeof(ge.x)); + VG_CHECK(&ge.y, sizeof(ge.y)); + VG_CHECK(&ge.infinity, sizeof(ge.infinity)); + ge_equals_ge(&secp256k1_ge_const_g, &ge); + CHECK(ecount == 0); + /* secp256k1_ec_pubkey_serialize illegal args. */ + ecount = 0; + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, NULL, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 1); + CHECK(len == 0); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, NULL, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 0); + CHECK(ecount == 2); + len = 65; + VG_UNDEF(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, NULL, SECP256K1_EC_UNCOMPRESSED) == 0); + VG_CHECK(sout, 65); + CHECK(ecount == 3); + CHECK(len == 0); + len = 65; + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, ~0) == 0); + CHECK(ecount == 4); + CHECK(len == 0); + len = 65; + VG_UNDEF(sout, 65); + CHECK(secp256k1_ec_pubkey_serialize(ctx, sout, &len, &pubkey, SECP256K1_EC_UNCOMPRESSED) == 1); + VG_CHECK(sout, 65); + CHECK(ecount == 4); + CHECK(len == 65); + /* Multiple illegal args. Should still set arg error only once. */ + ecount = 0; + ecount2 = 11; + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(ecount == 1); + /* Does the illegal arg callback actually change the behavior? */ + secp256k1_context_set_illegal_callback(ctx, uncounting_illegal_callback_fn, &ecount2); + CHECK(secp256k1_ec_pubkey_parse(ctx, NULL, NULL, 65) == 0); + CHECK(ecount == 1); + CHECK(ecount2 == 10); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + /* Try a bunch of prefabbed points with all possible encodings. */ + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NVALID; i++) { + ec_pubkey_parse_pointtest(valid[i], 1, 1); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NXVALID; i++) { + ec_pubkey_parse_pointtest(onlyxvalid[i], 1, 0); + } + for (i = 0; i < SECP256K1_EC_PARSE_TEST_NINVALID; i++) { + ec_pubkey_parse_pointtest(invalid[i], 0, 0); + } +} + +void run_eckey_edge_case_test(void) { + const unsigned char orderc[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41 + }; + const unsigned char zeros[sizeof(secp256k1_pubkey)] = {0x00}; + unsigned char ctmp[33]; + unsigned char ctmp2[33]; + secp256k1_pubkey pubkey; + secp256k1_pubkey pubkey2; + secp256k1_pubkey pubkey_one; + secp256k1_pubkey pubkey_negone; + const secp256k1_pubkey *pubkeys[3]; + size_t len; + int32_t ecount; + /* Group order is too large, reject. */ + CHECK(secp256k1_ec_seckey_verify(ctx, orderc) == 0); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, orderc) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* Maximum value is too large, reject. */ + memset(ctmp, 255, 32); + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* Zero is too small, reject. */ + memset(ctmp, 0, 32); + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* One must be accepted. */ + ctmp[31] = 0x01; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + pubkey_one = pubkey; + /* Group order + 1 is too large, reject. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x42; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 0); + memset(&pubkey, 1, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 0); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* -1 must be accepted. */ + ctmp[31] = 0x40; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + memset(&pubkey, 0, sizeof(pubkey)); + VG_UNDEF(&pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, ctmp) == 1); + VG_CHECK(&pubkey, sizeof(pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + pubkey_negone = pubkey; + /* Tweak of zero leaves the value changed. */ + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, ctmp2) == 1); + CHECK(memcmp(orderc, ctmp, 31) == 0 && ctmp[31] == 0x40); + memcpy(&pubkey2, &pubkey, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Multiply tweak of zero zeroizes the output. */ + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, ctmp2) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Overflowing key tweak zeroizes. */ + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, orderc) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, orderc) == 0); + CHECK(memcmp(zeros, ctmp, 32) == 0); + memcpy(ctmp, orderc, 32); + ctmp[31] = 0x40; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, orderc) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Private key tweaks results in a key of zero. */ + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 0); + CHECK(memcmp(zeros, ctmp2, 32) == 0); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + /* Tweak computation wraps and results in a key of 1. */ + ctmp2[31] = 2; + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp2, ctmp) == 1); + CHECK(memcmp(ctmp2, zeros, 31) == 0 && ctmp2[31] == 1); + ctmp2[31] = 2; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + ctmp2[31] = 1; + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Tweak mul * 2 = 1+1. */ + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 1); + ctmp2[31] = 2; + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + /* Test argument errors. */ + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + CHECK(ecount == 0); + /* Zeroize pubkey on parse error. */ + memset(&pubkey, 0, 32); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(memcmp(&pubkey, zeros, sizeof(pubkey)) == 0); + memcpy(&pubkey, &pubkey2, sizeof(pubkey)); + memset(&pubkey2, 0, 32); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey2, ctmp2) == 0); + CHECK(ecount == 2); + CHECK(memcmp(&pubkey2, zeros, sizeof(pubkey2)) == 0); + /* Plain argument errors. */ + ecount = 0; + CHECK(secp256k1_ec_seckey_verify(ctx, ctmp) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ec_seckey_verify(ctx, NULL) == 0); + CHECK(ecount == 1); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 4; + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 4; + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_privkey_tweak_add(ctx, ctmp, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + memset(ctmp2, 0, 32); + ctmp2[31] = 1; + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, NULL, ctmp2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_privkey_tweak_mul(ctx, ctmp, NULL) == 0); + CHECK(ecount == 2); + ecount = 0; + CHECK(secp256k1_ec_pubkey_create(ctx, NULL, ctmp) == 0); + CHECK(ecount == 1); + memset(&pubkey, 1, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 2); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + /* secp256k1_ec_pubkey_combine tests. */ + ecount = 0; + pubkeys[0] = &pubkey_one; + VG_UNDEF(&pubkeys[0], sizeof(secp256k1_pubkey *)); + VG_UNDEF(&pubkeys[1], sizeof(secp256k1_pubkey *)); + VG_UNDEF(&pubkeys[2], sizeof(secp256k1_pubkey *)); + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 0) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ec_pubkey_combine(ctx, NULL, pubkeys, 1) == 0); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 2); + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, NULL, 1) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 3); + pubkeys[0] = &pubkey_negone; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 1) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_negone, SECP256K1_EC_COMPRESSED) == 1); + CHECK(memcmp(ctmp, ctmp2, 33) == 0); + /* Result is infinity. */ + pubkeys[0] = &pubkey_one; + pubkeys[1] = &pubkey_negone; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 0); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) == 0); + CHECK(ecount == 3); + /* Passes through infinity but comes out one. */ + pubkeys[2] = &pubkey_one; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 3) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + len = 33; + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp, &len, &pubkey, SECP256K1_EC_COMPRESSED) == 1); + CHECK(secp256k1_ec_pubkey_serialize(ctx, ctmp2, &len, &pubkey_one, SECP256K1_EC_COMPRESSED) == 1); + CHECK(memcmp(ctmp, ctmp2, 33) == 0); + /* Adds to two. */ + pubkeys[1] = &pubkey_one; + memset(&pubkey, 255, sizeof(secp256k1_pubkey)); + VG_UNDEF(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(secp256k1_ec_pubkey_combine(ctx, &pubkey, pubkeys, 2) == 1); + VG_CHECK(&pubkey, sizeof(secp256k1_pubkey)); + CHECK(memcmp(&pubkey, zeros, sizeof(secp256k1_pubkey)) > 0); + CHECK(ecount == 3); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); +} + +void random_sign(secp256k1_scalar *sigr, secp256k1_scalar *sigs, const secp256k1_scalar *key, const secp256k1_scalar *msg, int *recid) { + secp256k1_scalar nonce; + do { + random_scalar_order_test(&nonce); + } while(!secp256k1_ecdsa_sig_sign(&ctx->ecmult_gen_ctx, sigr, sigs, key, msg, &nonce, recid)); +} + +void test_ecdsa_sign_verify(void) { + secp256k1_gej pubj; + secp256k1_ge pub; + secp256k1_scalar one; + secp256k1_scalar msg, key; + secp256k1_scalar sigr, sigs; + int recid; + int getrec; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pubj, &key); + secp256k1_ge_set_gej(&pub, &pubj); + getrec = secp256k1_rand_bits(1); + random_sign(&sigr, &sigs, &key, &msg, getrec?&recid:NULL); + if (getrec) { + CHECK(recid >= 0 && recid < 4); + } + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); + secp256k1_scalar_set_int(&one, 1); + secp256k1_scalar_add(&msg, &msg, &one); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &pub, &msg)); +} + +void run_ecdsa_sign_verify(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_sign_verify(); + } +} + +/** Dummy nonce generation function that just uses a precomputed nonce, and fails if it is not accepted. Use only for testing. */ +static int precomputed_nonce_function(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void)msg32; + (void)key32; + (void)algo16; + memcpy(nonce32, data, 32); + return (counter == 0); +} + +static int nonce_function_test_fail(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that has a fatal error on the first counter value. */ + if (counter == 0) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 1); +} + +static int nonce_function_test_retry(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + /* Dummy nonce generator that produces unacceptable nonces for the first several counter values. */ + if (counter < 3) { + memset(nonce32, counter==0 ? 0 : 255, 32); + if (counter == 2) { + nonce32[31]--; + } + return 1; + } + if (counter < 5) { + static const unsigned char order[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; + memcpy(nonce32, order, 32); + if (counter == 4) { + nonce32[31]++; + } + return 1; + } + /* Retry rate of 6979 is negligible esp. as we only call this in deterministic tests. */ + /* If someone does fine a case where it retries for secp256k1, we'd like to know. */ + if (counter > 5) { + return 0; + } + return nonce_function_rfc6979(nonce32, msg32, key32, algo16, data, counter - 5); +} + +int is_empty_signature(const secp256k1_ecdsa_signature *sig) { + static const unsigned char res[sizeof(secp256k1_ecdsa_signature)] = {0}; + return memcmp(sig, res, sizeof(secp256k1_ecdsa_signature)) == 0; +} + +void test_ecdsa_end_to_end(void) { + unsigned char extra[32] = {0x00}; + unsigned char privkey[32]; + unsigned char message[32]; + unsigned char privkey2[32]; + secp256k1_ecdsa_signature signature[6]; + secp256k1_scalar r, s; + unsigned char sig[74]; + size_t siglen = 74; + unsigned char pubkeyc[65]; + size_t pubkeyclen = 65; + secp256k1_pubkey pubkey; + unsigned char seckey[300]; + size_t seckeylen = 300; + + /* Generate a random key and message. */ + { + secp256k1_scalar msg, key; + random_scalar_order_test(&msg); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(privkey, &key); + secp256k1_scalar_get_b32(message, &msg); + } + + /* Construct and verify corresponding public key. */ + CHECK(secp256k1_ec_seckey_verify(ctx, privkey) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, privkey) == 1); + + /* Verify exporting and importing public key. */ + CHECK(secp256k1_ec_pubkey_serialize(ctx, pubkeyc, &pubkeyclen, &pubkey, secp256k1_rand_bits(1) == 1 ? SECP256K1_EC_COMPRESSED : SECP256K1_EC_UNCOMPRESSED)); + memset(&pubkey, 0, sizeof(pubkey)); + CHECK(secp256k1_ec_pubkey_parse(ctx, &pubkey, pubkeyc, pubkeyclen) == 1); + + /* Verify private key import and export. */ + CHECK(ec_privkey_export_der(ctx, seckey, &seckeylen, privkey, secp256k1_rand_bits(1) == 1)); + CHECK(ec_privkey_import_der(ctx, privkey2, seckey, seckeylen) == 1); + CHECK(memcmp(privkey, privkey2, 32) == 0); + + /* Optionally tweak the keys using addition. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[32]; + secp256k1_pubkey pubkey2; + secp256k1_rand256_test(rnd); + ret1 = secp256k1_ec_privkey_tweak_add(ctx, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_add(ctx, &pubkey, rnd); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Optionally tweak the keys using multiplication. */ + if (secp256k1_rand_int(3) == 0) { + int ret1; + int ret2; + unsigned char rnd[32]; + secp256k1_pubkey pubkey2; + secp256k1_rand256_test(rnd); + ret1 = secp256k1_ec_privkey_tweak_mul(ctx, privkey, rnd); + ret2 = secp256k1_ec_pubkey_tweak_mul(ctx, &pubkey, rnd); + CHECK(ret1 == ret2); + if (ret1 == 0) { + return; + } + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey2, privkey) == 1); + CHECK(memcmp(&pubkey, &pubkey2, sizeof(pubkey)) == 0); + } + + /* Sign. */ + CHECK(secp256k1_ecdsa_sign(ctx, &signature[0], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[4], message, privkey, NULL, NULL) == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &signature[1], message, privkey, NULL, extra) == 1); + extra[31] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &signature[2], message, privkey, NULL, extra) == 1); + extra[31] = 0; + extra[0] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &signature[3], message, privkey, NULL, extra) == 1); + CHECK(memcmp(&signature[0], &signature[4], sizeof(signature[0])) == 0); + CHECK(memcmp(&signature[0], &signature[1], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[0], &signature[2], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[0], &signature[3], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[1], &signature[2], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[1], &signature[3], sizeof(signature[0])) != 0); + CHECK(memcmp(&signature[2], &signature[3], sizeof(signature[0])) != 0); + /* Verify. */ + CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[1], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[2], message, &pubkey) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[3], message, &pubkey) == 1); + /* Test lower-S form, malleate, verify and fail, test again, malleate again */ + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[0])); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &signature[0]); + secp256k1_scalar_negate(&s, &s); + secp256k1_ecdsa_signature_save(&signature[5], &r, &s); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 0); + CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, &signature[5], &signature[5])); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + secp256k1_scalar_negate(&s, &s); + secp256k1_ecdsa_signature_save(&signature[5], &r, &s); + CHECK(!secp256k1_ecdsa_signature_normalize(ctx, NULL, &signature[5])); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[5], message, &pubkey) == 1); + CHECK(memcmp(&signature[5], &signature[0], 64) == 0); + + /* Serialize/parse DER and verify again */ + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + memset(&signature[0], 0, sizeof(signature[0])); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 1); + /* Serialize/destroy/parse DER and verify again. */ + siglen = 74; + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, sig, &siglen, &signature[0]) == 1); + sig[secp256k1_rand_int(siglen)] += 1 + secp256k1_rand_int(255); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &signature[0], sig, siglen) == 0 || + secp256k1_ecdsa_verify(ctx, &signature[0], message, &pubkey) == 0); +} + +void test_random_pubkeys(void) { + secp256k1_ge elem; + secp256k1_ge elem2; + unsigned char in[65]; + /* Generate some randomly sized pubkeys. */ + size_t len = secp256k1_rand_bits(2) == 0 ? 65 : 33; + if (secp256k1_rand_bits(2) == 0) { + len = secp256k1_rand_bits(6); + } + if (len == 65) { + in[0] = secp256k1_rand_bits(1) ? 4 : (secp256k1_rand_bits(1) ? 6 : 7); + } else { + in[0] = secp256k1_rand_bits(1) ? 2 : 3; + } + if (secp256k1_rand_bits(3) == 0) { + in[0] = secp256k1_rand_bits(8); + } + if (len > 1) { + secp256k1_rand256(&in[1]); + } + if (len > 33) { + secp256k1_rand256(&in[33]); + } + if (secp256k1_eckey_pubkey_parse(&elem, in, len)) { + unsigned char out[65]; + unsigned char firstb; + int res; + size_t size = len; + firstb = in[0]; + /* If the pubkey can be parsed, it should round-trip... */ + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, len == 33)); + CHECK(size == len); + CHECK(memcmp(&in[1], &out[1], len-1) == 0); + /* ... except for the type of hybrid inputs. */ + if ((in[0] != 6) && (in[0] != 7)) { + CHECK(in[0] == out[0]); + } + size = 65; + CHECK(secp256k1_eckey_pubkey_serialize(&elem, in, &size, 0)); + CHECK(size == 65); + CHECK(secp256k1_eckey_pubkey_parse(&elem2, in, size)); + ge_equals_ge(&elem,&elem2); + /* Check that the X9.62 hybrid type is checked. */ + in[0] = secp256k1_rand_bits(1) ? 6 : 7; + res = secp256k1_eckey_pubkey_parse(&elem2, in, size); + if (firstb == 2 || firstb == 3) { + if (in[0] == firstb + 4) { + CHECK(res); + } else { + CHECK(!res); + } + } + if (res) { + ge_equals_ge(&elem,&elem2); + CHECK(secp256k1_eckey_pubkey_serialize(&elem, out, &size, 0)); + CHECK(memcmp(&in[1], &out[1], 64) == 0); + } + } +} + +void run_random_pubkeys(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_random_pubkeys(); + } +} + +void run_ecdsa_end_to_end(void) { + int i; + for (i = 0; i < 64*count; i++) { + test_ecdsa_end_to_end(); + } +} + +int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) { + static const unsigned char zeroes[32] = {0}; +#ifdef ENABLE_OPENSSL_TESTS + static const unsigned char max_scalar[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40 + }; +#endif + + int ret = 0; + + secp256k1_ecdsa_signature sig_der; + unsigned char roundtrip_der[2048]; + unsigned char compact_der[64]; + size_t len_der = 2048; + int parsed_der = 0, valid_der = 0, roundtrips_der = 0; + + secp256k1_ecdsa_signature sig_der_lax; + unsigned char roundtrip_der_lax[2048]; + unsigned char compact_der_lax[64]; + size_t len_der_lax = 2048; + int parsed_der_lax = 0, valid_der_lax = 0, roundtrips_der_lax = 0; + +#ifdef ENABLE_OPENSSL_TESTS + ECDSA_SIG *sig_openssl; + const unsigned char *sigptr; + unsigned char roundtrip_openssl[2048]; + int len_openssl = 2048; + int parsed_openssl, valid_openssl = 0, roundtrips_openssl = 0; +#endif + + parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen); + if (parsed_der) { + ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0; + valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0); + } + if (valid_der) { + ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1; + roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0; + } + + parsed_der_lax = ecdsa_signature_parse_der_lax(ctx, &sig_der_lax, sig, siglen); + if (parsed_der_lax) { + ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der_lax, &sig_der_lax)) << 10; + valid_der_lax = (memcmp(compact_der_lax, zeroes, 32) != 0) && (memcmp(compact_der_lax + 32, zeroes, 32) != 0); + } + if (valid_der_lax) { + ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der_lax, &len_der_lax, &sig_der_lax)) << 11; + roundtrips_der_lax = (len_der_lax == siglen) && memcmp(roundtrip_der_lax, sig, siglen) == 0; + } + + if (certainly_der) { + ret |= (!parsed_der) << 2; + } + if (certainly_not_der) { + ret |= (parsed_der) << 17; + } + if (valid_der) { + ret |= (!roundtrips_der) << 3; + } + + if (valid_der) { + ret |= (!roundtrips_der_lax) << 12; + ret |= (len_der != len_der_lax) << 13; + ret |= (memcmp(roundtrip_der_lax, roundtrip_der, len_der) != 0) << 14; + } + ret |= (roundtrips_der != roundtrips_der_lax) << 15; + if (parsed_der) { + ret |= (!parsed_der_lax) << 16; + } + +#ifdef ENABLE_OPENSSL_TESTS + sig_openssl = ECDSA_SIG_new(); + sigptr = sig; + parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL); + if (parsed_openssl) { + valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256; + if (valid_openssl) { + unsigned char tmp[32] = {0}; + BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r)); + valid_openssl = memcmp(tmp, max_scalar, 32) < 0; + } + if (valid_openssl) { + unsigned char tmp[32] = {0}; + BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s)); + valid_openssl = memcmp(tmp, max_scalar, 32) < 0; + } + } + len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL); + if (len_openssl <= 2048) { + unsigned char *ptr = roundtrip_openssl; + CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl); + roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0); + } else { + len_openssl = 0; + } + ECDSA_SIG_free(sig_openssl); + + ret |= (parsed_der && !parsed_openssl) << 4; + ret |= (valid_der && !valid_openssl) << 5; + ret |= (roundtrips_openssl && !parsed_der) << 6; + ret |= (roundtrips_der != roundtrips_openssl) << 7; + if (roundtrips_openssl) { + ret |= (len_der != (size_t)len_openssl) << 8; + ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9; + } +#endif + return ret; +} + +static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) { + size_t i; + for (i = 0; i < ptrlen; i++) { + int shift = ptrlen - 1 - i; + if (shift >= 4) { + ptr[i] = 0; + } else { + ptr[i] = (val >> shift) & 0xFF; + } + } +} + +static void damage_array(unsigned char *sig, size_t *len) { + int pos; + int action = secp256k1_rand_bits(3); + if (action < 1 && *len > 3) { + /* Delete a byte. */ + pos = secp256k1_rand_int(*len); + memmove(sig + pos, sig + pos + 1, *len - pos - 1); + (*len)--; + return; + } else if (action < 2 && *len < 2048) { + /* Insert a byte. */ + pos = secp256k1_rand_int(1 + *len); + memmove(sig + pos + 1, sig + pos, *len - pos); + sig[pos] = secp256k1_rand_bits(8); + (*len)++; + return; + } else if (action < 4) { + /* Modify a byte. */ + sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255); + return; + } else { /* action < 8 */ + /* Modify a bit. */ + sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3); + return; + } +} + +static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) { + int der; + int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2]; + size_t tlen, elen, glen; + int indet; + int n; + + *len = 0; + der = secp256k1_rand_bits(2) == 0; + *certainly_der = der; + *certainly_not_der = 0; + indet = der ? 0 : secp256k1_rand_int(10) == 0; + + for (n = 0; n < 2; n++) { + /* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */ + nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0); + /* The length of the number in bytes (the first byte of which will always be nonzero) */ + nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8; + CHECK(nlen[n] <= 232); + /* The top bit of the number. */ + nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1)); + /* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */ + nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127)); + /* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */ + nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8); + if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) { + *certainly_not_der = 1; + } + CHECK(nlen[n] + nzlen[n] <= 300); + /* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */ + nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2); + if (!der) { + /* nlenlen[n] max 127 bytes */ + int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + nlenlen[n] += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427); + } + + /* The total length of the data to go, so far */ + tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1]; + CHECK(tlen <= 856); + + /* The length of the garbage inside the tuple. */ + elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8; + if (elen != 0) { + *certainly_not_der = 1; + } + tlen += elen; + CHECK(tlen <= 980); + + /* The length of the garbage after the end of the tuple. */ + glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8; + if (glen != 0) { + *certainly_not_der = 1; + } + CHECK(tlen + glen <= 990); + + /* Write the tuple header. */ + sig[(*len)++] = 0x30; + if (indet) { + /* Indeterminate length */ + sig[(*len)++] = 0x80; + *certainly_not_der = 1; + } else { + int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2); + if (!der) { + int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256; + tlenlen += add; + if (add != 0) { + *certainly_not_der = 1; + } + } + if (tlenlen == 0) { + /* Short length notation */ + sig[(*len)++] = tlen; + } else { + /* Long length notation */ + sig[(*len)++] = 128 + tlenlen; + assign_big_endian(sig + *len, tlenlen, tlen); + *len += tlenlen; + } + tlen += tlenlen; + } + tlen += 2; + CHECK(tlen + glen <= 1119); + + for (n = 0; n < 2; n++) { + /* Write the integer header. */ + sig[(*len)++] = 0x02; + if (nlenlen[n] == 0) { + /* Short length notation */ + sig[(*len)++] = nlen[n] + nzlen[n]; + } else { + /* Long length notation. */ + sig[(*len)++] = 128 + nlenlen[n]; + assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]); + *len += nlenlen[n]; + } + /* Write zero padding */ + while (nzlen[n] > 0) { + sig[(*len)++] = 0x00; + nzlen[n]--; + } + if (nlen[n] == 32 && !nlow[n]) { + /* Special extra 16 0xFF bytes in "high" 32-byte numbers */ + int i; + for (i = 0; i < 16; i++) { + sig[(*len)++] = 0xFF; + } + nlen[n] -= 16; + } + /* Write first byte of number */ + if (nlen[n] > 0) { + sig[(*len)++] = nhbyte[n]; + nlen[n]--; + } + /* Generate remaining random bytes of number */ + secp256k1_rand_bytes_test(sig + *len, nlen[n]); + *len += nlen[n]; + nlen[n] = 0; + } + + /* Generate random garbage inside tuple. */ + secp256k1_rand_bytes_test(sig + *len, elen); + *len += elen; + + /* Generate end-of-contents bytes. */ + if (indet) { + sig[(*len)++] = 0; + sig[(*len)++] = 0; + tlen += 2; + } + CHECK(tlen + glen <= 1121); + + /* Generate random garbage outside tuple. */ + secp256k1_rand_bytes_test(sig + *len, glen); + *len += glen; + tlen += glen; + CHECK(tlen <= 1121); + CHECK(tlen == *len); +} + +void run_ecdsa_der_parse(void) { + int i,j; + for (i = 0; i < 200 * count; i++) { + unsigned char buffer[2048]; + size_t buflen = 0; + int certainly_der = 0; + int certainly_not_der = 0; + random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der); + CHECK(buflen <= 2048); + for (j = 0; j < 16; j++) { + int ret = 0; + if (j > 0) { + damage_array(buffer, &buflen); + /* We don't know anything anymore about the DERness of the result */ + certainly_der = 0; + certainly_not_der = 0; + } + ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der); + if (ret != 0) { + size_t k; + fprintf(stderr, "Failure %x on ", ret); + for (k = 0; k < buflen; k++) { + fprintf(stderr, "%02x ", buffer[k]); + } + fprintf(stderr, "\n"); + } + CHECK(ret == 0); + } + } +} + +/* Tests several edge cases. */ +void test_ecdsa_edge_cases(void) { + int t; + secp256k1_ecdsa_signature sig; + + /* Test the case where ECDSA recomputes a point that is infinity. */ + { + secp256k1_gej keyj; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_negate(&ss, &ss); + secp256k1_scalar_inverse(&ss, &ss); + secp256k1_scalar_set_int(&sr, 1); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &keyj, &sr); + secp256k1_ge_set_gej(&key, &keyj); + msg = ss; + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with r of zero fails. */ + { + const unsigned char pubkey_mods_zero[33] = { + 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, + 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, + 0x41 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 0); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey_mods_zero, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with s of zero fails. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01 + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 0); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 1); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Verify signature with message 0 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02 + }; + const unsigned char pubkey2[33] = { + 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xfe, 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, + 0x3b, 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, + 0x43 + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_set_int(&msg, 0); + secp256k1_scalar_set_int(&sr, 2); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_set_int(&ss, 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + } + + /* Verify signature with message 1 passes. */ + { + const unsigned char pubkey[33] = { + 0x02, 0x14, 0x4e, 0x5a, 0x58, 0xef, 0x5b, 0x22, + 0x6f, 0xd2, 0xe2, 0x07, 0x6a, 0x77, 0xcf, 0x05, + 0xb4, 0x1d, 0xe7, 0x4a, 0x30, 0x98, 0x27, 0x8c, + 0x93, 0xe6, 0xe6, 0x3c, 0x0b, 0xc4, 0x73, 0x76, + 0x25 + }; + const unsigned char pubkey2[33] = { + 0x02, 0x8a, 0xd5, 0x37, 0xed, 0x73, 0xd9, 0x40, + 0x1d, 0xa0, 0x33, 0xd2, 0xdc, 0xf0, 0xaf, 0xae, + 0x34, 0xcf, 0x5f, 0x96, 0x4c, 0x73, 0x28, 0x0f, + 0x92, 0xc0, 0xf6, 0x9d, 0xd9, 0xb2, 0x09, 0x10, + 0x62 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xeb + }; + secp256k1_ge key; + secp256k1_ge key2; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + secp256k1_scalar_set_b32(&sr, csr, NULL); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_eckey_pubkey_parse(&key2, pubkey2, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 1); + secp256k1_scalar_set_int(&ss, 2); + secp256k1_scalar_inverse_var(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key2, &msg) == 0); + } + + /* Verify signature with message -1 passes. */ + { + const unsigned char pubkey[33] = { + 0x03, 0xaf, 0x97, 0xff, 0x7d, 0x3a, 0xf6, 0xa0, + 0x02, 0x94, 0xbd, 0x9f, 0x4b, 0x2e, 0xd7, 0x52, + 0x28, 0xdb, 0x49, 0x2a, 0x65, 0xcb, 0x1e, 0x27, + 0x57, 0x9c, 0xba, 0x74, 0x20, 0xd5, 0x1d, 0x20, + 0xf1 + }; + const unsigned char csr[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4, + 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xee + }; + secp256k1_ge key; + secp256k1_scalar msg; + secp256k1_scalar sr, ss; + secp256k1_scalar_set_int(&ss, 1); + secp256k1_scalar_set_int(&msg, 1); + secp256k1_scalar_negate(&msg, &msg); + secp256k1_scalar_set_b32(&sr, csr, NULL); + CHECK(secp256k1_eckey_pubkey_parse(&key, pubkey, 33)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + secp256k1_scalar_negate(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 1); + secp256k1_scalar_set_int(&ss, 3); + secp256k1_scalar_inverse_var(&ss, &ss); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sr, &ss, &key, &msg) == 0); + } + + /* Signature where s would be zero. */ + { + secp256k1_pubkey pubkey; + size_t siglen; + int32_t ecount; + unsigned char signature[72]; + static const unsigned char nonce[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + static const unsigned char nonce2[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x40 + }; + const unsigned char key[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + }; + unsigned char msg[32] = { + 0x86, 0x41, 0x99, 0x81, 0x06, 0x23, 0x44, 0x53, + 0xaa, 0x5f, 0x9d, 0x6a, 0x31, 0x78, 0xf4, 0xf7, + 0xb8, 0x12, 0xe0, 0x0b, 0x81, 0x7a, 0x77, 0x62, + 0x65, 0xdf, 0xdd, 0x31, 0xb9, 0x3e, 0x29, 0xa9, + }; + ecount = 0; + secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 0); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 0); + msg[31] = 0xaa; + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_ecdsa_sign(ctx, NULL, msg, key, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, NULL, key, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, NULL, precomputed_nonce_function, nonce2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, precomputed_nonce_function, nonce2) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, key) == 1); + CHECK(secp256k1_ecdsa_verify(ctx, NULL, msg, &pubkey) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, NULL, &pubkey) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, NULL) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_ec_pubkey_create(ctx, &pubkey, NULL) == 0); + CHECK(ecount == 7); + /* That pubkeyload fails via an ARGCHECK is a little odd but makes sense because pubkeys are an opaque data type. */ + CHECK(secp256k1_ecdsa_verify(ctx, &sig, msg, &pubkey) == 0); + CHECK(ecount == 8); + siglen = 72; + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, NULL, &siglen, &sig) == 0); + CHECK(ecount == 9); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, NULL, &sig) == 0); + CHECK(ecount == 10); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, NULL) == 0); + CHECK(ecount == 11); + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 1); + CHECK(ecount == 11); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, NULL, signature, siglen) == 0); + CHECK(ecount == 12); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, NULL, siglen) == 0); + CHECK(ecount == 13); + CHECK(secp256k1_ecdsa_signature_parse_der(ctx, &sig, signature, siglen) == 1); + CHECK(ecount == 13); + siglen = 10; + /* Too little room for a signature does not fail via ARGCHECK. */ + CHECK(secp256k1_ecdsa_signature_serialize_der(ctx, signature, &siglen, &sig) == 0); + CHECK(ecount == 13); + ecount = 0; + CHECK(secp256k1_ecdsa_signature_normalize(ctx, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, NULL, &sig) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_signature_serialize_compact(ctx, signature, &sig) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, NULL, signature) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, NULL) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 1); + CHECK(ecount == 5); + memset(signature, 255, 64); + CHECK(secp256k1_ecdsa_signature_parse_compact(ctx, &sig, signature) == 0); + CHECK(ecount == 5); + secp256k1_context_set_illegal_callback(ctx, NULL, NULL); + } + + /* Nonce function corner cases. */ + for (t = 0; t < 2; t++) { + static const unsigned char zero[32] = {0x00}; + int i; + unsigned char key[32]; + unsigned char msg[32]; + secp256k1_ecdsa_signature sig2; + secp256k1_scalar sr[512], ss; + const unsigned char *extra; + extra = t == 0 ? NULL : zero; + memset(msg, 0, 32); + msg[31] = 1; + /* High key results in signature failure. */ + memset(key, 0xFF, 32); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* Zero key results in signature failure. */ + memset(key, 0, 32); + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, NULL, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* Nonce function failure results in signature failure. */ + key[31] = 1; + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_fail, extra) == 0); + CHECK(is_empty_signature(&sig)); + /* The retry loop successfully makes its way to the first good value. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig, msg, key, nonce_function_test_retry, extra) == 1); + CHECK(!is_empty_signature(&sig)); + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, nonce_function_rfc6979, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function is deterministic. */ + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + CHECK(memcmp(&sig, &sig2, sizeof(sig)) == 0); + /* The default nonce function changes output with different messages. */ + for(i = 0; i < 256; i++) { + int j; + msg[0] = i; + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + msg[0] = 0; + msg[31] = 2; + /* The default nonce function changes output with different keys. */ + for(i = 256; i < 512; i++) { + int j; + key[0] = i - 256; + CHECK(secp256k1_ecdsa_sign(ctx, &sig2, msg, key, NULL, extra) == 1); + CHECK(!is_empty_signature(&sig2)); + secp256k1_ecdsa_signature_load(ctx, &sr[i], &ss, &sig2); + for (j = 0; j < i; j++) { + CHECK(!secp256k1_scalar_eq(&sr[i], &sr[j])); + } + } + key[0] = 0; + } + + { + /* Check that optional nonce arguments do not have equivalent effect. */ + const unsigned char zeros[32] = {0}; + unsigned char nonce[32]; + unsigned char nonce2[32]; + unsigned char nonce3[32]; + unsigned char nonce4[32]; + VG_UNDEF(nonce,32); + VG_UNDEF(nonce2,32); + VG_UNDEF(nonce3,32); + VG_UNDEF(nonce4,32); + CHECK(nonce_function_rfc6979(nonce, zeros, zeros, NULL, NULL, 0) == 1); + VG_CHECK(nonce,32); + CHECK(nonce_function_rfc6979(nonce2, zeros, zeros, zeros, NULL, 0) == 1); + VG_CHECK(nonce2,32); + CHECK(nonce_function_rfc6979(nonce3, zeros, zeros, NULL, (void *)zeros, 0) == 1); + VG_CHECK(nonce3,32); + CHECK(nonce_function_rfc6979(nonce4, zeros, zeros, zeros, (void *)zeros, 0) == 1); + VG_CHECK(nonce4,32); + CHECK(memcmp(nonce, nonce2, 32) != 0); + CHECK(memcmp(nonce, nonce3, 32) != 0); + CHECK(memcmp(nonce, nonce4, 32) != 0); + CHECK(memcmp(nonce2, nonce3, 32) != 0); + CHECK(memcmp(nonce2, nonce4, 32) != 0); + CHECK(memcmp(nonce3, nonce4, 32) != 0); + } + + + /* Privkey export where pubkey is the point at infinity. */ + { + unsigned char privkey[300]; + unsigned char seckey[32] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, + 0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b, + 0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x41, + }; + size_t outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 0)); + outlen = 300; + CHECK(!ec_privkey_export_der(ctx, privkey, &outlen, seckey, 1)); + } +} + +void run_ecdsa_edge_cases(void) { + test_ecdsa_edge_cases(); +} + +#ifdef ENABLE_OPENSSL_TESTS +EC_KEY *get_openssl_key(const unsigned char *key32) { + unsigned char privkey[300]; + size_t privkeylen; + const unsigned char* pbegin = privkey; + int compr = secp256k1_rand_bits(1); + EC_KEY *ec_key = EC_KEY_new_by_curve_name(NID_secp256k1); + CHECK(ec_privkey_export_der(ctx, privkey, &privkeylen, key32, compr)); + CHECK(d2i_ECPrivateKey(&ec_key, &pbegin, privkeylen)); + CHECK(EC_KEY_check_key(ec_key)); + return ec_key; +} + +void test_ecdsa_openssl(void) { + secp256k1_gej qj; + secp256k1_ge q; + secp256k1_scalar sigr, sigs; + secp256k1_scalar one; + secp256k1_scalar msg2; + secp256k1_scalar key, msg; + EC_KEY *ec_key; + unsigned int sigsize = 80; + size_t secp_sigsize = 80; + unsigned char message[32]; + unsigned char signature[80]; + unsigned char key32[32]; + secp256k1_rand256_test(message); + secp256k1_scalar_set_b32(&msg, message, NULL); + random_scalar_order_test(&key); + secp256k1_scalar_get_b32(key32, &key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &qj, &key); + secp256k1_ge_set_gej(&q, &qj); + ec_key = get_openssl_key(key32); + CHECK(ec_key != NULL); + CHECK(ECDSA_sign(0, message, sizeof(message), signature, &sigsize, ec_key)); + CHECK(secp256k1_ecdsa_sig_parse(&sigr, &sigs, signature, sigsize)); + CHECK(secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg)); + secp256k1_scalar_set_int(&one, 1); + secp256k1_scalar_add(&msg2, &msg, &one); + CHECK(!secp256k1_ecdsa_sig_verify(&ctx->ecmult_ctx, &sigr, &sigs, &q, &msg2)); + + random_sign(&sigr, &sigs, &key, &msg, NULL); + CHECK(secp256k1_ecdsa_sig_serialize(signature, &secp_sigsize, &sigr, &sigs)); + CHECK(ECDSA_verify(0, message, sizeof(message), signature, secp_sigsize, ec_key) == 1); + + EC_KEY_free(ec_key); +} + +void run_ecdsa_openssl(void) { + int i; + for (i = 0; i < 10*count; i++) { + test_ecdsa_openssl(); + } +} +#endif + +#ifdef ENABLE_MODULE_ECDH +# include "modules/ecdh/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_SCHNORR +# include "modules/schnorr/tests_impl.h" +#endif + +#ifdef ENABLE_MODULE_RECOVERY +# include "modules/recovery/tests_impl.h" +#endif + +int main(int argc, char **argv) { + unsigned char seed16[16] = {0}; + unsigned char run32[32] = {0}; + /* find iteration count */ + if (argc > 1) { + count = strtol(argv[1], NULL, 0); + } + + /* find random seed */ + if (argc > 2) { + int pos = 0; + const char* ch = argv[2]; + while (pos < 16 && ch[0] != 0 && ch[1] != 0) { + unsigned short sh; + if (sscanf(ch, "%2hx", &sh)) { + seed16[pos] = sh; + } else { + break; + } + ch += 2; + pos++; + } + } else { + FILE *frand = fopen("/dev/urandom", "r"); + if ((frand == NULL) || !fread(&seed16, sizeof(seed16), 1, frand)) { + uint64_t t = time(NULL) * (uint64_t)1337; + seed16[0] ^= t; + seed16[1] ^= t >> 8; + seed16[2] ^= t >> 16; + seed16[3] ^= t >> 24; + seed16[4] ^= t >> 32; + seed16[5] ^= t >> 40; + seed16[6] ^= t >> 48; + seed16[7] ^= t >> 56; + } + fclose(frand); + } + secp256k1_rand_seed(seed16); + + printf("test count = %i\n", count); + printf("random seed = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", seed16[0], seed16[1], seed16[2], seed16[3], seed16[4], seed16[5], seed16[6], seed16[7], seed16[8], seed16[9], seed16[10], seed16[11], seed16[12], seed16[13], seed16[14], seed16[15]); + + /* initialize */ + run_context_tests(); + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (secp256k1_rand_bits(1)) { + secp256k1_rand256(run32); + CHECK(secp256k1_context_randomize(ctx, secp256k1_rand_bits(1) ? run32 : NULL)); + } + + run_rand_bits(); + run_rand_int(); + + run_sha256_tests(); + run_hmac_sha256_tests(); + run_rfc6979_hmac_sha256_tests(); + +#ifndef USE_NUM_NONE + /* num tests */ + run_num_smalltests(); +#endif + + /* scalar tests */ + run_scalar_tests(); + + /* field tests */ + run_field_inv(); + run_field_inv_var(); + run_field_inv_all_var(); + run_field_misc(); + run_field_convert(); + run_sqr(); + run_sqrt(); + + /* group tests */ + run_ge(); + run_group_decompress(); + + /* ecmult tests */ + run_wnaf(); + run_point_times_order(); + run_ecmult_chain(); + run_ecmult_constants(); + run_ecmult_gen_blind(); + run_ecmult_const_tests(); + run_ec_combine(); + + /* endomorphism tests */ +#ifdef USE_ENDOMORPHISM + run_endomorphism_tests(); +#endif + + /* EC point parser test */ + run_ec_pubkey_parse_test(); + + /* EC key edge cases */ + run_eckey_edge_case_test(); + +#ifdef ENABLE_MODULE_ECDH + /* ecdh tests */ + run_ecdh_tests(); +#endif + + /* ecdsa tests */ + run_random_pubkeys(); + run_ecdsa_der_parse(); + run_ecdsa_sign_verify(); + run_ecdsa_end_to_end(); + run_ecdsa_edge_cases(); +#ifdef ENABLE_OPENSSL_TESTS + run_ecdsa_openssl(); +#endif + +#ifdef ENABLE_MODULE_SCHNORR + /* Schnorr tests */ + run_schnorr_tests(); +#endif + +#ifdef ENABLE_MODULE_RECOVERY + /* ECDSA pubkey recovery tests */ + run_recovery_tests(); +#endif + + secp256k1_rand256(run32); + printf("random run = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", run32[0], run32[1], run32[2], run32[3], run32[4], run32[5], run32[6], run32[7], run32[8], run32[9], run32[10], run32[11], run32[12], run32[13], run32[14], run32[15]); + + /* shutdown */ + secp256k1_context_destroy(ctx); + + printf("no problems found\n"); + return 0; +} diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c new file mode 100644 index 000000000..b040bb073 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/tests_exhaustive.c @@ -0,0 +1,470 @@ +/*********************************************************************** + * Copyright (c) 2016 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include + +#include + +#undef USE_ECMULT_STATIC_PRECOMPUTATION + +#ifndef EXHAUSTIVE_TEST_ORDER +/* see group_impl.h for allowable values */ +#define EXHAUSTIVE_TEST_ORDER 13 +#define EXHAUSTIVE_TEST_LAMBDA 9 /* cube root of 1 mod 13 */ +#endif + +#include "include/secp256k1.h" +#include "group.h" +#include "secp256k1.c" +#include "testrand_impl.h" + +#ifdef ENABLE_MODULE_RECOVERY +#include "src/modules/recovery/main_impl.h" +#include "include/secp256k1_recovery.h" +#endif + +/** stolen from tests.c */ +void ge_equals_ge(const secp256k1_ge *a, const secp256k1_ge *b) { + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + CHECK(secp256k1_fe_equal_var(&a->x, &b->x)); + CHECK(secp256k1_fe_equal_var(&a->y, &b->y)); +} + +void ge_equals_gej(const secp256k1_ge *a, const secp256k1_gej *b) { + secp256k1_fe z2s; + secp256k1_fe u1, u2, s1, s2; + CHECK(a->infinity == b->infinity); + if (a->infinity) { + return; + } + /* Check a.x * b.z^2 == b.x && a.y * b.z^3 == b.y, to avoid inverses. */ + secp256k1_fe_sqr(&z2s, &b->z); + secp256k1_fe_mul(&u1, &a->x, &z2s); + u2 = b->x; secp256k1_fe_normalize_weak(&u2); + secp256k1_fe_mul(&s1, &a->y, &z2s); secp256k1_fe_mul(&s1, &s1, &b->z); + s2 = b->y; secp256k1_fe_normalize_weak(&s2); + CHECK(secp256k1_fe_equal_var(&u1, &u2)); + CHECK(secp256k1_fe_equal_var(&s1, &s2)); +} + +void random_fe(secp256k1_fe *x) { + unsigned char bin[32]; + do { + secp256k1_rand256(bin); + if (secp256k1_fe_set_b32(x, bin)) { + return; + } + } while(1); +} +/** END stolen from tests.c */ + +int secp256k1_nonce_function_smallint(unsigned char *nonce32, const unsigned char *msg32, + const unsigned char *key32, const unsigned char *algo16, + void *data, unsigned int attempt) { + secp256k1_scalar s; + int *idata = data; + (void)msg32; + (void)key32; + (void)algo16; + /* Some nonces cannot be used because they'd cause s and/or r to be zero. + * The signing function has retry logic here that just re-calls the nonce + * function with an increased `attempt`. So if attempt > 0 this means we + * need to change the nonce to avoid an infinite loop. */ + if (attempt > 0) { + *idata = (*idata + 1) % EXHAUSTIVE_TEST_ORDER; + } + secp256k1_scalar_set_int(&s, *idata); + secp256k1_scalar_get_b32(nonce32, &s); + return 1; +} + +#ifdef USE_ENDOMORPHISM +void test_exhaustive_endomorphism(const secp256k1_ge *group, int order) { + int i; + for (i = 0; i < order; i++) { + secp256k1_ge res; + secp256k1_ge_mul_lambda(&res, &group[i]); + ge_equals_ge(&group[i * EXHAUSTIVE_TEST_LAMBDA % EXHAUSTIVE_TEST_ORDER], &res); + } +} +#endif + +void test_exhaustive_addition(const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j; + + /* Sanity-check (and check infinity functions) */ + CHECK(secp256k1_ge_is_infinity(&group[0])); + CHECK(secp256k1_gej_is_infinity(&groupj[0])); + for (i = 1; i < order; i++) { + CHECK(!secp256k1_ge_is_infinity(&group[i])); + CHECK(!secp256k1_gej_is_infinity(&groupj[i])); + } + + /* Check all addition formulae */ + for (j = 0; j < order; j++) { + secp256k1_fe fe_inv; + secp256k1_fe_inv(&fe_inv, &groupj[j].z); + for (i = 0; i < order; i++) { + secp256k1_ge zless_gej; + secp256k1_gej tmp; + /* add_var */ + secp256k1_gej_add_var(&tmp, &groupj[i], &groupj[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* add_ge */ + if (j > 0) { + secp256k1_gej_add_ge(&tmp, &groupj[i], &group[j]); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + /* add_ge_var */ + secp256k1_gej_add_ge_var(&tmp, &groupj[i], &group[j], NULL); + ge_equals_gej(&group[(i + j) % order], &tmp); + /* add_zinv_var */ + zless_gej.infinity = groupj[j].infinity; + zless_gej.x = groupj[j].x; + zless_gej.y = groupj[j].y; + secp256k1_gej_add_zinv_var(&tmp, &groupj[i], &zless_gej, &fe_inv); + ge_equals_gej(&group[(i + j) % order], &tmp); + } + } + + /* Check doubling */ + for (i = 0; i < order; i++) { + secp256k1_gej tmp; + if (i > 0) { + secp256k1_gej_double_nonzero(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + secp256k1_gej_double_var(&tmp, &groupj[i], NULL); + ge_equals_gej(&group[(2 * i) % order], &tmp); + } + + /* Check negation */ + for (i = 1; i < order; i++) { + secp256k1_ge tmp; + secp256k1_gej tmpj; + secp256k1_ge_neg(&tmp, &group[i]); + ge_equals_ge(&group[order - i], &tmp); + secp256k1_gej_neg(&tmpj, &groupj[i]); + ge_equals_gej(&group[order - i], &tmpj); + } +} + +void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *group, const secp256k1_gej *groupj, int order) { + int i, j, r_log; + for (r_log = 1; r_log < order; r_log++) { + for (j = 0; j < order; j++) { + for (i = 0; i < order; i++) { + secp256k1_gej tmp; + secp256k1_scalar na, ng; + secp256k1_scalar_set_int(&na, i); + secp256k1_scalar_set_int(&ng, j); + + secp256k1_ecmult(&ctx->ecmult_ctx, &tmp, &groupj[r_log], &na, &ng); + ge_equals_gej(&group[(i * r_log + j) % order], &tmp); + + if (i > 0) { + secp256k1_ecmult_const(&tmp, &group[i], &ng); + ge_equals_gej(&group[(i * j) % order], &tmp); + } + } + } + } +} + +void r_from_k(secp256k1_scalar *r, const secp256k1_ge *group, int k) { + secp256k1_fe x; + unsigned char x_bin[32]; + k %= EXHAUSTIVE_TEST_ORDER; + x = group[k].x; + secp256k1_fe_normalize(&x); + secp256k1_fe_get_b32(x_bin, &x); + secp256k1_scalar_set_b32(r, x_bin, NULL); +} + +void test_exhaustive_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int s, r, msg, key; + for (s = 1; s < order; s++) { + for (r = 1; r < order; r++) { + for (msg = 1; msg < order; msg++) { + for (key = 1; key < order; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int k, should_verify; + unsigned char msg32[32]; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < order; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* Verify by calling verify */ + secp256k1_ecdsa_signature_save(&sig, &r_s, &s_s); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + secp256k1_scalar_get_b32(msg32, &msg_s); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} + +void test_exhaustive_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k; + + /* Loop */ + for (i = 1; i < order; i++) { /* message */ + for (j = 1; j < order; j++) { /* key */ + for (k = 1; k < order; k++) { /* nonce */ + const int starting_k = k; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign(ctx, &sig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } + + /* We would like to verify zero-knowledge here by counting how often every + * possible (s, r) tuple appears, but because the group order is larger + * than the field order, when coercing the x-values to scalar values, some + * appear more often than others, so we are actually not zero-knowledge. + * (This effect also appears in the real code, but the difference is on the + * order of 1/2^128th the field order, so the deviation is not useful to a + * computationally bounded attacker.) + */ +} + +#ifdef ENABLE_MODULE_RECOVERY +void test_exhaustive_recovery_sign(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + int i, j, k; + + /* Loop */ + for (i = 1; i < order; i++) { /* message */ + for (j = 1; j < order; j++) { /* key */ + for (k = 1; k < order; k++) { /* nonce */ + const int starting_k = k; + secp256k1_fe r_dot_y_normalized; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_scalar sk, msg, r, s, expected_r; + unsigned char sk32[32], msg32[32]; + int expected_recid; + int recid; + secp256k1_scalar_set_int(&msg, i); + secp256k1_scalar_set_int(&sk, j); + secp256k1_scalar_get_b32(sk32, &sk); + secp256k1_scalar_get_b32(msg32, &msg); + + secp256k1_ecdsa_sign_recoverable(ctx, &rsig, msg32, sk32, secp256k1_nonce_function_smallint, &k); + + /* Check directly */ + secp256k1_ecdsa_recoverable_signature_load(ctx, &r, &s, &recid, &rsig); + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + /* In computing the recid, there is an overflow condition that is disabled in + * scalar_low_impl.h `secp256k1_scalar_set_b32` because almost every r.y value + * will exceed the group order, and our signing code always holds out for r + * values that don't overflow, so with a proper overflow check the tests would + * loop indefinitely. */ + r_dot_y_normalized = group[k].y; + secp256k1_fe_normalize(&r_dot_y_normalized); + /* Also the recovery id is flipped depending if we hit the low-s branch */ + if ((k * s) % order == (i + r * j) % order) { + expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 1 : 0; + } else { + expected_recid = secp256k1_fe_is_odd(&r_dot_y_normalized) ? 0 : 1; + } + CHECK(recid == expected_recid); + + /* Convert to a standard sig then check */ + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + secp256k1_ecdsa_signature_load(ctx, &r, &s, &sig); + /* Note that we compute expected_r *after* signing -- this is important + * because our nonce-computing function function might change k during + * signing. */ + r_from_k(&expected_r, group, k); + CHECK(r == expected_r); + CHECK((k * s) % order == (i + r * j) % order || + (k * (EXHAUSTIVE_TEST_ORDER - s)) % order == (i + r * j) % order); + + /* Overflow means we've tried every possible nonce */ + if (k < starting_k) { + break; + } + } + } + } +} + +void test_exhaustive_recovery_verify(const secp256k1_context *ctx, const secp256k1_ge *group, int order) { + /* This is essentially a copy of test_exhaustive_verify, with recovery added */ + int s, r, msg, key; + for (s = 1; s < order; s++) { + for (r = 1; r < order; r++) { + for (msg = 1; msg < order; msg++) { + for (key = 1; key < order; key++) { + secp256k1_ge nonconst_ge; + secp256k1_ecdsa_recoverable_signature rsig; + secp256k1_ecdsa_signature sig; + secp256k1_pubkey pk; + secp256k1_scalar sk_s, msg_s, r_s, s_s; + secp256k1_scalar s_times_k_s, msg_plus_r_times_sk_s; + int recid = 0; + int k, should_verify; + unsigned char msg32[32]; + + secp256k1_scalar_set_int(&s_s, s); + secp256k1_scalar_set_int(&r_s, r); + secp256k1_scalar_set_int(&msg_s, msg); + secp256k1_scalar_set_int(&sk_s, key); + secp256k1_scalar_get_b32(msg32, &msg_s); + + /* Verify by hand */ + /* Run through every k value that gives us this r and check that *one* works. + * Note there could be none, there could be multiple, ECDSA is weird. */ + should_verify = 0; + for (k = 0; k < order; k++) { + secp256k1_scalar check_x_s; + r_from_k(&check_x_s, group, k); + if (r_s == check_x_s) { + secp256k1_scalar_set_int(&s_times_k_s, k); + secp256k1_scalar_mul(&s_times_k_s, &s_times_k_s, &s_s); + secp256k1_scalar_mul(&msg_plus_r_times_sk_s, &r_s, &sk_s); + secp256k1_scalar_add(&msg_plus_r_times_sk_s, &msg_plus_r_times_sk_s, &msg_s); + should_verify |= secp256k1_scalar_eq(&s_times_k_s, &msg_plus_r_times_sk_s); + } + } + /* nb we have a "high s" rule */ + should_verify &= !secp256k1_scalar_is_high(&s_s); + + /* We would like to try recovering the pubkey and checking that it matches, + * but pubkey recovery is impossible in the exhaustive tests (the reason + * being that there are 12 nonzero r values, 12 nonzero points, and no + * overlap between the sets, so there are no valid signatures). */ + + /* Verify by converting to a standard signature and calling verify */ + secp256k1_ecdsa_recoverable_signature_save(&rsig, &r_s, &s_s, recid); + secp256k1_ecdsa_recoverable_signature_convert(ctx, &sig, &rsig); + memcpy(&nonconst_ge, &group[sk_s], sizeof(nonconst_ge)); + secp256k1_pubkey_save(&pk, &nonconst_ge); + CHECK(should_verify == + secp256k1_ecdsa_verify(ctx, &sig, msg32, &pk)); + } + } + } + } +} +#endif + +int main(void) { + int i; + secp256k1_gej groupj[EXHAUSTIVE_TEST_ORDER]; + secp256k1_ge group[EXHAUSTIVE_TEST_ORDER]; + + /* Build context */ + secp256k1_context *ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + + /* TODO set z = 1, then do num_tests runs with random z values */ + + /* Generate the entire group */ + secp256k1_gej_set_infinity(&groupj[0]); + secp256k1_ge_set_gej(&group[0], &groupj[0]); + for (i = 1; i < EXHAUSTIVE_TEST_ORDER; i++) { + /* Set a different random z-value for each Jacobian point */ + secp256k1_fe z; + random_fe(&z); + + secp256k1_gej_add_ge(&groupj[i], &groupj[i - 1], &secp256k1_ge_const_g); + secp256k1_ge_set_gej(&group[i], &groupj[i]); + secp256k1_gej_rescale(&groupj[i], &z); + + /* Verify against ecmult_gen */ + { + secp256k1_scalar scalar_i; + secp256k1_gej generatedj; + secp256k1_ge generated; + + secp256k1_scalar_set_int(&scalar_i, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &generatedj, &scalar_i); + secp256k1_ge_set_gej(&generated, &generatedj); + + CHECK(group[i].infinity == 0); + CHECK(generated.infinity == 0); + CHECK(secp256k1_fe_equal_var(&generated.x, &group[i].x)); + CHECK(secp256k1_fe_equal_var(&generated.y, &group[i].y)); + } + } + + /* Run the tests */ +#ifdef USE_ENDOMORPHISM + test_exhaustive_endomorphism(group, EXHAUSTIVE_TEST_ORDER); +#endif + test_exhaustive_addition(group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_ecmult(ctx, group, groupj, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); + +#ifdef ENABLE_MODULE_RECOVERY + test_exhaustive_recovery_sign(ctx, group, EXHAUSTIVE_TEST_ORDER); + test_exhaustive_recovery_verify(ctx, group, EXHAUSTIVE_TEST_ORDER); +#endif + + secp256k1_context_destroy(ctx); + return 0; +} + diff --git a/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/util.h b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/util.h new file mode 100644 index 000000000..4092a86c9 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/libsecp256k1/src/util.h @@ -0,0 +1,113 @@ +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_UTIL_H_ +#define _SECP256K1_UTIL_H_ + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#include +#include +#include + +typedef struct { + void (*fn)(const char *text, void* data); + const void* data; +} secp256k1_callback; + +static SECP256K1_INLINE void secp256k1_callback_call(const secp256k1_callback * const cb, const char * const text) { + cb->fn(text, (void*)cb->data); +} + +#ifdef DETERMINISTIC +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s\n", msg); \ + abort(); \ +} while(0); +#else +#define TEST_FAILURE(msg) do { \ + fprintf(stderr, "%s:%d: %s\n", __FILE__, __LINE__, msg); \ + abort(); \ +} while(0) +#endif + +#ifdef HAVE_BUILTIN_EXPECT +#define EXPECT(x,c) __builtin_expect((x),(c)) +#else +#define EXPECT(x,c) (x) +#endif + +#ifdef DETERMINISTIC +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed"); \ + } \ +} while(0) +#else +#define CHECK(cond) do { \ + if (EXPECT(!(cond), 0)) { \ + TEST_FAILURE("test condition failed: " #cond); \ + } \ +} while(0) +#endif + +/* Like assert(), but when VERIFY is defined, and side-effect safe. */ +#if defined(COVERAGE) +#define VERIFY_CHECK(check) +#define VERIFY_SETUP(stmt) +#elif defined(VERIFY) +#define VERIFY_CHECK CHECK +#define VERIFY_SETUP(stmt) do { stmt; } while(0) +#else +#define VERIFY_CHECK(cond) do { (void)(cond); } while(0) +#define VERIFY_SETUP(stmt) +#endif + +static SECP256K1_INLINE void *checked_malloc(const secp256k1_callback* cb, size_t size) { + void *ret = malloc(size); + if (ret == NULL) { + secp256k1_callback_call(cb, "Out of memory"); + } + return ret; +} + +/* Macro for restrict, when available and not in a VERIFY build. */ +#if defined(SECP256K1_BUILD) && defined(VERIFY) +# define SECP256K1_RESTRICT +#else +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(3,0) +# define SECP256K1_RESTRICT __restrict__ +# elif (defined(_MSC_VER) && _MSC_VER >= 1400) +# define SECP256K1_RESTRICT __restrict +# else +# define SECP256K1_RESTRICT +# endif +# else +# define SECP256K1_RESTRICT restrict +# endif +#endif + +#if defined(_WIN32) +# define I64FORMAT "I64d" +# define I64uFORMAT "I64u" +#else +# define I64FORMAT "lld" +# define I64uFORMAT "llu" +#endif + +#if defined(HAVE___INT128) +# if defined(__GNUC__) +# define SECP256K1_GNUC_EXT __extension__ +# else +# define SECP256K1_GNUC_EXT +# endif +SECP256K1_GNUC_EXT typedef unsigned __int128 uint128_t; +#endif + +#endif diff --git a/crypto/secp256k1/internal/secp256k1/panic_cb.go b/crypto/secp256k1/internal/secp256k1/panic_cb.go new file mode 100644 index 000000000..6d59a1d24 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/panic_cb.go @@ -0,0 +1,21 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +package secp256k1 + +import "C" +import "unsafe" + +// Callbacks for converting libsecp256k1 internal faults into +// recoverable Go panics. + +//export secp256k1GoPanicIllegal +func secp256k1GoPanicIllegal(msg *C.char, data unsafe.Pointer) { + panic("illegal argument: " + C.GoString(msg)) +} + +//export secp256k1GoPanicError +func secp256k1GoPanicError(msg *C.char, data unsafe.Pointer) { + panic("internal error: " + C.GoString(msg)) +} diff --git a/crypto/secp256k1/internal/secp256k1/secp256.go b/crypto/secp256k1/internal/secp256k1/secp256.go new file mode 100644 index 000000000..35d0eef34 --- /dev/null +++ b/crypto/secp256k1/internal/secp256k1/secp256.go @@ -0,0 +1,167 @@ +// Copyright 2015 Jeffrey Wilcke, Felix Lange, Gustav Simonsson. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be found in +// the LICENSE file. + +// Package secp256k1 wraps the bitcoin secp256k1 C library. +package secp256k1 + +/* +#cgo CFLAGS: -I./libsecp256k1 +#cgo CFLAGS: -I./libsecp256k1/src/ +#define USE_NUM_NONE +#define USE_FIELD_10X26 +#define USE_FIELD_INV_BUILTIN +#define USE_SCALAR_8X32 +#define USE_SCALAR_INV_BUILTIN +#define NDEBUG +#include "./libsecp256k1/src/secp256k1.c" +#include "./libsecp256k1/src/modules/recovery/main_impl.h" +#include "ext.h" + +typedef void (*callbackFunc) (const char* msg, void* data); +extern void secp256k1GoPanicIllegal(const char* msg, void* data); +extern void secp256k1GoPanicError(const char* msg, void* data); +*/ +import "C" + +import ( + "errors" + "math/big" + "unsafe" +) + +var context *C.secp256k1_context + +func init() { + // around 20 ms on a modern CPU. + context = C.secp256k1_context_create_sign_verify() + C.secp256k1_context_set_illegal_callback(context, C.callbackFunc(C.secp256k1GoPanicIllegal), nil) + C.secp256k1_context_set_error_callback(context, C.callbackFunc(C.secp256k1GoPanicError), nil) +} + +var ( + ErrInvalidMsgLen = errors.New("invalid message length, need 32 bytes") + ErrInvalidSignatureLen = errors.New("invalid signature length") + ErrInvalidRecoveryID = errors.New("invalid signature recovery id") + ErrInvalidKey = errors.New("invalid private key") + ErrInvalidPubkey = errors.New("invalid public key") + ErrSignFailed = errors.New("signing failed") + ErrRecoverFailed = errors.New("recovery failed") +) + +// Sign creates a recoverable ECDSA signature. +// The produced signature is in the 65-byte [R || S || V] format where V is 0 or 1. +// +// The caller is responsible for ensuring that msg cannot be chosen +// directly by an attacker. It is usually preferable to use a cryptographic +// hash function on any input before handing it to this function. +func Sign(msg []byte, seckey []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen + } + if len(seckey) != 32 { + return nil, ErrInvalidKey + } + seckeydata := (*C.uchar)(unsafe.Pointer(&seckey[0])) + if C.secp256k1_ec_seckey_verify(context, seckeydata) != 1 { + return nil, ErrInvalidKey + } + + var ( + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + noncefunc = C.secp256k1_nonce_function_rfc6979 + sigstruct C.secp256k1_ecdsa_recoverable_signature + ) + if C.secp256k1_ecdsa_sign_recoverable(context, &sigstruct, msgdata, seckeydata, noncefunc, nil) == 0 { + return nil, ErrSignFailed + } + + var ( + sig = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + recid C.int + ) + C.secp256k1_ecdsa_recoverable_signature_serialize_compact(context, sigdata, &recid, &sigstruct) + sig[64] = byte(recid) // add back recid to get 65 bytes sig + return sig, nil +} + +// RecoverPubkey returns the public key of the signer. +// msg must be the 32-byte hash of the message to be signed. +// sig must be a 65-byte compact ECDSA signature containing the +// recovery id as the last element. +func RecoverPubkey(msg []byte, sig []byte) ([]byte, error) { + if len(msg) != 32 { + return nil, ErrInvalidMsgLen + } + if err := checkSignature(sig); err != nil { + return nil, err + } + + var ( + pubkey = make([]byte, 65) + sigdata = (*C.uchar)(unsafe.Pointer(&sig[0])) + msgdata = (*C.uchar)(unsafe.Pointer(&msg[0])) + ) + if C.secp256k1_ext_ecdsa_recover(context, (*C.uchar)(unsafe.Pointer(&pubkey[0])), sigdata, msgdata) == 0 { + return nil, ErrRecoverFailed + } + return pubkey, nil +} + +// VerifySignature checks that the given pubkey created signature over message. +// The signature should be in [R || S] format. +func VerifySignature(pubkey, msg, signature []byte) bool { + if len(msg) != 32 || len(signature) != 64 || len(pubkey) == 0 { + return false + } + sigdata := (*C.uchar)(unsafe.Pointer(&signature[0])) + msgdata := (*C.uchar)(unsafe.Pointer(&msg[0])) + keydata := (*C.uchar)(unsafe.Pointer(&pubkey[0])) + return C.secp256k1_ext_ecdsa_verify(context, sigdata, msgdata, keydata, C.size_t(len(pubkey))) != 0 +} + +// DecompressPubkey parses a public key in the 33-byte compressed format. +// It returns non-nil coordinates if the public key is valid. +func DecompressPubkey(pubkey []byte) (x, y *big.Int) { + if len(pubkey) != 33 { + return nil, nil + } + var ( + pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0])) + pubkeylen = C.size_t(len(pubkey)) + out = make([]byte, 65) + outdata = (*C.uchar)(unsafe.Pointer(&out[0])) + outlen = C.size_t(len(out)) + ) + if C.secp256k1_ext_reencode_pubkey(context, outdata, outlen, pubkeydata, pubkeylen) == 0 { + return nil, nil + } + return new(big.Int).SetBytes(out[1:33]), new(big.Int).SetBytes(out[33:]) +} + +// CompressPubkey encodes a public key to 33-byte compressed format. +func CompressPubkey(x, y *big.Int) []byte { + var ( + pubkey = S256().Marshal(x, y) + pubkeydata = (*C.uchar)(unsafe.Pointer(&pubkey[0])) + pubkeylen = C.size_t(len(pubkey)) + out = make([]byte, 33) + outdata = (*C.uchar)(unsafe.Pointer(&out[0])) + outlen = C.size_t(len(out)) + ) + if C.secp256k1_ext_reencode_pubkey(context, outdata, outlen, pubkeydata, pubkeylen) == 0 { + panic("libsecp256k1 error") + } + return out +} + +func checkSignature(sig []byte) error { + if len(sig) != 65 { + return ErrInvalidSignatureLen + } + if sig[64] >= 4 { + return ErrInvalidRecoveryID + } + return nil +} diff --git a/crypto/secp256k1/secp256k1_cgo.go b/crypto/secp256k1/secp256k1_cgo.go index 3e5b1ddd2..3d4a553ac 100644 --- a/crypto/secp256k1/secp256k1_cgo.go +++ b/crypto/secp256k1/secp256k1_cgo.go @@ -3,9 +3,8 @@ package secp256k1 import ( - "github.com/ethereum/go-ethereum/crypto/secp256k1" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/secp256k1/internal/secp256k1" ) // Sign creates an ECDSA signature on curve Secp256k1, using SHA256 on the msg. From 411bc5e49fcc3dda866ae799ba2ceda0084f80d4 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Wed, 6 Mar 2019 09:54:49 +0100 Subject: [PATCH 085/259] types: followup after validator set changes (#3301) * fix failure in TestProposerFrequency * Add test to check priority order after updates * Changed applyRemovals() and removed Remove() Changed applyRemovals() similar to applyUpdates() Removed function Remove() Updated comments * review comments * simplify applyRemovals and add more comments * small correction in comment * Fix check in test * Fix priority check for centering, address review comments * fix assert for priority centering * review comments * review comments * cleanup and review comments added upper limit check for validator voting power moved check for empty validator set earlier moved panic on potential negative set length in verifyRemovals added more tests * review comments --- types/validator_set.go | 227 ++++++++++++----------- types/validator_set_test.go | 360 ++++++++++++++++++++++++++++++------ 2 files changed, 419 insertions(+), 168 deletions(-) diff --git a/types/validator_set.go b/types/validator_set.go index c70f33962..3d31cf7d0 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -2,6 +2,7 @@ package types import ( "bytes" + "errors" "fmt" "math" "math/big" @@ -93,7 +94,7 @@ func (vals *ValidatorSet) IncrementProposerPriority(times int) { vals.shiftByAvgProposerPriority() var proposer *Validator - // call IncrementProposerPriority(1) times times: + // Call IncrementProposerPriority(1) times times. for i := 0; i < times; i++ { proposer = vals.incrementProposerPriority() } @@ -117,9 +118,9 @@ func (vals *ValidatorSet) RescalePriorities(diffMax int64) { // NOTE: This may make debugging priority issues easier as well. diff := computeMaxMinPriorityDiff(vals) ratio := (diff + diffMax - 1) / diffMax - if ratio > 1 { + if diff > diffMax { for _, val := range vals.Validators { - val.ProposerPriority /= ratio + val.ProposerPriority = val.ProposerPriority / ratio } } } @@ -130,15 +131,15 @@ func (vals *ValidatorSet) incrementProposerPriority() *Validator { newPrio := safeAddClip(val.ProposerPriority, val.VotingPower) val.ProposerPriority = newPrio } - // Decrement the validator with most ProposerPriority: + // Decrement the validator with most ProposerPriority. mostest := vals.getValWithMostPriority() - // mind underflow + // Mind the underflow. mostest.ProposerPriority = safeSubClip(mostest.ProposerPriority, vals.TotalVotingPower()) return mostest } -// should not be called on an empty validator set +// Should not be called on an empty validator set. func (vals *ValidatorSet) computeAvgProposerPriority() int64 { n := int64(len(vals.Validators)) sum := big.NewInt(0) @@ -150,11 +151,11 @@ func (vals *ValidatorSet) computeAvgProposerPriority() int64 { return avg.Int64() } - // this should never happen: each val.ProposerPriority is in bounds of int64 + // This should never happen: each val.ProposerPriority is in bounds of int64. panic(fmt.Sprintf("Cannot represent avg ProposerPriority as an int64 %v", avg)) } -// compute the difference between the max and min ProposerPriority of that set +// Compute the difference between the max and min ProposerPriority of that set. func computeMaxMinPriorityDiff(vals *ValidatorSet) int64 { if vals.IsNilOrEmpty() { panic("empty validator set") @@ -195,7 +196,7 @@ func (vals *ValidatorSet) shiftByAvgProposerPriority() { } } -// Makes a copy of the validator list +// Makes a copy of the validator list. func validatorListCopy(valsList []*Validator) []*Validator { if valsList == nil { return nil @@ -207,7 +208,7 @@ func validatorListCopy(valsList []*Validator) []*Validator { return valsCopy } -// Copy each validator into a new ValidatorSet +// Copy each validator into a new ValidatorSet. func (vals *ValidatorSet) Copy() *ValidatorSet { return &ValidatorSet{ Validators: validatorListCopy(vals.Validators), @@ -253,21 +254,29 @@ func (vals *ValidatorSet) Size() int { return len(vals.Validators) } -// TotalVotingPower returns the sum of the voting powers of all validators. -func (vals *ValidatorSet) TotalVotingPower() int64 { - if vals.totalVotingPower == 0 { - sum := int64(0) - for _, val := range vals.Validators { - // mind overflow - sum = safeAddClip(sum, val.VotingPower) - } +// Force recalculation of the set's total voting power. +func (vals *ValidatorSet) updateTotalVotingPower() { + + sum := int64(0) + for _, val := range vals.Validators { + // mind overflow + sum = safeAddClip(sum, val.VotingPower) if sum > MaxTotalVotingPower { panic(fmt.Sprintf( "Total voting power should be guarded to not exceed %v; got: %v", MaxTotalVotingPower, sum)) } - vals.totalVotingPower = sum + } + + vals.totalVotingPower = sum +} + +// TotalVotingPower returns the sum of the voting powers of all validators. +// It recomputes the total voting power if required. +func (vals *ValidatorSet) TotalVotingPower() int64 { + if vals.totalVotingPower == 0 { + vals.updateTotalVotingPower() } return vals.totalVotingPower } @@ -307,27 +316,6 @@ func (vals *ValidatorSet) Hash() []byte { return merkle.SimpleHashFromByteSlices(bzs) } -// Remove deletes the validator with address. It returns the validator removed -// and true. If returns nil and false if validator is not present in the set. -func (vals *ValidatorSet) Remove(address []byte) (val *Validator, removed bool) { - idx := sort.Search(len(vals.Validators), func(i int) bool { - return bytes.Compare(address, vals.Validators[i].Address) <= 0 - }) - if idx >= len(vals.Validators) || !bytes.Equal(vals.Validators[idx].Address, address) { - return nil, false - } - removedVal := vals.Validators[idx] - newValidators := vals.Validators[:idx] - if idx+1 < len(vals.Validators) { - newValidators = append(newValidators, vals.Validators[idx+1:]...) - } - vals.Validators = newValidators - // Invalidate cache - vals.Proposer = nil - vals.totalVotingPower = 0 - return removedVal, true -} - // Iterate will run the given function over the set. func (vals *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) { for i, val := range vals.Validators { @@ -338,15 +326,15 @@ func (vals *ValidatorSet) Iterate(fn func(index int, val *Validator) bool) { } } -// Checks changes against duplicates, splits the changes in updates and removals, sorts them by address +// Checks changes against duplicates, splits the changes in updates and removals, sorts them by address. // // Returns: // updates, removals - the sorted lists of updates and removals // err - non-nil if duplicate entries or entries with negative voting power are seen // -// No changes are made to 'origChanges' +// No changes are made to 'origChanges'. func processChanges(origChanges []*Validator) (updates, removals []*Validator, err error) { - // Make a deep copy of the changes and sort by address + // Make a deep copy of the changes and sort by address. changes := validatorListCopy(origChanges) sort.Sort(ValidatorsByAddress(changes)) @@ -354,14 +342,19 @@ func processChanges(origChanges []*Validator) (updates, removals []*Validator, e updates = make([]*Validator, 0, len(changes)) var prevAddr Address - // Scan changes by address and append valid validators to updates or removals lists + // Scan changes by address and append valid validators to updates or removals lists. for _, valUpdate := range changes { if bytes.Equal(valUpdate.Address, prevAddr) { err = fmt.Errorf("duplicate entry %v in %v", valUpdate, changes) return nil, nil, err } if valUpdate.VotingPower < 0 { - err = fmt.Errorf("voting power can't be negative %v", valUpdate) + err = fmt.Errorf("voting power can't be negative: %v", valUpdate) + return nil, nil, err + } + if valUpdate.VotingPower > MaxTotalVotingPower { + err = fmt.Errorf("to prevent clipping/ overflow, voting power can't be higher than %v: %v ", + MaxTotalVotingPower, valUpdate) return nil, nil, err } if valUpdate.VotingPower == 0 { @@ -376,59 +369,49 @@ func processChanges(origChanges []*Validator) (updates, removals []*Validator, e // Verifies a list of updates against a validator set, making sure the allowed // total voting power would not be exceeded if these updates would be applied to the set. -// It also computes the total voting power of the set that would result after the updates but -// before the removals. // // Returns: // updatedTotalVotingPower - the new total voting power if these updates would be applied +// numNewValidators - number of new validators // err - non-nil if the maximum allowed total voting power would be exceeded // -// 'updates' should be a list of proper validator changes, i.e. they have been scanned +// 'updates' should be a list of proper validator changes, i.e. they have been verified // by processChanges for duplicates and invalid values. // No changes are made to the validator set 'vals'. -func verifyUpdates(updates []*Validator, vals *ValidatorSet) (updatedTotalVotingPower int64, err error) { +func verifyUpdates(updates []*Validator, vals *ValidatorSet) (updatedTotalVotingPower int64, numNewValidators int, err error) { - // Scan the updates, compute new total voting power, check for overflow updatedTotalVotingPower = vals.TotalVotingPower() for _, valUpdate := range updates { address := valUpdate.Address _, val := vals.GetByAddress(address) if val == nil { - // new validator, add its voting power the the total + // New validator, add its voting power the the total. updatedTotalVotingPower += valUpdate.VotingPower + numNewValidators++ } else { - // updated validator, add the difference in power to the total + // Updated validator, add the difference in power to the total. updatedTotalVotingPower += valUpdate.VotingPower - val.VotingPower } - - if updatedTotalVotingPower < 0 { - err = fmt.Errorf( - "failed to add/update validator with negative voting power %v", - valUpdate) - return 0, err - } overflow := updatedTotalVotingPower > MaxTotalVotingPower if overflow { err = fmt.Errorf( "failed to add/update validator %v, total voting power would exceed the max allowed %v", valUpdate, MaxTotalVotingPower) - return 0, err + return 0, 0, err } } - return updatedTotalVotingPower, nil + return updatedTotalVotingPower, numNewValidators, nil } -// Computes the proposer priority for the validators not present in the set based on 'updatedTotalVotingPower' +// Computes the proposer priority for the validators not present in the set based on 'updatedTotalVotingPower'. // Leaves unchanged the priorities of validators that are changed. // // 'updates' parameter must be a list of unique validators to be added or updated. // No changes are made to the validator set 'vals'. -func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotalVotingPower int64) int { +func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotalVotingPower int64) { - numNew := 0 - // Scan and update the proposerPriority for newly added and updated validators for _, valUpdate := range updates { address := valUpdate.Address _, val := vals.GetByAddress(address) @@ -442,13 +425,11 @@ func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotal // // Compute ProposerPriority = -1.125*totalVotingPower == -(updatedVotingPower + (updatedVotingPower >> 3)). valUpdate.ProposerPriority = -(updatedTotalVotingPower + (updatedTotalVotingPower >> 3)) - numNew++ } else { valUpdate.ProposerPriority = val.ProposerPriority } } - return numNew } // Merges the vals' validator list with the updates list. @@ -457,20 +438,19 @@ func computeNewPriorities(updates []*Validator, vals *ValidatorSet, updatedTotal // must have been validated with verifyUpdates() and priorities computed with computeNewPriorities(). func (vals *ValidatorSet) applyUpdates(updates []*Validator) { - existing := make([]*Validator, len(vals.Validators)) - copy(existing, vals.Validators) - + existing := vals.Validators merged := make([]*Validator, len(existing)+len(updates)) i := 0 for len(existing) > 0 && len(updates) > 0 { - if bytes.Compare(existing[0].Address, updates[0].Address) < 0 { + if bytes.Compare(existing[0].Address, updates[0].Address) < 0 { // unchanged validator merged[i] = existing[0] existing = existing[1:] } else { + // Apply add or update. merged[i] = updates[0] if bytes.Equal(existing[0].Address, updates[0].Address) { - // validator present in both, advance existing + // Validator is present in both, advance existing. existing = existing[1:] } updates = updates[1:] @@ -478,18 +458,18 @@ func (vals *ValidatorSet) applyUpdates(updates []*Validator) { i++ } + // Add the elements which are left. for j := 0; j < len(existing); j++ { merged[i] = existing[j] i++ } - + // OR add updates which are left. for j := 0; j < len(updates); j++ { merged[i] = updates[j] i++ } vals.Validators = merged[:i] - vals.totalVotingPower = 0 } // Checks that the validators to be removed are part of the validator set. @@ -503,6 +483,9 @@ func verifyRemovals(deletes []*Validator, vals *ValidatorSet) error { return fmt.Errorf("failed to find validator %X to remove", address) } } + if len(deletes) > len(vals.Validators) { + panic("more deletes than validators") + } return nil } @@ -510,50 +493,49 @@ func verifyRemovals(deletes []*Validator, vals *ValidatorSet) error { // Should not fail as verification has been done before. func (vals *ValidatorSet) applyRemovals(deletes []*Validator) { - for _, valUpdate := range deletes { - address := valUpdate.Address - _, removed := vals.Remove(address) - if !removed { - // Should never happen - panic(fmt.Sprintf("failed to remove validator %X", address)) + existing := vals.Validators + + merged := make([]*Validator, len(existing)-len(deletes)) + i := 0 + + // Loop over deletes until we removed all of them. + for len(deletes) > 0 { + if bytes.Equal(existing[0].Address, deletes[0].Address) { + deletes = deletes[1:] + } else { // Leave it in the resulting slice. + merged[i] = existing[0] + i++ } + existing = existing[1:] } + + // Add the elements which are left. + for j := 0; j < len(existing); j++ { + merged[i] = existing[j] + i++ + } + + vals.Validators = merged[:i] } -// UpdateWithChangeSet attempts to update the validator set with 'changes' -// It performs the following steps: -// - validates the changes making sure there are no duplicates and splits them in updates and deletes -// - verifies that applying the changes will not result in errors -// - computes the total voting power BEFORE removals to ensure that in the next steps the relative priorities -// across old and newly added validators is fair -// - computes the priorities of new validators against the final set -// - applies the updates against the validator set -// - applies the removals against the validator set -// - performs scaling and centering of priority values -// If error is detected during verification steps it is returned and the validator set -// is not changed. -func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { - return vals.updateWithChangeSet(changes, true) -} - -// main function used by UpdateWithChangeSet() and NewValidatorSet() -// If 'allowDeletes' is false then delete operations are not allowed and must be reported if -// present in 'changes' +// Main function used by UpdateWithChangeSet() and NewValidatorSet(). +// If 'allowDeletes' is false then delete operations (identified by validators with voting power 0) +// are not allowed and will trigger an error if present in 'changes'. +// The 'allowDeletes' flag is set to false by NewValidatorSet() and to true by UpdateWithChangeSet(). func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes bool) error { if len(changes) <= 0 { return nil } - // Check for duplicates within changes, split in 'updates' and 'deletes' lists (sorted) + // Check for duplicates within changes, split in 'updates' and 'deletes' lists (sorted). updates, deletes, err := processChanges(changes) if err != nil { return err } if !allowDeletes && len(deletes) != 0 { - err = fmt.Errorf("cannot process validators with voting power 0: %v", deletes) - return err + return fmt.Errorf("cannot process validators with voting power 0: %v", deletes) } // Verify that applying the 'deletes' against 'vals' will not result in error. @@ -562,29 +544,48 @@ func (vals *ValidatorSet) updateWithChangeSet(changes []*Validator, allowDeletes } // Verify that applying the 'updates' against 'vals' will not result in error. - updatedTotalVotingPower, err := verifyUpdates(updates, vals) + updatedTotalVotingPower, numNewValidators, err := verifyUpdates(updates, vals) if err != nil { return err } - // Compute the priorities for updates - numNewValidators := computeNewPriorities(updates, vals, updatedTotalVotingPower) - if len(vals.Validators)+numNewValidators <= len(deletes) { - err = fmt.Errorf("applying the validator changes would result in empty set") - return err + // Check that the resulting set will not be empty. + if numNewValidators == 0 && len(vals.Validators) == len(deletes) { + return errors.New("applying the validator changes would result in empty set") } - // Apply updates and removals + // Compute the priorities for updates. + computeNewPriorities(updates, vals, updatedTotalVotingPower) + + // Apply updates and removals. vals.applyUpdates(updates) vals.applyRemovals(deletes) - // Scale and center + vals.updateTotalVotingPower() + + // Scale and center. vals.RescalePriorities(PriorityWindowSizeFactor * vals.TotalVotingPower()) vals.shiftByAvgProposerPriority() return nil } +// UpdateWithChangeSet attempts to update the validator set with 'changes'. +// It performs the following steps: +// - validates the changes making sure there are no duplicates and splits them in updates and deletes +// - verifies that applying the changes will not result in errors +// - computes the total voting power BEFORE removals to ensure that in the next steps the priorities +// across old and newly added validators are fair +// - computes the priorities of new validators against the final set +// - applies the updates against the validator set +// - applies the removals against the validator set +// - performs scaling and centering of priority values +// If an error is detected during verification steps, it is returned and the validator set +// is not changed. +func (vals *ValidatorSet) UpdateWithChangeSet(changes []*Validator) error { + return vals.updateWithChangeSet(changes, true) +} + // Verify that +2/3 of the set had signed the given signBytes. func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height int64, commit *Commit) error { @@ -768,7 +769,7 @@ func (vals *ValidatorSet) StringIndented(indent string) string { //------------------------------------- // Implements sort for sorting validators by address. -// Sort validators by address +// Sort validators by address. type ValidatorsByAddress []*Validator func (valz ValidatorsByAddress) Len() int { @@ -786,7 +787,7 @@ func (valz ValidatorsByAddress) Swap(i, j int) { } //---------------------------------------- -// For testing +// for testing // RandValidatorSet returns a randomized validator set, useful for testing. // NOTE: PrivValidator are in order. @@ -805,7 +806,7 @@ func RandValidatorSet(numValidators int, votingPower int64) (*ValidatorSet, []Pr } /////////////////////////////////////////////////////////////////////////////// -// Safe addition/subtraction +// safe addition/subtraction func safeAdd(a, b int64) (int64, bool) { if b > 0 && a > math.MaxInt64-b { diff --git a/types/validator_set_test.go b/types/validator_set_test.go index d9558d131..22f373c73 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -4,7 +4,7 @@ import ( "bytes" "fmt" "math" - "math/rand" + "sort" "strings" "testing" "testing/quick" @@ -72,13 +72,6 @@ func TestValidatorSetBasic(t *testing.T) { _, val = vset.GetByAddress(val.Address) assert.Equal(t, proposerPriority, val.ProposerPriority) - // remove - val2, removed := vset.Remove(randValidator_(vset.TotalVotingPower()).Address) - assert.Nil(t, val2) - assert.False(t, removed) - val2, removed = vset.Remove(val.Address) - assert.Equal(t, val.Address, val2.Address) - assert.True(t, removed) } func TestCopy(t *testing.T) { @@ -658,6 +651,71 @@ type testVal struct { power int64 } +func permutation(valList []testVal) []testVal { + if len(valList) == 0 { + return nil + } + permList := make([]testVal, len(valList)) + perm := cmn.RandPerm(len(valList)) + for i, v := range perm { + permList[v] = valList[i] + } + return permList +} + +func createNewValidatorList(testValList []testVal) []*Validator { + valList := make([]*Validator, 0, len(testValList)) + for _, val := range testValList { + valList = append(valList, newValidator([]byte(val.name), val.power)) + } + return valList +} + +func createNewValidatorSet(testValList []testVal) *ValidatorSet { + return NewValidatorSet(createNewValidatorList(testValList)) +} + +func valSetTotalProposerPriority(valSet *ValidatorSet) int64 { + sum := int64(0) + for _, val := range valSet.Validators { + // mind overflow + sum = safeAddClip(sum, val.ProposerPriority) + } + return sum +} + +func verifyValidatorSet(t *testing.T, valSet *ValidatorSet) { + // verify that the capacity and length of validators is the same + assert.Equal(t, len(valSet.Validators), cap(valSet.Validators)) + + // verify that the set's total voting power has been updated + tvp := valSet.totalVotingPower + valSet.updateTotalVotingPower() + expectedTvp := valSet.TotalVotingPower() + assert.Equal(t, expectedTvp, tvp, + "expected TVP %d. Got %d, valSet=%s", expectedTvp, tvp, valSet) + + // verify that validator priorities are centered + valsCount := int64(len(valSet.Validators)) + tpp := valSetTotalProposerPriority(valSet) + assert.True(t, tpp < valsCount && tpp > -valsCount, + "expected total priority in (-%d, %d). Got %d", valsCount, valsCount, tpp) + + // verify that priorities are scaled + dist := computeMaxMinPriorityDiff(valSet) + assert.True(t, dist <= PriorityWindowSizeFactor*tvp, + "expected priority distance < %d. Got %d", PriorityWindowSizeFactor*tvp, dist) +} + +func toTestValList(valList []*Validator) []testVal { + testList := make([]testVal, len(valList)) + for i, val := range valList { + testList[i].name = string(val.Address) + testList[i].power = val.VotingPower + } + return testList +} + func testValSet(nVals int, power int64) []testVal { vals := make([]testVal, nVals) for i := 0; i < nVals; i++ { @@ -755,6 +813,10 @@ func TestValSetUpdatesOverflows(t *testing.T) { testValSet(2, 10), []testVal{{"v2", math.MaxInt64}}, }, + { // add validator leading to overflow + testValSet(1, maxVP), + []testVal{{"v2", math.MaxInt64}}, + }, { // add validator leading to exceed Max testValSet(1, maxVP-1), []testVal{{"v2", 5}}, @@ -763,6 +825,10 @@ func TestValSetUpdatesOverflows(t *testing.T) { testValSet(2, maxVP/3), []testVal{{"v3", maxVP / 2}}, }, + { // add validator leading to exceed Max + testValSet(1, maxVP), + []testVal{{"v2", maxVP}}, + }, } for i, tt := range testCases { @@ -837,30 +903,27 @@ func TestValSetUpdatesBasicTestsExecute(t *testing.T) { // create a new set and apply updates, keeping copies for the checks valSet := createNewValidatorSet(tt.startVals) valList := createNewValidatorList(tt.updateVals) - valListCopy := validatorListCopy(valList) err := valSet.UpdateWithChangeSet(valList) assert.NoError(t, err, "test %d", i) - // check the parameter list has not changed - assert.Equal(t, valList, valListCopy, "test %v", i) + valListCopy := validatorListCopy(valSet.Validators) + // check that the voting power in the set's validators is not changing if the voting power + // is changed in the list of validators previously passed as parameter to UpdateWithChangeSet. + // this is to make sure copies of the validators are made by UpdateWithChangeSet. + if len(valList) > 0 { + valList[0].VotingPower++ + assert.Equal(t, toTestValList(valListCopy), toTestValList(valSet.Validators), "test %v", i) + + } // check the final validator list is as expected and the set is properly scaled and centered. - assert.Equal(t, getValidatorResults(valSet.Validators), tt.expectedVals, "test %v", i) + assert.Equal(t, tt.expectedVals, toTestValList(valSet.Validators), "test %v", i) verifyValidatorSet(t, valSet) } } -func getValidatorResults(valList []*Validator) []testVal { - testList := make([]testVal, len(valList)) - for i, val := range valList { - testList[i].name = string(val.Address) - testList[i].power = val.VotingPower - } - return testList -} - // Test that different permutations of an update give the same result. -func TestValSetUpdatesOrderTestsExecute(t *testing.T) { +func TestValSetUpdatesOrderIndependenceTestsExecute(t *testing.T) { // startVals - initial validators to create the set with // updateVals - a sequence of updates to be applied to the set. // updateVals is shuffled a number of times during testing to check for same resulting validator set. @@ -973,55 +1036,242 @@ func TestValSetApplyUpdatesTestsExecute(t *testing.T) { valSet.applyUpdates(valList) // check the new list of validators for proper merge - assert.Equal(t, getValidatorResults(valSet.Validators), tt.expectedVals, "test %v", i) + assert.Equal(t, toTestValList(valSet.Validators), tt.expectedVals, "test %v", i) + } +} + +type testVSetCfg struct { + startVals []testVal + deletedVals []testVal + updatedVals []testVal + addedVals []testVal + expectedVals []testVal +} + +func randTestVSetCfg(t *testing.T, nBase, nAddMax int) testVSetCfg { + if nBase <= 0 || nAddMax < 0 { + panic(fmt.Sprintf("bad parameters %v %v", nBase, nAddMax)) + } + + const maxPower = 1000 + var nOld, nDel, nChanged, nAdd int + + nOld = int(cmn.RandUint()%uint(nBase)) + 1 + if nBase-nOld > 0 { + nDel = int(cmn.RandUint() % uint(nBase-nOld)) + } + nChanged = nBase - nOld - nDel + + if nAddMax > 0 { + nAdd = cmn.RandInt()%nAddMax + 1 + } + + cfg := testVSetCfg{} + + cfg.startVals = make([]testVal, nBase) + cfg.deletedVals = make([]testVal, nDel) + cfg.addedVals = make([]testVal, nAdd) + cfg.updatedVals = make([]testVal, nChanged) + cfg.expectedVals = make([]testVal, nBase-nDel+nAdd) + + for i := 0; i < nBase; i++ { + cfg.startVals[i] = testVal{fmt.Sprintf("v%d", i), int64(cmn.RandUint()%maxPower + 1)} + if i < nOld { + cfg.expectedVals[i] = cfg.startVals[i] + } + if i >= nOld && i < nOld+nChanged { + cfg.updatedVals[i-nOld] = testVal{fmt.Sprintf("v%d", i), int64(cmn.RandUint()%maxPower + 1)} + cfg.expectedVals[i] = cfg.updatedVals[i-nOld] + } + if i >= nOld+nChanged { + cfg.deletedVals[i-nOld-nChanged] = testVal{fmt.Sprintf("v%d", i), 0} + } + } + + for i := nBase; i < nBase+nAdd; i++ { + cfg.addedVals[i-nBase] = testVal{fmt.Sprintf("v%d", i), int64(cmn.RandUint()%maxPower + 1)} + cfg.expectedVals[i-nDel] = cfg.addedVals[i-nBase] + } + + sort.Sort(testValsByAddress(cfg.startVals)) + sort.Sort(testValsByAddress(cfg.deletedVals)) + sort.Sort(testValsByAddress(cfg.updatedVals)) + sort.Sort(testValsByAddress(cfg.addedVals)) + sort.Sort(testValsByAddress(cfg.expectedVals)) + + return cfg + +} + +func applyChangesToValSet(t *testing.T, valSet *ValidatorSet, valsLists ...[]testVal) { + changes := make([]testVal, 0) + for _, valsList := range valsLists { + changes = append(changes, valsList...) + } + valList := createNewValidatorList(changes) + err := valSet.UpdateWithChangeSet(valList) + assert.NoError(t, err) +} + +func isAddressInList(address []byte, valsList []testVal) bool { + for _, val := range valsList { + if bytes.Equal([]byte(val.name), address) { + return true + } + } + return false +} + +func TestValSetUpdatePriorityOrderTests(t *testing.T) { + const nMaxElections = 5000 + + testCases := []testVSetCfg{ + 0: { // remove high power validator, keep old equal lower power validators + startVals: []testVal{{"v1", 1}, {"v2", 1}, {"v3", 1000}}, + deletedVals: []testVal{{"v3", 0}}, + updatedVals: []testVal{}, + addedVals: []testVal{}, + expectedVals: []testVal{{"v1", 1}, {"v2", 1}}, + }, + 1: { // remove high power validator, keep old different power validators + startVals: []testVal{{"v1", 1}, {"v2", 10}, {"v3", 1000}}, + deletedVals: []testVal{{"v3", 0}}, + updatedVals: []testVal{}, + addedVals: []testVal{}, + expectedVals: []testVal{{"v1", 1}, {"v2", 10}}, + }, + 2: { // remove high power validator, add new low power validators, keep old lower power + startVals: []testVal{{"v1", 1}, {"v2", 2}, {"v3", 1000}}, + deletedVals: []testVal{{"v3", 0}}, + updatedVals: []testVal{{"v2", 1}}, + addedVals: []testVal{{"v4", 40}, {"v5", 50}}, + expectedVals: []testVal{{"v1", 1}, {"v2", 1}, {"v4", 40}, {"v5", 50}}, + }, + + // generate a configuration with 100 validators, + // randomly select validators for updates and deletes, and + // generate 10 new validators to be added + 3: randTestVSetCfg(t, 100, 10), + + 4: randTestVSetCfg(t, 1000, 100), + + 5: randTestVSetCfg(t, 10, 100), + + 6: randTestVSetCfg(t, 100, 1000), + + 7: randTestVSetCfg(t, 1000, 1000), + + 8: randTestVSetCfg(t, 10000, 1000), + + 9: randTestVSetCfg(t, 1000, 10000), + } + + for _, cfg := range testCases { + + // create a new validator set + valSet := createNewValidatorSet(cfg.startVals) verifyValidatorSet(t, valSet) + + // run election up to nMaxElections times, apply changes and verify that the priority order is correct + verifyValSetUpdatePriorityOrder(t, valSet, cfg, nMaxElections) } } -func permutation(valList []testVal) []testVal { - if len(valList) == 0 { - return nil +func verifyValSetUpdatePriorityOrder(t *testing.T, valSet *ValidatorSet, cfg testVSetCfg, nMaxElections int) { + + // Run election up to nMaxElections times, sort validators by priorities + valSet.IncrementProposerPriority(cmn.RandInt()%nMaxElections + 1) + origValsPriSorted := validatorListCopy(valSet.Validators) + sort.Sort(validatorsByPriority(origValsPriSorted)) + + // apply the changes, get the updated validators, sort by priorities + applyChangesToValSet(t, valSet, cfg.addedVals, cfg.updatedVals, cfg.deletedVals) + updatedValsPriSorted := validatorListCopy(valSet.Validators) + sort.Sort(validatorsByPriority(updatedValsPriSorted)) + + // basic checks + assert.Equal(t, toTestValList(valSet.Validators), cfg.expectedVals) + verifyValidatorSet(t, valSet) + + // verify that the added validators have the smallest priority: + // - they should be at the beginning of valListNewPriority since it is sorted by priority + if len(cfg.addedVals) > 0 { + addedValsPriSlice := updatedValsPriSorted[:len(cfg.addedVals)] + sort.Sort(ValidatorsByAddress(addedValsPriSlice)) + assert.Equal(t, cfg.addedVals, toTestValList(addedValsPriSlice)) + + // - and should all have the same priority + expectedPri := addedValsPriSlice[0].ProposerPriority + for _, val := range addedValsPriSlice[1:] { + assert.Equal(t, expectedPri, val.ProposerPriority) + } } - permList := make([]testVal, len(valList)) - perm := rand.Perm(len(valList)) - for i, v := range perm { - permList[v] = valList[i] + + // check that the priority order for validators that remained is the same + // as in the original set + remainingValsPriSlice := updatedValsPriSorted[len(cfg.addedVals):] + + for len(remainingValsPriSlice) > 0 { + addressInChanged := remainingValsPriSlice[0].Address + addressInOld := origValsPriSorted[0].Address + + // skip validators in original list that have been removed + if isAddressInList(addressInOld, cfg.deletedVals) { + origValsPriSorted = origValsPriSorted[1:] + continue + } + assert.Equal(t, addressInOld, addressInChanged, "wrong priority order") + + remainingValsPriSlice = remainingValsPriSlice[1:] + origValsPriSorted = origValsPriSorted[1:] } - return permList } -func createNewValidatorList(testValList []testVal) []*Validator { - valList := make([]*Validator, 0, len(testValList)) - for _, val := range testValList { - valList = append(valList, newValidator([]byte(val.name), val.power)) +//--------------------- +// Sort validators by priority and address +type validatorsByPriority []*Validator + +func (valz validatorsByPriority) Len() int { + return len(valz) +} + +func (valz validatorsByPriority) Less(i, j int) bool { + if valz[i].ProposerPriority < valz[j].ProposerPriority { + return true } - return valList + if valz[i].ProposerPriority > valz[j].ProposerPriority { + return false + } + return bytes.Compare(valz[i].Address, valz[j].Address) < 0 } -func createNewValidatorSet(testValList []testVal) *ValidatorSet { - valList := createNewValidatorList(testValList) - valSet := NewValidatorSet(valList) - return valSet +func (valz validatorsByPriority) Swap(i, j int) { + it := valz[i] + valz[i] = valz[j] + valz[j] = it } -func verifyValidatorSet(t *testing.T, valSet *ValidatorSet) { - // verify that the vals' tvp is set to the sum of the all vals voting powers - tvp := valSet.TotalVotingPower() - assert.Equal(t, valSet.totalVotingPower, tvp, - "expected TVP %d. Got %d, valSet=%s", tvp, valSet.totalVotingPower, valSet) +//------------------------------------- +// Sort testVal-s by address. +type testValsByAddress []testVal - // verify that validator priorities are centered - l := int64(len(valSet.Validators)) - tpp := valSet.TotalVotingPower() - assert.True(t, tpp <= l || tpp >= -l, - "expected total priority in (-%d, %d). Got %d", l, l, tpp) - - // verify that priorities are scaled - dist := computeMaxMinPriorityDiff(valSet) - assert.True(t, dist <= PriorityWindowSizeFactor*tvp, - "expected priority distance < %d. Got %d", PriorityWindowSizeFactor*tvp, dist) +func (tvals testValsByAddress) Len() int { + return len(tvals) } +func (tvals testValsByAddress) Less(i, j int) bool { + return bytes.Compare([]byte(tvals[i].name), []byte(tvals[j].name)) == -1 +} + +func (tvals testValsByAddress) Swap(i, j int) { + it := tvals[i] + tvals[i] = tvals[j] + tvals[j] = it +} + +//------------------------------------- +// Benchmark tests +// func BenchmarkUpdates(b *testing.B) { const ( n = 100 From f25d727035f4615cbb4b43452191fd83218fc1e1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 7 Mar 2019 09:10:34 +0400 Subject: [PATCH 086/259] make dupl linter pass (#3385) Refs #3262 --- .golangci.yml | 1 - Makefile | 4 +- libs/db/db_test.go | 149 ++++++++++++++++++--------------------- rpc/client/event_test.go | 58 ++++++--------- 4 files changed, 91 insertions(+), 121 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index 45cabe201..cf8bf165d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,6 @@ linters: - maligned - errcheck - staticcheck - - dupl - ineffassign - interfacer - unconvert diff --git a/Makefile b/Makefile index 08373644c..79ae6aaba 100644 --- a/Makefile +++ b/Makefile @@ -214,11 +214,11 @@ vagrant_test: ### go tests test: @echo "--> Running go test" - go test -p 1 $(PACKAGES) + @go test -p 1 $(PACKAGES) test_race: @echo "--> Running go test --race" - go test -p 1 -v -race $(PACKAGES) + @go test -p 1 -v -race $(PACKAGES) # uses https://github.com/sasha-s/go-deadlock/ to detect potential deadlocks test_with_deadlock: diff --git a/libs/db/db_test.go b/libs/db/db_test.go index ffa7bb6aa..7cb721b26 100644 --- a/libs/db/db_test.go +++ b/libs/db/db_test.go @@ -121,86 +121,75 @@ func TestDBIteratorNonemptyBeginAfter(t *testing.T) { } } -func TestDBBatchWrite1(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() +func TestDBBatchWrite(t *testing.T) { + testCases := []struct { + modify func(batch Batch) + calls map[string]int + }{ + 0: { + func(batch Batch) { + batch.Set(bz("1"), bz("1")) + batch.Set(bz("2"), bz("2")) + batch.Delete(bz("3")) + batch.Set(bz("4"), bz("4")) + batch.Write() + }, + map[string]int{ + "Set": 0, "SetSync": 0, "SetNoLock": 3, "SetNoLockSync": 0, + "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 1, "DeleteNoLockSync": 0, + }, + }, + 1: { + func(batch Batch) { + batch.Set(bz("1"), bz("1")) + batch.Set(bz("2"), bz("2")) + batch.Set(bz("4"), bz("4")) + batch.Delete(bz("3")) + batch.Write() + }, + map[string]int{ + "Set": 0, "SetSync": 0, "SetNoLock": 3, "SetNoLockSync": 0, + "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 1, "DeleteNoLockSync": 0, + }, + }, + 2: { + func(batch Batch) { + batch.Set(bz("1"), bz("1")) + batch.Set(bz("2"), bz("2")) + batch.Delete(bz("3")) + batch.Set(bz("4"), bz("4")) + batch.WriteSync() + }, + map[string]int{ + "Set": 0, "SetSync": 0, "SetNoLock": 2, "SetNoLockSync": 1, + "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 1, "DeleteNoLockSync": 0, + }, + }, + 3: { + func(batch Batch) { + batch.Set(bz("1"), bz("1")) + batch.Set(bz("2"), bz("2")) + batch.Set(bz("4"), bz("4")) + batch.Delete(bz("3")) + batch.WriteSync() + }, + map[string]int{ + "Set": 0, "SetSync": 0, "SetNoLock": 3, "SetNoLockSync": 0, + "Delete": 0, "DeleteSync": 0, "DeleteNoLock": 0, "DeleteNoLockSync": 1, + }, + }, + } - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Delete(bz("3")) - batch.Set(bz("4"), bz("4")) - batch.Write() + for i, tc := range testCases { + mdb := newMockDB() + ddb := NewDebugDB(t.Name(), mdb) + batch := ddb.NewBatch() - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 3, mdb.calls["SetNoLock"]) - assert.Equal(t, 0, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLockSync"]) -} - -func TestDBBatchWrite2(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Set(bz("4"), bz("4")) - batch.Delete(bz("3")) - batch.Write() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 3, mdb.calls["SetNoLock"]) - assert.Equal(t, 0, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLockSync"]) -} - -func TestDBBatchWriteSync1(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Delete(bz("3")) - batch.Set(bz("4"), bz("4")) - batch.WriteSync() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 2, mdb.calls["SetNoLock"]) - assert.Equal(t, 1, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLockSync"]) -} - -func TestDBBatchWriteSync2(t *testing.T) { - mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() - - batch.Set(bz("1"), bz("1")) - batch.Set(bz("2"), bz("2")) - batch.Set(bz("4"), bz("4")) - batch.Delete(bz("3")) - batch.WriteSync() - - assert.Equal(t, 0, mdb.calls["Set"]) - assert.Equal(t, 0, mdb.calls["SetSync"]) - assert.Equal(t, 3, mdb.calls["SetNoLock"]) - assert.Equal(t, 0, mdb.calls["SetNoLockSync"]) - assert.Equal(t, 0, mdb.calls["Delete"]) - assert.Equal(t, 0, mdb.calls["DeleteSync"]) - assert.Equal(t, 0, mdb.calls["DeleteNoLock"]) - assert.Equal(t, 1, mdb.calls["DeleteNoLockSync"]) + tc.modify(batch) + + for call, exp := range tc.calls { + got := mdb.calls[call] + assert.Equal(t, exp, got, "#%v - key: %s", i, call) + } + } } diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index da4625d51..7b00d6ead 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -1,6 +1,7 @@ package client_test import ( + "fmt" "reflect" "testing" "time" @@ -10,6 +11,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" ) @@ -78,7 +80,10 @@ func TestBlockEvents(t *testing.T) { } } -func TestTxEventsSentWithBroadcastTxAsync(t *testing.T) { +func TestTxEventsSentWithBroadcastTxAsync(t *testing.T) { testTxEventsSent(t, "async") } +func TestTxEventsSentWithBroadcastTxSync(t *testing.T) { testTxEventsSent(t, "sync") } + +func testTxEventsSent(t *testing.T, broadcastMethod string) { for i, c := range GetClients() { i, c := i, c // capture params t.Run(reflect.TypeOf(c).String(), func(t *testing.T) { @@ -95,45 +100,22 @@ func TestTxEventsSentWithBroadcastTxAsync(t *testing.T) { _, _, tx := MakeTxKV() evtTyp := types.EventTx - // send async - txres, err := c.BroadcastTxAsync(tx) - require.Nil(t, err, "%+v", err) - require.Equal(t, txres.Code, abci.CodeTypeOK) // FIXME - - // and wait for confirmation - evt, err := client.WaitForOneEvent(c, evtTyp, waitForEventTimeout) - require.Nil(t, err, "%d: %+v", i, err) - // and make sure it has the proper info - txe, ok := evt.(types.EventDataTx) - require.True(t, ok, "%d: %#v", i, evt) - // make sure this is the proper tx - require.EqualValues(t, tx, txe.Tx) - require.True(t, txe.Result.IsOK()) - }) - } -} - -func TestTxEventsSentWithBroadcastTxSync(t *testing.T) { - for i, c := range GetClients() { - i, c := i, c // capture params - t.Run(reflect.TypeOf(c).String(), func(t *testing.T) { - - // start for this test it if it wasn't already running - if !c.IsRunning() { - // if so, then we start it, listen, and stop it. - err := c.Start() - require.Nil(t, err, "%d: %+v", i, err) - defer c.Stop() + // send + var ( + txres *ctypes.ResultBroadcastTx + err error + ) + switch broadcastMethod { + case "async": + txres, err = c.BroadcastTxAsync(tx) + case "sync": + txres, err = c.BroadcastTxSync(tx) + default: + panic(fmt.Sprintf("Unknown broadcastMethod %s", broadcastMethod)) } - // make the tx - _, _, tx := MakeTxKV() - evtTyp := types.EventTx - - // send sync - txres, err := c.BroadcastTxSync(tx) - require.Nil(t, err, "%+v", err) - require.Equal(t, txres.Code, abci.CodeTypeOK) // FIXME + require.NoError(t, err) + require.Equal(t, txres.Code, abci.CodeTypeOK) // and wait for confirmation evt, err := client.WaitForOneEvent(c, evtTyp, waitForEventTimeout) From 91b488f9a541f5dd69f0bb2db4249356733a2ed3 Mon Sep 17 00:00:00 2001 From: YOSHIDA Masanori Date: Thu, 7 Mar 2019 22:02:13 +0900 Subject: [PATCH 087/259] docs: fix the reverse of meaning in spec (#3387) https://tools.ietf.org/html/rfc6962#section-2.1 "The largest power of two less than the number of items" is actually correct! For n > 1, let k be the largest power of two smaller than n (i.e., k < n <= 2k). --- docs/spec/blockchain/encoding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index 1b999335b..e8258e4a9 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -175,7 +175,7 @@ The differences between RFC 6962 and the simplest form a merkle tree are that: The leaf nodes are `SHA256(0x00 || leaf_data)`, and inner nodes are `SHA256(0x01 || left_hash || right_hash)`. 2) When the number of items isn't a power of two, the left half of the tree is as big as it could be. - (The smallest power of two less than the number of items) This allows new leaves to be added with less + (The largest power of two less than the number of items) This allows new leaves to be added with less recomputation. For example: ``` From 3ebfa99f2c2403c17348e41190406892abf21d2a Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 7 Mar 2019 19:35:04 +0400 Subject: [PATCH 088/259] do not pin repos without releases to exact revisions (#3382) We're pinning repos without releases because it's very easy to upgrade all the dependencies by executing dep ensure --upgrade. Instead, we should just never run this command directly, only dep ensure --upgrade . And we can defend that in PRs. Refs #3374 The problem with pinning to exact revisions: people who import Tendermint as a library (e.g. abci/types) are stuck with these revisions even though the code they import may not even use them. --- Gopkg.toml | 34 +++++++--------------------------- 1 file changed, 7 insertions(+), 27 deletions(-) diff --git a/Gopkg.toml b/Gopkg.toml index c334ab71d..db97cb090 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -76,34 +76,14 @@ version = "^0.9.1" ################################### -## Some repos dont have releases. -## Pin to revision +## Repos which don't have releases. -[[constraint]] - name = "github.com/btcsuite/btcd" - revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d" - -[[constraint]] - name = "golang.org/x/crypto" - revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" - -[[override]] - name = "github.com/jmhodges/levigo" - revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" - -# last revision used by go-crypto -[[constraint]] - name = "github.com/btcsuite/btcutil" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" - - -[[constraint]] - name = "github.com/rcrowley/go-metrics" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - -[[constraint]] - name = "golang.org/x/net" - revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" +## - github.com/btcsuite/btcd +## - golang.org/x/crypto +## - github.com/jmhodges/levigo +## - github.com/btcsuite/btcutil +## - github.com/rcrowley/go-metrics +## - golang.org/x/net [prune] go-tests = true From 28e9e9e7145b357cbd7078f4c83dcf66c13d4f7d Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Thu, 7 Mar 2019 17:03:57 +0100 Subject: [PATCH 089/259] update levigo to 1.0.0 (#3389) Although the version we were pinning to is from Nov. 2016 there were no substantial changes: jmhodges/levigo@2b8c778 added go-modules support (no code changes) jmhodges/levigo@853d788 added a badge to the readme closes #3381 --- Gopkg.lock | 5 +++-- Gopkg.toml | 5 ++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 146c9420d..b5d022ae8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -154,11 +154,12 @@ version = "v1.0" [[projects]] - digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214" + digest = "1:a74b5a8e34ee5843cd6e65f698f3e75614f812ff170c2243425d75bc091e9af2" name = "github.com/jmhodges/levigo" packages = ["."] pruneopts = "UT" - revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9" + revision = "853d788c5c416eaaee5b044570784a96c7a26975" + version = "v1.0.0" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index db97cb090..505f0da4a 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -75,12 +75,15 @@ name = "github.com/prometheus/client_golang" version = "^0.9.1" +[[constraint]] + name = "github.com/jmhodges/levigo" + version = "^1.0.0" + ################################### ## Repos which don't have releases. ## - github.com/btcsuite/btcd ## - golang.org/x/crypto -## - github.com/jmhodges/levigo ## - github.com/btcsuite/btcutil ## - github.com/rcrowley/go-metrics ## - golang.org/x/net From e415c326f93e5fa473d83a0ea6fa495ad0ea87f3 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Fri, 8 Mar 2019 06:40:59 +0100 Subject: [PATCH 090/259] update golang.org/x/crypto (#3392) Update Gopkg.lock via dep ensure --update golang.org/x/crypto see #3391 (comment) (nothing to review here really). --- Gopkg.lock | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index b5d022ae8..530cd89dd 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -369,7 +369,8 @@ version = "v0.14.1" [[projects]] - digest = "1:00d2b3e64cdc3fa69aa250dfbe4cc38c4837d4f37e62279be2ae52107ffbbb44" + branch = "master" + digest = "1:f4edb30d5ff238e2abba10457010f74cd55ae20bbda8c54db1a07155fa020490" name = "golang.org/x/crypto" packages = [ "bcrypt", @@ -390,7 +391,7 @@ "salsa20/salsa", ] pruneopts = "UT" - revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447" + revision = "8dd112bcdc25174059e45e07517d9fc663123347" [[projects]] digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" From b6a510a3e7cefca56af4bdb009413e3950c6d59e Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 8 Mar 2019 09:46:09 +0400 Subject: [PATCH 091/259] make ineffassign linter pass (#3386) Refs #3262 This fixes two small bugs: 1) lite/dbprovider: return `ok` instead of true in parse* functions. It's weird that we're ignoring `ok` value before. 2) consensus/state: previously because of the shadowing we almost never output "Error with msg". Now we declare both `added` and `err` in the beginning of the function, so there's no shadowing. --- .golangci.yml | 1 - consensus/state.go | 12 ++++++++---- lite/dbprovider.go | 7 ++++--- lite/dynamic_verifier_test.go | 1 + lite/proxy/query_test.go | 2 ++ p2p/conn/connection_test.go | 8 +++++--- rpc/core/status.go | 2 +- state/execution_test.go | 1 + state/state_test.go | 6 ++++++ 9 files changed, 28 insertions(+), 12 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index cf8bf165d..a051e1a45 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,6 @@ linters: - maligned - errcheck - staticcheck - - ineffassign - interfacer - unconvert - goconst diff --git a/consensus/state.go b/consensus/state.go index cf32afe7b..d4a12a0c3 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -670,7 +670,10 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { cs.mtx.Lock() defer cs.mtx.Unlock() - var err error + var ( + err error + added bool + ) msg, peerID := mi.Msg, mi.PeerID switch msg := msg.(type) { case *ProposalMessage: @@ -679,7 +682,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { err = cs.setProposal(msg.Proposal) case *BlockPartMessage: // if the proposal is complete, we'll enterPrevote or tryFinalizeCommit - added, err := cs.addProposalBlockPart(msg, peerID) + added, err = cs.addProposalBlockPart(msg, peerID) if added { cs.statsMsgQueue <- mi } @@ -691,7 +694,7 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { case *VoteMessage: // attempt to add the vote and dupeout the validator if its a duplicate signature // if the vote gives us a 2/3-any or 2/3-one, we transition - added, err := cs.tryAddVote(msg.Vote, peerID) + added, err = cs.tryAddVote(msg.Vote, peerID) if added { cs.statsMsgQueue <- mi } @@ -714,7 +717,8 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { cs.Logger.Error("Unknown msg type", reflect.TypeOf(msg)) } if err != nil { - cs.Logger.Error("Error with msg", "height", cs.Height, "round", cs.Round, "type", reflect.TypeOf(msg), "peer", peerID, "err", err, "msg", msg) + cs.Logger.Error("Error with msg", "height", cs.Height, "round", cs.Round, + "peer", peerID, "err", err, "msg", msg) } } diff --git a/lite/dbprovider.go b/lite/dbprovider.go index ef1b2a598..5582a9632 100644 --- a/lite/dbprovider.go +++ b/lite/dbprovider.go @@ -258,14 +258,15 @@ func parseKey(key []byte) (chainID string, height int64, part string, ok bool) { } func parseSignedHeaderKey(key []byte) (chainID string, height int64, ok bool) { - chainID, height, part, ok := parseKey(key) + var part string + chainID, height, part, ok = parseKey(key) if part != "sh" { return "", 0, false } - return chainID, height, true + return } func parseChainKeyPrefix(key []byte) (chainID string, height int64, ok bool) { chainID, height, _, ok = parseKey(key) - return chainID, height, true + return } diff --git a/lite/dynamic_verifier_test.go b/lite/dynamic_verifier_test.go index 386de513c..e85cb7de0 100644 --- a/lite/dynamic_verifier_test.go +++ b/lite/dynamic_verifier_test.go @@ -255,6 +255,7 @@ func TestConcurrencyInquirerVerify(t *testing.T) { cert.SetLogger(log.TestingLogger()) err = source.SaveFullCommit(fcz[7]) + require.Nil(err, "%+v", err) err = source.SaveFullCommit(fcz[8]) require.Nil(err, "%+v", err) sh := fcz[8].SignedHeader diff --git a/lite/proxy/query_test.go b/lite/proxy/query_test.go index c1450a5e6..db2b6e46c 100644 --- a/lite/proxy/query_test.go +++ b/lite/proxy/query_test.go @@ -93,6 +93,8 @@ func _TestAppProofs(t *testing.T) { // verify a query before the tx block has no data (and valid non-exist proof) bs, height, proof, err := GetWithProof(prt, k, brh-1, cl, cert) require.NoError(err, "%#v", err) + require.NotNil(proof) + require.Equal(height, brh-1) // require.NotNil(proof) // TODO: Ensure that *some* keys will be there, ensuring that proof is nil, // (currently there's a race condition) diff --git a/p2p/conn/connection_test.go b/p2p/conn/connection_test.go index afad69d1d..283b00ebe 100644 --- a/p2p/conn/connection_test.go +++ b/p2p/conn/connection_test.go @@ -223,7 +223,10 @@ func TestMConnectionMultiplePongsInTheBeginning(t *testing.T) { serverGotPing := make(chan struct{}) go func() { // read ping (one byte) - var packet, err = Packet(nil), error(nil) + var ( + packet Packet + err error + ) _, err = cdc.UnmarshalBinaryLengthPrefixedReader(server, &packet, maxPingPongPacketSize) require.Nil(t, err) serverGotPing <- struct{}{} @@ -492,8 +495,7 @@ func TestMConnectionReadErrorUnknownMsgType(t *testing.T) { defer mconnServer.Stop() // send msg with unknown msg type - err := error(nil) - err = amino.EncodeUvarint(mconnClient.conn, 4) + err := amino.EncodeUvarint(mconnClient.conn, 4) assert.Nil(t, err) _, err = mconnClient.conn.Write([]byte{0xFF, 0xFF, 0xFF, 0xFF}) assert.Nil(t, err) diff --git a/rpc/core/status.go b/rpc/core/status.go index 224857d00..ae22ecd35 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -71,7 +71,7 @@ import ( // } // ``` func Status() (*ctypes.ResultStatus, error) { - var latestHeight int64 = -1 + var latestHeight int64 if consensusReactor.FastSync() { latestHeight = blockStore.Height() } else { diff --git a/state/execution_test.go b/state/execution_test.go index 94336851c..a9fdfe270 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -43,6 +43,7 @@ func TestApplyBlock(t *testing.T) { block := makeBlock(state, 1) blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + //nolint:ineffassign state, err = blockExec.ApplyBlock(state, blockID, block) require.Nil(t, err) diff --git a/state/state_test.go b/state/state_test.go index 4566d93ea..ff8eed027 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -668,6 +668,7 @@ func TestLargeGenesisValidator(t *testing.T) { blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + require.NoError(t, err) // no changes in voting power (ProposerPrio += VotingPower == Voting in 1st round; than shiftByAvg == 0, // than -Total == -Voting) // -> no change in ProposerPrio (stays zero): @@ -692,6 +693,7 @@ func TestLargeGenesisValidator(t *testing.T) { block := makeBlock(oldState, oldState.LastBlockHeight+1) blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + require.NoError(t, err) lastState := updatedState for i := 0; i < 200; i++ { @@ -706,6 +708,7 @@ func TestLargeGenesisValidator(t *testing.T) { blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} updatedStateInner, err := updateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates) + require.NoError(t, err) lastState = updatedStateInner } // set state to last state of above iteration @@ -735,6 +738,7 @@ func TestLargeGenesisValidator(t *testing.T) { block := makeBlock(oldState, oldState.LastBlockHeight+1) blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) + require.NoError(t, err) } require.Equal(t, 10+2, len(state.NextValidators.Validators)) @@ -766,6 +770,7 @@ func TestLargeGenesisValidator(t *testing.T) { block = makeBlock(curState, curState.LastBlockHeight+1) blockID = types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} curState, err = updateState(curState, blockID, &block.Header, abciResponses, validatorUpdates) + require.NoError(t, err) if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) { isProposerUnchanged = false } @@ -790,6 +795,7 @@ func TestLargeGenesisValidator(t *testing.T) { blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} updatedState, err = updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) + require.NoError(t, err) if i > numVals { // expect proposers to cycle through after the first iteration (of numVals blocks): if proposers[i%numVals] == nil { proposers[i%numVals] = updatedState.NextValidators.Proposer From 90794260bcd23989e4bd8530d596e39d9509e7ca Mon Sep 17 00:00:00 2001 From: mircea-c Date: Sat, 9 Mar 2019 10:13:36 -0500 Subject: [PATCH 092/259] circleci: removed complexity from docs deployment job (#3396) --- .circleci/config.yml | 19 +++++++++++++++++-- CHANGELOG_PENDING.md | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 025fc48e8..9c51bc48f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,7 +10,7 @@ defaults: &defaults docs_update_config: &docs_update_config working_directory: ~/repo docker: - - image: tendermint/docs_deployment + - image: tendermintdev/jq_curl environment: AWS_REGION: us-east-1 @@ -239,7 +239,22 @@ jobs: - run: name: Trigger website build command: | - chamber exec tendermint -- start_website_build + curl --silent \ + --show-error \ + -X POST \ + --header "Content-Type: application/json" \ + -d "{\"branch\": \"$CIRCLE_BRANCH\"}" \ + "https://circleci.com/api/v1.1/project/github/$CIRCLE_PROJECT_USERNAME/$WEBSITE_REPO_NAME/build?circle-token=$TENDERBOT_API_TOKEN" > response.json + + RESULT=`jq -r '.status' response.json` + MESSAGE=`jq -r '.message' response.json` + + if [[ ${RESULT} == "null" ]] || [[ ${RESULT} -ne "200" ]]; then + echo "CircleCI API call failed: $MESSAGE" + exit 1 + else + echo "Website build started" + fi workflows: version: 2 diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index cbb4077c7..9ca5ab649 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -40,6 +40,8 @@ Special thanks to external contributors on this release: - leveldb.alivesnaps - leveldb.aliveiters +CI/CD: * [\#3396](https://github.com/tendermint/tendermint/pull/3396) + ### BUG FIXES: - [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection From b021f1e505482fb34f8a8e57cd86b171e4a57344 Mon Sep 17 00:00:00 2001 From: Yumin Xia Date: Sun, 10 Mar 2019 00:46:32 -0800 Subject: [PATCH 093/259] libs/db: close batch (#3397) ClevelDB requires closing when WriteBatch is no longer needed, https://godoc.org/github.com/jmhodges/levigo#WriteBatch.Close Fixes the memory leak in https://github.com/cosmos/cosmos-sdk/issues/3842 --- libs/db/c_level_db.go | 5 +++++ libs/db/debug_db.go | 5 +++++ libs/db/go_level_db.go | 4 ++++ libs/db/mem_batch.go | 4 ++++ libs/db/prefix_db.go | 4 ++++ libs/db/remotedb/grpcdb/server.go | 1 + libs/db/remotedb/remotedb.go | 4 ++++ libs/db/types.go | 2 ++ lite/dbprovider.go | 1 + state/txindex/kv/kv.go | 2 ++ 10 files changed, 32 insertions(+) diff --git a/libs/db/c_level_db.go b/libs/db/c_level_db.go index e411dfdd0..116e51bc2 100644 --- a/libs/db/c_level_db.go +++ b/libs/db/c_level_db.go @@ -187,6 +187,11 @@ func (mBatch *cLevelDBBatch) WriteSync() { } } +// Implements Batch. +func (mBatch *cLevelDBBatch) Close() { + mBatch.batch.Close() +} + //---------------------------------------- // Iterator // NOTE This is almost identical to db/go_level_db.Iterator diff --git a/libs/db/debug_db.go b/libs/db/debug_db.go index bb361a266..658cd0555 100644 --- a/libs/db/debug_db.go +++ b/libs/db/debug_db.go @@ -250,3 +250,8 @@ func (dbch debugBatch) WriteSync() { fmt.Printf("%v.batch.WriteSync()\n", dbch.label) dbch.bch.WriteSync() } + +// Implements Batch. +func (dbch debugBatch) Close() { + dbch.bch.Close() +} diff --git a/libs/db/go_level_db.go b/libs/db/go_level_db.go index fd487a4dd..9a4358f60 100644 --- a/libs/db/go_level_db.go +++ b/libs/db/go_level_db.go @@ -184,6 +184,10 @@ func (mBatch *goLevelDBBatch) WriteSync() { } } +// Implements Batch. +// Close is no-op for goLevelDBBatch. +func (mBatch *goLevelDBBatch) Close() {} + //---------------------------------------- // Iterator // NOTE This is almost identical to db/c_level_db.Iterator diff --git a/libs/db/mem_batch.go b/libs/db/mem_batch.go index 5c5d0c13a..ebba43f54 100644 --- a/libs/db/mem_batch.go +++ b/libs/db/mem_batch.go @@ -46,6 +46,10 @@ func (mBatch *memBatch) WriteSync() { mBatch.write(true) } +func (mBatch *memBatch) Close() { + mBatch.ops = nil +} + func (mBatch *memBatch) write(doSync bool) { if mtx := mBatch.db.Mutex(); mtx != nil { mtx.Lock() diff --git a/libs/db/prefix_db.go b/libs/db/prefix_db.go index 40d72560c..0dd06ef9d 100644 --- a/libs/db/prefix_db.go +++ b/libs/db/prefix_db.go @@ -248,6 +248,10 @@ func (pb prefixBatch) WriteSync() { pb.source.WriteSync() } +func (pb prefixBatch) Close() { + pb.source.Close() +} + //---------------------------------------- // prefixIterator diff --git a/libs/db/remotedb/grpcdb/server.go b/libs/db/remotedb/grpcdb/server.go index 3a9955ddf..bfe65e610 100644 --- a/libs/db/remotedb/grpcdb/server.go +++ b/libs/db/remotedb/grpcdb/server.go @@ -180,6 +180,7 @@ func (s *server) BatchWriteSync(c context.Context, b *protodb.Batch) (*protodb.N func (s *server) batchWrite(c context.Context, b *protodb.Batch, sync bool) (*protodb.Nothing, error) { bat := s.db.NewBatch() + defer bat.Close() for _, op := range b.Ops { switch op.Type { case protodb.Operation_SET: diff --git a/libs/db/remotedb/remotedb.go b/libs/db/remotedb/remotedb.go index 2b60d8159..c70d54b9e 100644 --- a/libs/db/remotedb/remotedb.go +++ b/libs/db/remotedb/remotedb.go @@ -260,3 +260,7 @@ func (bat *batch) WriteSync() { panic(fmt.Sprintf("RemoteDB.BatchWriteSync: %v", err)) } } + +func (bat *batch) Close() { + bat.ops = nil +} diff --git a/libs/db/types.go b/libs/db/types.go index 9b9c6d0b9..30f8afd18 100644 --- a/libs/db/types.go +++ b/libs/db/types.go @@ -57,10 +57,12 @@ type DB interface { //---------------------------------------- // Batch +// Batch Close must be called when the program no longer needs the object. type Batch interface { SetDeleter Write() WriteSync() + Close() } type SetDeleter interface { diff --git a/lite/dbprovider.go b/lite/dbprovider.go index 5582a9632..4e76e3657 100644 --- a/lite/dbprovider.go +++ b/lite/dbprovider.go @@ -54,6 +54,7 @@ func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error { dbp.logger.Info("DBProvider.SaveFullCommit()...", "fc", fc) batch := dbp.db.NewBatch() + defer batch.Close() // Save the fc.validators. // We might be overwriting what we already have, but diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 93249b7f9..84208b8c1 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -78,6 +78,7 @@ func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { // AddBatch indexes a batch of transactions using the given list of tags. func (txi *TxIndex) AddBatch(b *txindex.Batch) error { storeBatch := txi.store.NewBatch() + defer storeBatch.Close() for _, result := range b.Ops { hash := result.Tx.Hash() @@ -109,6 +110,7 @@ func (txi *TxIndex) AddBatch(b *txindex.Batch) error { // Index indexes a single transaction using the given list of tags. func (txi *TxIndex) Index(result *types.TxResult) error { b := txi.store.NewBatch() + defer b.Close() hash := result.Tx.Hash() From 36d7180ca216f0d7ff62851fa441de2b0371d699 Mon Sep 17 00:00:00 2001 From: Yumin Xia Date: Sun, 10 Mar 2019 00:46:32 -0800 Subject: [PATCH 094/259] libs/db: close batch (#3397) ClevelDB requires closing when WriteBatch is no longer needed, https://godoc.org/github.com/jmhodges/levigo#WriteBatch.Close Fixes the memory leak in https://github.com/cosmos/cosmos-sdk/issues/3842 --- libs/db/c_level_db.go | 5 +++++ libs/db/debug_db.go | 5 +++++ libs/db/go_level_db.go | 4 ++++ libs/db/mem_batch.go | 4 ++++ libs/db/prefix_db.go | 4 ++++ libs/db/remotedb/grpcdb/server.go | 1 + libs/db/remotedb/remotedb.go | 4 ++++ libs/db/types.go | 2 ++ lite/dbprovider.go | 1 + state/txindex/kv/kv.go | 2 ++ 10 files changed, 32 insertions(+) diff --git a/libs/db/c_level_db.go b/libs/db/c_level_db.go index 7f74b2a71..decb1af51 100644 --- a/libs/db/c_level_db.go +++ b/libs/db/c_level_db.go @@ -179,6 +179,11 @@ func (mBatch *cLevelDBBatch) WriteSync() { } } +// Implements Batch. +func (mBatch *cLevelDBBatch) Close() { + mBatch.batch.Close() +} + //---------------------------------------- // Iterator // NOTE This is almost identical to db/go_level_db.Iterator diff --git a/libs/db/debug_db.go b/libs/db/debug_db.go index bb361a266..658cd0555 100644 --- a/libs/db/debug_db.go +++ b/libs/db/debug_db.go @@ -250,3 +250,8 @@ func (dbch debugBatch) WriteSync() { fmt.Printf("%v.batch.WriteSync()\n", dbch.label) dbch.bch.WriteSync() } + +// Implements Batch. +func (dbch debugBatch) Close() { + dbch.bch.Close() +} diff --git a/libs/db/go_level_db.go b/libs/db/go_level_db.go index fd487a4dd..9a4358f60 100644 --- a/libs/db/go_level_db.go +++ b/libs/db/go_level_db.go @@ -184,6 +184,10 @@ func (mBatch *goLevelDBBatch) WriteSync() { } } +// Implements Batch. +// Close is no-op for goLevelDBBatch. +func (mBatch *goLevelDBBatch) Close() {} + //---------------------------------------- // Iterator // NOTE This is almost identical to db/c_level_db.Iterator diff --git a/libs/db/mem_batch.go b/libs/db/mem_batch.go index 5c5d0c13a..ebba43f54 100644 --- a/libs/db/mem_batch.go +++ b/libs/db/mem_batch.go @@ -46,6 +46,10 @@ func (mBatch *memBatch) WriteSync() { mBatch.write(true) } +func (mBatch *memBatch) Close() { + mBatch.ops = nil +} + func (mBatch *memBatch) write(doSync bool) { if mtx := mBatch.db.Mutex(); mtx != nil { mtx.Lock() diff --git a/libs/db/prefix_db.go b/libs/db/prefix_db.go index 40d72560c..0dd06ef9d 100644 --- a/libs/db/prefix_db.go +++ b/libs/db/prefix_db.go @@ -248,6 +248,10 @@ func (pb prefixBatch) WriteSync() { pb.source.WriteSync() } +func (pb prefixBatch) Close() { + pb.source.Close() +} + //---------------------------------------- // prefixIterator diff --git a/libs/db/remotedb/grpcdb/server.go b/libs/db/remotedb/grpcdb/server.go index 3a9955ddf..bfe65e610 100644 --- a/libs/db/remotedb/grpcdb/server.go +++ b/libs/db/remotedb/grpcdb/server.go @@ -180,6 +180,7 @@ func (s *server) BatchWriteSync(c context.Context, b *protodb.Batch) (*protodb.N func (s *server) batchWrite(c context.Context, b *protodb.Batch, sync bool) (*protodb.Nothing, error) { bat := s.db.NewBatch() + defer bat.Close() for _, op := range b.Ops { switch op.Type { case protodb.Operation_SET: diff --git a/libs/db/remotedb/remotedb.go b/libs/db/remotedb/remotedb.go index 2b60d8159..c70d54b9e 100644 --- a/libs/db/remotedb/remotedb.go +++ b/libs/db/remotedb/remotedb.go @@ -260,3 +260,7 @@ func (bat *batch) WriteSync() { panic(fmt.Sprintf("RemoteDB.BatchWriteSync: %v", err)) } } + +func (bat *batch) Close() { + bat.ops = nil +} diff --git a/libs/db/types.go b/libs/db/types.go index 9b9c6d0b9..30f8afd18 100644 --- a/libs/db/types.go +++ b/libs/db/types.go @@ -57,10 +57,12 @@ type DB interface { //---------------------------------------- // Batch +// Batch Close must be called when the program no longer needs the object. type Batch interface { SetDeleter Write() WriteSync() + Close() } type SetDeleter interface { diff --git a/lite/dbprovider.go b/lite/dbprovider.go index ef1b2a598..9a3636d57 100644 --- a/lite/dbprovider.go +++ b/lite/dbprovider.go @@ -54,6 +54,7 @@ func (dbp *DBProvider) SaveFullCommit(fc FullCommit) error { dbp.logger.Info("DBProvider.SaveFullCommit()...", "fc", fc) batch := dbp.db.NewBatch() + defer batch.Close() // Save the fc.validators. // We might be overwriting what we already have, but diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 93249b7f9..84208b8c1 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -78,6 +78,7 @@ func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { // AddBatch indexes a batch of transactions using the given list of tags. func (txi *TxIndex) AddBatch(b *txindex.Batch) error { storeBatch := txi.store.NewBatch() + defer storeBatch.Close() for _, result := range b.Ops { hash := result.Tx.Hash() @@ -109,6 +110,7 @@ func (txi *TxIndex) AddBatch(b *txindex.Batch) error { // Index indexes a single transaction using the given list of tags. func (txi *TxIndex) Index(result *types.TxResult) error { b := txi.store.NewBatch() + defer b.Close() hash := result.Tx.Hash() From f996b10f479d7c9a6d81cca5a02c47b29a52b3f3 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sun, 10 Mar 2019 13:06:34 +0400 Subject: [PATCH 095/259] update changelog and bump version to 0.30.2 --- CHANGELOG.md | 19 +++++++++++++++++++ version/version.go | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e8761aa..44ecdf388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v0.30.2 + +*March 10th, 2019* + +This release fixes a CLevelDB memory leak. It was happening because we were not +closing the WriteBatch object after use. See [levigo's +godoc](https://godoc.org/github.com/jmhodges/levigo#WriteBatch.Close) for the +Close method. Special thanks goes to @Stumble who both reported an issue in +[cosmos-sdk](https://github.com/cosmos/cosmos-sdk/issues/3842) and provided a +fix here. + +### BREAKING CHANGES: + +* Go API +- [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Add Close() method to Batch interface (@Stumble) + +### BUG FIXES: +- [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Fix CLevelDB memory leak (@Stumble) + ## v0.30.1 *February 20th, 2019* diff --git a/version/version.go b/version/version.go index 1f30978c1..1b0a36ae1 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.30.1" + TMCoreSemVer = "0.30.2" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From 100ff08de93ff1907bf810f584ec5bdc7a2a5260 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 11 Mar 2019 15:31:53 +0400 Subject: [PATCH 096/259] p2p: do not panic when filter times out (#3384) Fixes #3369 --- CHANGELOG_PENDING.md | 1 + p2p/switch.go | 9 ++++++++- p2p/switch_test.go | 39 +++++++++++++++++++++++++++++++++++++++ p2p/transport.go | 2 +- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 9ca5ab649..5120e2632 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -46,3 +46,4 @@ CI/CD: * [\#3396](https://github.com/tendermint/tendermint/pull/3396) - [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection - [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) +- [p2p] \#3369 do not panic when filter times out diff --git a/p2p/switch.go b/p2p/switch.go index ccd6d40f2..a07f70ce9 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -497,7 +497,14 @@ func (sw *Switch) acceptRoutine() { ) continue - case *ErrTransportClosed: + case ErrFilterTimeout: + sw.Logger.Error( + "Peer filter timed out", + "err", err, + ) + + continue + case ErrTransportClosed: sw.Logger.Error( "Stopped accept routine, as transport is closed", "numPeers", sw.peers.Size(), diff --git a/p2p/switch_test.go b/p2p/switch_test.go index 47cfed55f..d5dd178b6 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -2,6 +2,7 @@ package p2p import ( "bytes" + "errors" "fmt" "io" "io/ioutil" @@ -532,6 +533,44 @@ func TestSwitchAcceptRoutine(t *testing.T) { } } +type errorTransport struct { + acceptErr error +} + +func (et errorTransport) Accept(c peerConfig) (Peer, error) { + return nil, et.acceptErr +} +func (errorTransport) Dial(NetAddress, peerConfig) (Peer, error) { + panic("not implemented") +} +func (errorTransport) Cleanup(Peer) { + panic("not implemented") +} + +func TestSwitchAcceptRoutineErrorCases(t *testing.T) { + sw := NewSwitch(cfg, errorTransport{ErrFilterTimeout{}}) + assert.NotPanics(t, func() { + err := sw.Start() + assert.NoError(t, err) + sw.Stop() + }) + + sw = NewSwitch(cfg, errorTransport{ErrRejected{conn: nil, err: errors.New("filtered"), isFiltered: true}}) + assert.NotPanics(t, func() { + err := sw.Start() + assert.NoError(t, err) + sw.Stop() + }) + // TODO(melekes) check we remove our address from addrBook + + sw = NewSwitch(cfg, errorTransport{ErrTransportClosed{}}) + assert.NotPanics(t, func() { + err := sw.Start() + assert.NoError(t, err) + sw.Stop() + }) +} + func BenchmarkSwitchBroadcast(b *testing.B) { s1, s2 := MakeSwitchPair(b, func(i int, sw *Switch) *Switch { // Make bar reactors of bar channels each diff --git a/p2p/transport.go b/p2p/transport.go index d1bccf9b8..d36065ab1 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -175,7 +175,7 @@ func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) { return mt.wrapPeer(a.conn, a.nodeInfo, cfg, nil), nil case <-mt.closec: - return nil, &ErrTransportClosed{} + return nil, ErrTransportClosed{} } } From dc359bd3a51c52803a6af820a36ee41796284e87 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Mon, 11 Mar 2019 16:17:25 +0200 Subject: [PATCH 097/259] types: remove check for priority order of existing validators (#3407) When scaling and averaging is invoked, it is possible to have validators with close priorities ending up with same priority. With the current code, this makes it impossible to verify the priority orders before and after updates. Fixes #3383 --- types/validator_set_test.go | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/types/validator_set_test.go b/types/validator_set_test.go index 22f373c73..9fc2d346c 100644 --- a/types/validator_set_test.go +++ b/types/validator_set_test.go @@ -1113,15 +1113,6 @@ func applyChangesToValSet(t *testing.T, valSet *ValidatorSet, valsLists ...[]tes assert.NoError(t, err) } -func isAddressInList(address []byte, valsList []testVal) bool { - for _, val := range valsList { - if bytes.Equal([]byte(val.name), address) { - return true - } - } - return false -} - func TestValSetUpdatePriorityOrderTests(t *testing.T) { const nMaxElections = 5000 @@ -1206,25 +1197,6 @@ func verifyValSetUpdatePriorityOrder(t *testing.T, valSet *ValidatorSet, cfg tes assert.Equal(t, expectedPri, val.ProposerPriority) } } - - // check that the priority order for validators that remained is the same - // as in the original set - remainingValsPriSlice := updatedValsPriSorted[len(cfg.addedVals):] - - for len(remainingValsPriSlice) > 0 { - addressInChanged := remainingValsPriSlice[0].Address - addressInOld := origValsPriSorted[0].Address - - // skip validators in original list that have been removed - if isAddressInList(addressInOld, cfg.deletedVals) { - origValsPriSorted = origValsPriSorted[1:] - continue - } - assert.Equal(t, addressInOld, addressInChanged, "wrong priority order") - - remainingValsPriSlice = remainingValsPriSlice[1:] - origValsPriSorted = origValsPriSorted[1:] - } } //--------------------- From 15f621141dce76d992f6c8dcfbd4c522878b6108 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 11 Mar 2019 22:21:17 +0400 Subject: [PATCH 098/259] remove TimeIotaMs from ABCI consensus params (#3403) Also - init substructures to avoid panic in pb2tm.ConsensusParams Before: if csp.Block is nil and we later try to access/write to it, we'll panic. After: if csp.Block is nil and we later try to access/write to it, there'll be no panic. --- CHANGELOG_PENDING.md | 4 +- abci/types/types.pb.go | 410 ++++++++++++++++++----------------------- abci/types/types.proto | 2 - consensus/replay.go | 7 +- docs/spec/abci/abci.md | 2 - state/state_test.go | 7 +- types/params.go | 2 +- types/params_test.go | 9 +- types/protobuf.go | 16 +- types/protobuf_test.go | 2 + 10 files changed, 209 insertions(+), 252 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 5120e2632..29400929f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -25,7 +25,7 @@ Special thanks to external contributors on this release: mempool's current `txs_total_bytes` is exposed via `total_bytes` field in `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. - [config] \#2920 Remove `consensus.blocktime_iota` parameter -- [genesis] \#2920 Add `time_iota_ms` to block's consensus parameters +- [genesis] \#2920 Add `time_iota_ms` to block's consensus parameters (not exposed to the application) - [genesis] \#2920 Rename `consensus_params.block_size` to `consensus_params.block` ### IMPROVEMENTS: @@ -40,8 +40,6 @@ Special thanks to external contributors on this release: - leveldb.alivesnaps - leveldb.aliveiters -CI/CD: * [\#3396](https://github.com/tendermint/tendermint/pull/3396) - ### BUG FIXES: - [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 79af610c9..b09213a5f 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -61,7 +61,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{0} + return fileDescriptor_types_a177e47fab90f91d, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -483,7 +483,7 @@ func (m *RequestEcho) Reset() { *m = RequestEcho{} } func (m *RequestEcho) String() string { return proto.CompactTextString(m) } func (*RequestEcho) ProtoMessage() {} func (*RequestEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{1} + return fileDescriptor_types_a177e47fab90f91d, []int{1} } func (m *RequestEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -529,7 +529,7 @@ func (m *RequestFlush) Reset() { *m = RequestFlush{} } func (m *RequestFlush) String() string { return proto.CompactTextString(m) } func (*RequestFlush) ProtoMessage() {} func (*RequestFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{2} + return fileDescriptor_types_a177e47fab90f91d, []int{2} } func (m *RequestFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -571,7 +571,7 @@ func (m *RequestInfo) Reset() { *m = RequestInfo{} } func (m *RequestInfo) String() string { return proto.CompactTextString(m) } func (*RequestInfo) ProtoMessage() {} func (*RequestInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{3} + return fileDescriptor_types_a177e47fab90f91d, []int{3} } func (m *RequestInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,7 +634,7 @@ func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } func (*RequestSetOption) ProtoMessage() {} func (*RequestSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{4} + return fileDescriptor_types_a177e47fab90f91d, []int{4} } func (m *RequestSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -692,7 +692,7 @@ func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } func (*RequestInitChain) ProtoMessage() {} func (*RequestInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{5} + return fileDescriptor_types_a177e47fab90f91d, []int{5} } func (m *RequestInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -770,7 +770,7 @@ func (m *RequestQuery) Reset() { *m = RequestQuery{} } func (m *RequestQuery) String() string { return proto.CompactTextString(m) } func (*RequestQuery) ProtoMessage() {} func (*RequestQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{6} + return fileDescriptor_types_a177e47fab90f91d, []int{6} } func (m *RequestQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -841,7 +841,7 @@ func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } func (*RequestBeginBlock) ProtoMessage() {} func (*RequestBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{7} + return fileDescriptor_types_a177e47fab90f91d, []int{7} } func (m *RequestBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -909,7 +909,7 @@ func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } func (*RequestCheckTx) ProtoMessage() {} func (*RequestCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{8} + return fileDescriptor_types_a177e47fab90f91d, []int{8} } func (m *RequestCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -956,7 +956,7 @@ func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } func (*RequestDeliverTx) ProtoMessage() {} func (*RequestDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{9} + return fileDescriptor_types_a177e47fab90f91d, []int{9} } func (m *RequestDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1003,7 @@ func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } func (*RequestEndBlock) ProtoMessage() {} func (*RequestEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{10} + return fileDescriptor_types_a177e47fab90f91d, []int{10} } func (m *RequestEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1049,7 @@ func (m *RequestCommit) Reset() { *m = RequestCommit{} } func (m *RequestCommit) String() string { return proto.CompactTextString(m) } func (*RequestCommit) ProtoMessage() {} func (*RequestCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{11} + return fileDescriptor_types_a177e47fab90f91d, []int{11} } func (m *RequestCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1102,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{12} + return fileDescriptor_types_a177e47fab90f91d, []int{12} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1555,7 +1555,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{13} + return fileDescriptor_types_a177e47fab90f91d, []int{13} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1602,7 +1602,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{14} + return fileDescriptor_types_a177e47fab90f91d, []int{14} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1648,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{15} + return fileDescriptor_types_a177e47fab90f91d, []int{15} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1692,7 +1692,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{16} + return fileDescriptor_types_a177e47fab90f91d, []int{16} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1771,7 +1771,7 @@ func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } func (*ResponseSetOption) ProtoMessage() {} func (*ResponseSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{17} + return fileDescriptor_types_a177e47fab90f91d, []int{17} } func (m *ResponseSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1833,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{18} + return fileDescriptor_types_a177e47fab90f91d, []int{18} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1896,7 +1896,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{19} + return fileDescriptor_types_a177e47fab90f91d, []int{19} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1999,7 +1999,7 @@ func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } func (*ResponseBeginBlock) ProtoMessage() {} func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{20} + return fileDescriptor_types_a177e47fab90f91d, []int{20} } func (m *ResponseBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2053,7 +2053,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{21} + return fileDescriptor_types_a177e47fab90f91d, []int{21} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2156,7 +2156,7 @@ func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } func (*ResponseDeliverTx) ProtoMessage() {} func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{22} + return fileDescriptor_types_a177e47fab90f91d, []int{22} } func (m *ResponseDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2254,7 +2254,7 @@ func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } func (*ResponseEndBlock) ProtoMessage() {} func (*ResponseEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{23} + return fileDescriptor_types_a177e47fab90f91d, []int{23} } func (m *ResponseEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2316,7 +2316,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{24} + return fileDescriptor_types_a177e47fab90f91d, []int{24} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2367,7 +2367,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } func (*ConsensusParams) ProtoMessage() {} func (*ConsensusParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{25} + return fileDescriptor_types_a177e47fab90f91d, []int{25} } func (m *ConsensusParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2422,9 +2422,7 @@ type BlockParams struct { // Note: must be greater than 0 MaxBytes int64 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` // Note: must be greater or equal to -1 - MaxGas int64 `protobuf:"varint,2,opt,name=max_gas,json=maxGas,proto3" json:"max_gas,omitempty"` - // Note: must be greater than 0 - TimeIotaMs int64 `protobuf:"varint,3,opt,name=time_iota_ms,json=timeIotaMs,proto3" json:"time_iota_ms,omitempty"` + MaxGas int64 `protobuf:"varint,2,opt,name=max_gas,json=maxGas,proto3" json:"max_gas,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2434,7 +2432,7 @@ func (m *BlockParams) Reset() { *m = BlockParams{} } func (m *BlockParams) String() string { return proto.CompactTextString(m) } func (*BlockParams) ProtoMessage() {} func (*BlockParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{26} + return fileDescriptor_types_a177e47fab90f91d, []int{26} } func (m *BlockParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2477,13 +2475,6 @@ func (m *BlockParams) GetMaxGas() int64 { return 0 } -func (m *BlockParams) GetTimeIotaMs() int64 { - if m != nil { - return m.TimeIotaMs - } - return 0 -} - // EvidenceParams contains limits on the evidence. type EvidenceParams struct { // Note: must be greater than 0 @@ -2497,7 +2488,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } func (m *EvidenceParams) String() string { return proto.CompactTextString(m) } func (*EvidenceParams) ProtoMessage() {} func (*EvidenceParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{27} + return fileDescriptor_types_a177e47fab90f91d, []int{27} } func (m *EvidenceParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2545,7 +2536,7 @@ func (m *ValidatorParams) Reset() { *m = ValidatorParams{} } func (m *ValidatorParams) String() string { return proto.CompactTextString(m) } func (*ValidatorParams) ProtoMessage() {} func (*ValidatorParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{28} + return fileDescriptor_types_a177e47fab90f91d, []int{28} } func (m *ValidatorParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2593,7 +2584,7 @@ func (m *LastCommitInfo) Reset() { *m = LastCommitInfo{} } func (m *LastCommitInfo) String() string { return proto.CompactTextString(m) } func (*LastCommitInfo) ProtoMessage() {} func (*LastCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{29} + return fileDescriptor_types_a177e47fab90f91d, []int{29} } func (m *LastCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2667,7 +2658,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{30} + return fileDescriptor_types_a177e47fab90f91d, []int{30} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2820,7 +2811,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{31} + return fileDescriptor_types_a177e47fab90f91d, []int{31} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2875,7 +2866,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{32} + return fileDescriptor_types_a177e47fab90f91d, []int{32} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2930,7 +2921,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{33} + return fileDescriptor_types_a177e47fab90f91d, []int{33} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2987,7 +2978,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{34} + return fileDescriptor_types_a177e47fab90f91d, []int{34} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3043,7 +3034,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{35} + return fileDescriptor_types_a177e47fab90f91d, []int{35} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3099,7 +3090,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{36} + return fileDescriptor_types_a177e47fab90f91d, []int{36} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3154,7 +3145,7 @@ func (m *PubKey) Reset() { *m = PubKey{} } func (m *PubKey) String() string { return proto.CompactTextString(m) } func (*PubKey) ProtoMessage() {} func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{37} + return fileDescriptor_types_a177e47fab90f91d, []int{37} } func (m *PubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3212,7 +3203,7 @@ func (m *Evidence) Reset() { *m = Evidence{} } func (m *Evidence) String() string { return proto.CompactTextString(m) } func (*Evidence) ProtoMessage() {} func (*Evidence) Descriptor() ([]byte, []int) { - return fileDescriptor_types_e441973ce6650a0d, []int{38} + return fileDescriptor_types_a177e47fab90f91d, []int{38} } func (m *Evidence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -4816,9 +4807,6 @@ func (this *BlockParams) Equal(that interface{}) bool { if this.MaxGas != that1.MaxGas { return false } - if this.TimeIotaMs != that1.TimeIotaMs { - return false - } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { return false } @@ -7023,11 +7011,6 @@ func (m *BlockParams) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintTypes(dAtA, i, uint64(m.MaxGas)) } - if m.TimeIotaMs != 0 { - dAtA[i] = 0x18 - i++ - i = encodeVarintTypes(dAtA, i, uint64(m.TimeIotaMs)) - } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) } @@ -8150,12 +8133,8 @@ func NewPopulatedBlockParams(r randyTypes, easy bool) *BlockParams { if r.Intn(2) == 0 { this.MaxGas *= -1 } - this.TimeIotaMs = int64(r.Int63()) - if r.Intn(2) == 0 { - this.TimeIotaMs *= -1 - } if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedTypes(r, 4) + this.XXX_unrecognized = randUnrecognizedTypes(r, 3) } return this } @@ -9335,9 +9314,6 @@ func (m *BlockParams) Size() (n int) { if m.MaxGas != 0 { n += 1 + sovTypes(uint64(m.MaxGas)) } - if m.TimeIotaMs != 0 { - n += 1 + sovTypes(uint64(m.TimeIotaMs)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -13604,25 +13580,6 @@ func (m *BlockParams) Unmarshal(dAtA []byte) error { break } } - case 3: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field TimeIotaMs", wireType) - } - m.TimeIotaMs = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.TimeIotaMs |= (int64(b) & 0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15400,150 +15357,149 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_e441973ce6650a0d) } +func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_a177e47fab90f91d) } func init() { - golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_e441973ce6650a0d) + golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_a177e47fab90f91d) } -var fileDescriptor_types_e441973ce6650a0d = []byte{ - // 2223 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcd, 0x73, 0x1c, 0x47, - 0x15, 0xd7, 0xec, 0xf7, 0xbc, 0xd5, 0x7e, 0xa4, 0x2d, 0xdb, 0xeb, 0x25, 0x48, 0xae, 0x31, 0x24, - 0x12, 0x51, 0x56, 0x89, 0x82, 0x29, 0x39, 0x0e, 0x54, 0x69, 0x6d, 0x83, 0x54, 0x49, 0x40, 0x8c, - 0x6d, 0x71, 0xa1, 0x6a, 0xaa, 0x77, 0xa7, 0xb5, 0x3b, 0xa5, 0xdd, 0x99, 0xc9, 0x4c, 0xaf, 0xb2, - 0xe2, 0xc8, 0x39, 0x87, 0x1c, 0xf8, 0x13, 0x38, 0xf0, 0x27, 0xe4, 0xc8, 0x89, 0xca, 0x91, 0x03, - 0x67, 0x03, 0xa2, 0x38, 0xc0, 0x95, 0xa2, 0x8a, 0x23, 0xd5, 0xaf, 0x7b, 0x3e, 0x35, 0x6b, 0xe2, - 0xc0, 0x89, 0xcb, 0x6e, 0xf7, 0xfb, 0xe8, 0x8f, 0x37, 0xef, 0xbd, 0xdf, 0x7b, 0x0d, 0xb7, 0xe8, - 0x68, 0xec, 0xec, 0xf1, 0x4b, 0x9f, 0x85, 0xf2, 0x77, 0xe0, 0x07, 0x1e, 0xf7, 0x48, 0x15, 0x27, - 0xfd, 0xb7, 0x27, 0x0e, 0x9f, 0x2e, 0x46, 0x83, 0xb1, 0x37, 0xdf, 0x9b, 0x78, 0x13, 0x6f, 0x0f, - 0xb9, 0xa3, 0xc5, 0x19, 0xce, 0x70, 0x82, 0x23, 0xa9, 0xd5, 0x7f, 0x98, 0x12, 0xe7, 0xcc, 0xb5, - 0x59, 0x30, 0x77, 0x5c, 0x9e, 0x1e, 0x8e, 0x83, 0x4b, 0x9f, 0x7b, 0x7b, 0x73, 0x16, 0x9c, 0xcf, - 0x98, 0xfa, 0x53, 0xca, 0x07, 0xff, 0x51, 0x79, 0xe6, 0x8c, 0xc2, 0xbd, 0xb1, 0x37, 0x9f, 0x7b, - 0x6e, 0xfa, 0xb0, 0xfd, 0xad, 0x89, 0xe7, 0x4d, 0x66, 0x2c, 0x39, 0x1c, 0x77, 0xe6, 0x2c, 0xe4, - 0x74, 0xee, 0x4b, 0x01, 0xe3, 0x77, 0x15, 0xa8, 0x9b, 0xec, 0x93, 0x05, 0x0b, 0x39, 0xd9, 0x86, - 0x0a, 0x1b, 0x4f, 0xbd, 0x5e, 0xe9, 0xae, 0xb6, 0xdd, 0xdc, 0x27, 0x03, 0xb9, 0x90, 0xe2, 0x3e, - 0x19, 0x4f, 0xbd, 0xa3, 0x35, 0x13, 0x25, 0xc8, 0x5b, 0x50, 0x3d, 0x9b, 0x2d, 0xc2, 0x69, 0xaf, - 0x8c, 0xa2, 0x37, 0xb2, 0xa2, 0x3f, 0x14, 0xac, 0xa3, 0x35, 0x53, 0xca, 0x88, 0x65, 0x1d, 0xf7, - 0xcc, 0xeb, 0x55, 0x8a, 0x96, 0x3d, 0x76, 0xcf, 0x70, 0x59, 0x21, 0x41, 0x0e, 0x00, 0x42, 0xc6, - 0x2d, 0xcf, 0xe7, 0x8e, 0xe7, 0xf6, 0xaa, 0x28, 0x7f, 0x3b, 0x2b, 0xff, 0x94, 0xf1, 0x9f, 0x20, - 0xfb, 0x68, 0xcd, 0xd4, 0xc3, 0x68, 0x22, 0x34, 0x1d, 0xd7, 0xe1, 0xd6, 0x78, 0x4a, 0x1d, 0xb7, - 0x57, 0x2b, 0xd2, 0x3c, 0x76, 0x1d, 0xfe, 0x48, 0xb0, 0x85, 0xa6, 0x13, 0x4d, 0xc4, 0x55, 0x3e, - 0x59, 0xb0, 0xe0, 0xb2, 0x57, 0x2f, 0xba, 0xca, 0x4f, 0x05, 0x4b, 0x5c, 0x05, 0x65, 0xc8, 0x43, - 0x68, 0x8e, 0xd8, 0xc4, 0x71, 0xad, 0xd1, 0xcc, 0x1b, 0x9f, 0xf7, 0x1a, 0xa8, 0xd2, 0xcb, 0xaa, - 0x0c, 0x85, 0xc0, 0x50, 0xf0, 0x8f, 0xd6, 0x4c, 0x18, 0xc5, 0x33, 0xb2, 0x0f, 0x8d, 0xf1, 0x94, - 0x8d, 0xcf, 0x2d, 0xbe, 0xec, 0xe9, 0xa8, 0x79, 0x33, 0xab, 0xf9, 0x48, 0x70, 0x9f, 0x2d, 0x8f, - 0xd6, 0xcc, 0xfa, 0x58, 0x0e, 0xc9, 0x7d, 0xd0, 0x99, 0x6b, 0xab, 0xed, 0x9a, 0xa8, 0x74, 0x2b, - 0xf7, 0x5d, 0x5c, 0x3b, 0xda, 0xac, 0xc1, 0xd4, 0x98, 0x0c, 0xa0, 0x26, 0x9c, 0xc1, 0xe1, 0xbd, - 0x75, 0xd4, 0xd9, 0xc8, 0x6d, 0x84, 0xbc, 0xa3, 0x35, 0x53, 0x49, 0x09, 0xf3, 0xd9, 0x6c, 0xe6, - 0x5c, 0xb0, 0x40, 0x1c, 0xee, 0x46, 0x91, 0xf9, 0x1e, 0x4b, 0x3e, 0x1e, 0x4f, 0xb7, 0xa3, 0xc9, - 0xb0, 0x0e, 0xd5, 0x0b, 0x3a, 0x5b, 0x30, 0xe3, 0x4d, 0x68, 0xa6, 0x3c, 0x85, 0xf4, 0xa0, 0x3e, - 0x67, 0x61, 0x48, 0x27, 0xac, 0xa7, 0xdd, 0xd5, 0xb6, 0x75, 0x33, 0x9a, 0x1a, 0x6d, 0x58, 0x4f, - 0xfb, 0x89, 0x31, 0x8f, 0x15, 0x85, 0x2f, 0x08, 0xc5, 0x0b, 0x16, 0x84, 0xc2, 0x01, 0x94, 0xa2, - 0x9a, 0x92, 0x7b, 0xd0, 0x42, 0x3b, 0x58, 0x11, 0x5f, 0xf8, 0x69, 0xc5, 0x5c, 0x47, 0xe2, 0xa9, - 0x12, 0xda, 0x82, 0xa6, 0xbf, 0xef, 0xc7, 0x22, 0x65, 0x14, 0x01, 0x7f, 0xdf, 0x57, 0x02, 0xc6, - 0xfb, 0xd0, 0xcd, 0xbb, 0x12, 0xe9, 0x42, 0xf9, 0x9c, 0x5d, 0xaa, 0xfd, 0xc4, 0x90, 0x6c, 0xa8, - 0x6b, 0xe1, 0x1e, 0xba, 0xa9, 0xee, 0xf8, 0x79, 0x29, 0x56, 0x8e, 0xbd, 0x89, 0x1c, 0x40, 0x45, - 0x04, 0x15, 0x6a, 0x37, 0xf7, 0xfb, 0x03, 0x19, 0x71, 0x83, 0x28, 0xe2, 0x06, 0xcf, 0xa2, 0x88, - 0x1b, 0x36, 0xbe, 0x7c, 0xb1, 0xb5, 0xf6, 0xf9, 0x1f, 0xb7, 0x34, 0x13, 0x35, 0xc8, 0x1d, 0xe1, - 0x10, 0xd4, 0x71, 0x2d, 0xc7, 0x56, 0xfb, 0xd4, 0x71, 0x7e, 0x6c, 0x93, 0x43, 0xe8, 0x8e, 0x3d, - 0x37, 0x64, 0x6e, 0xb8, 0x08, 0x2d, 0x9f, 0x06, 0x74, 0x1e, 0xaa, 0x58, 0x8b, 0x3e, 0xff, 0xa3, - 0x88, 0x7d, 0x82, 0x5c, 0xb3, 0x33, 0xce, 0x12, 0xc8, 0x07, 0x00, 0x17, 0x74, 0xe6, 0xd8, 0x94, - 0x7b, 0x41, 0xd8, 0xab, 0xdc, 0x2d, 0xa7, 0x94, 0x4f, 0x23, 0xc6, 0x73, 0xdf, 0xa6, 0x9c, 0x0d, - 0x2b, 0xe2, 0x64, 0x66, 0x4a, 0x9e, 0xbc, 0x01, 0x1d, 0xea, 0xfb, 0x56, 0xc8, 0x29, 0x67, 0xd6, - 0xe8, 0x92, 0xb3, 0x10, 0xe3, 0x71, 0xdd, 0x6c, 0x51, 0xdf, 0x7f, 0x2a, 0xa8, 0x43, 0x41, 0x34, - 0xec, 0xf8, 0x6b, 0x62, 0xa8, 0x10, 0x02, 0x15, 0x9b, 0x72, 0x8a, 0xd6, 0x58, 0x37, 0x71, 0x2c, - 0x68, 0x3e, 0xe5, 0x53, 0x75, 0x47, 0x1c, 0x93, 0x5b, 0x50, 0x9b, 0x32, 0x67, 0x32, 0xe5, 0x78, - 0xad, 0xb2, 0xa9, 0x66, 0xc2, 0xf0, 0x7e, 0xe0, 0x5d, 0x30, 0xcc, 0x16, 0x0d, 0x53, 0x4e, 0x8c, - 0xbf, 0x6a, 0xf0, 0xda, 0xb5, 0xf0, 0x12, 0xeb, 0x4e, 0x69, 0x38, 0x8d, 0xf6, 0x12, 0x63, 0xf2, - 0x96, 0x58, 0x97, 0xda, 0x2c, 0x50, 0x59, 0xac, 0xa5, 0x6e, 0x7c, 0x84, 0x44, 0x75, 0x51, 0x25, - 0x42, 0x9e, 0x40, 0x77, 0x46, 0x43, 0x6e, 0xc9, 0x28, 0xb0, 0x30, 0x4b, 0x95, 0x33, 0x91, 0xf9, - 0x11, 0x8d, 0xa2, 0x45, 0x38, 0xa7, 0x52, 0x6f, 0xcf, 0x32, 0x54, 0x72, 0x04, 0x1b, 0xa3, 0xcb, - 0x5f, 0x50, 0x97, 0x3b, 0x2e, 0xb3, 0xae, 0xd9, 0xbc, 0xa3, 0x96, 0x7a, 0x72, 0xe1, 0xd8, 0xcc, - 0x1d, 0x47, 0xc6, 0xbe, 0x11, 0xab, 0xc4, 0x1f, 0x23, 0x34, 0xee, 0x42, 0x3b, 0x9b, 0x0b, 0x48, - 0x1b, 0x4a, 0x7c, 0xa9, 0x6e, 0x58, 0xe2, 0x4b, 0xc3, 0x88, 0x3d, 0x30, 0x0e, 0xc8, 0x6b, 0x32, - 0x3b, 0xd0, 0xc9, 0x25, 0x87, 0x94, 0xb9, 0xb5, 0xb4, 0xb9, 0x8d, 0x0e, 0xb4, 0x32, 0x39, 0xc1, - 0xf8, 0xac, 0x0a, 0x0d, 0x93, 0x85, 0xbe, 0x70, 0x26, 0x72, 0x00, 0x3a, 0x5b, 0x8e, 0x99, 0x4c, - 0xc7, 0x5a, 0x2e, 0xd9, 0x49, 0x99, 0x27, 0x11, 0x5f, 0xa4, 0x85, 0x58, 0x98, 0xec, 0x64, 0xa0, - 0xe4, 0x46, 0x5e, 0x29, 0x8d, 0x25, 0xbb, 0x59, 0x2c, 0xd9, 0xc8, 0xc9, 0xe6, 0xc0, 0x64, 0x27, - 0x03, 0x26, 0xf9, 0x85, 0x33, 0x68, 0xf2, 0xa0, 0x00, 0x4d, 0xf2, 0xc7, 0x5f, 0x01, 0x27, 0x0f, - 0x0a, 0xe0, 0xa4, 0x77, 0x6d, 0xaf, 0x42, 0x3c, 0xd9, 0xcd, 0xe2, 0x49, 0xfe, 0x3a, 0x39, 0x40, - 0xf9, 0xa0, 0x08, 0x50, 0xee, 0xe4, 0x74, 0x56, 0x22, 0xca, 0x7b, 0xd7, 0x10, 0xe5, 0x56, 0x4e, - 0xb5, 0x00, 0x52, 0x1e, 0x64, 0x72, 0x3d, 0x14, 0xde, 0xad, 0x38, 0xd9, 0x93, 0xef, 0x5d, 0x47, - 0xa3, 0xdb, 0xf9, 0x4f, 0x5b, 0x04, 0x47, 0x7b, 0x39, 0x38, 0xba, 0x99, 0x3f, 0x65, 0x0e, 0x8f, - 0x12, 0x54, 0xd9, 0x11, 0x71, 0x9f, 0xf3, 0x34, 0x91, 0x23, 0x58, 0x10, 0x78, 0x81, 0x4a, 0xd8, - 0x72, 0x62, 0x6c, 0x8b, 0x4c, 0x94, 0xf8, 0xd7, 0x4b, 0x10, 0x08, 0x9d, 0x3e, 0xe5, 0x5d, 0xc6, - 0x17, 0x5a, 0xa2, 0x8b, 0x11, 0x9d, 0xce, 0x62, 0xba, 0xca, 0x62, 0x29, 0x60, 0x2a, 0x65, 0x81, - 0x69, 0x0b, 0x9a, 0x22, 0x57, 0xe6, 0x30, 0x87, 0xfa, 0x11, 0xe6, 0x90, 0xef, 0xc0, 0x6b, 0x98, - 0x67, 0x24, 0x7c, 0xa9, 0x40, 0xac, 0x60, 0x20, 0x76, 0x04, 0x43, 0x5a, 0x4c, 0x26, 0xc0, 0xb7, - 0xe1, 0x46, 0x4a, 0x56, 0xac, 0x8b, 0x39, 0x4e, 0x26, 0xdf, 0x6e, 0x2c, 0x7d, 0xe8, 0xfb, 0x47, - 0x34, 0x9c, 0x1a, 0x1f, 0x27, 0x06, 0x4a, 0xf0, 0x8c, 0x40, 0x65, 0xec, 0xd9, 0xf2, 0xde, 0x2d, - 0x13, 0xc7, 0x02, 0xe3, 0x66, 0xde, 0x04, 0x0f, 0xa7, 0x9b, 0x62, 0x28, 0xa4, 0xe2, 0x50, 0xd2, - 0x65, 0xcc, 0x18, 0xbf, 0xd2, 0x92, 0xf5, 0x12, 0x88, 0x2b, 0x42, 0x23, 0xed, 0xbf, 0x41, 0xa3, - 0xd2, 0xab, 0xa1, 0x91, 0x71, 0xa5, 0x25, 0x9f, 0x2c, 0xc6, 0x99, 0xaf, 0x77, 0x45, 0xe1, 0x3d, - 0x8e, 0x6b, 0xb3, 0x25, 0x9a, 0xb4, 0x6c, 0xca, 0x49, 0x54, 0x02, 0xd4, 0xd0, 0xcc, 0xd9, 0x12, - 0xa0, 0x8e, 0x34, 0x39, 0x21, 0xf7, 0x10, 0x9f, 0xbc, 0x33, 0x15, 0xaa, 0xad, 0x81, 0x2a, 0xd4, - 0x4f, 0x04, 0xd1, 0x94, 0xbc, 0x54, 0xb6, 0xd5, 0x33, 0xe0, 0xf6, 0x3a, 0xe8, 0xe2, 0xa0, 0xa1, - 0x4f, 0xc7, 0x0c, 0x23, 0x4f, 0x37, 0x13, 0x82, 0x71, 0x02, 0xe4, 0x7a, 0xc4, 0x93, 0xf7, 0xa1, - 0xc2, 0xe9, 0x44, 0xd8, 0x5b, 0x98, 0xac, 0x3d, 0x90, 0x45, 0xfe, 0xe0, 0xc3, 0xd3, 0x13, 0xea, - 0x04, 0xc3, 0x5b, 0xc2, 0x54, 0x7f, 0x7f, 0xb1, 0xd5, 0x16, 0x32, 0xbb, 0xde, 0xdc, 0xe1, 0x6c, - 0xee, 0xf3, 0x4b, 0x13, 0x75, 0x8c, 0x7f, 0x68, 0x02, 0x09, 0x32, 0x99, 0xa0, 0xd0, 0x70, 0x91, - 0xbb, 0x97, 0x52, 0xa0, 0xfd, 0xd5, 0x8c, 0xf9, 0x4d, 0x80, 0x09, 0x0d, 0xad, 0x4f, 0xa9, 0xcb, - 0x99, 0xad, 0x2c, 0xaa, 0x4f, 0x68, 0xf8, 0x33, 0x24, 0x88, 0x0a, 0x47, 0xb0, 0x17, 0x21, 0xb3, - 0xd1, 0xb4, 0x65, 0xb3, 0x3e, 0xa1, 0xe1, 0xf3, 0x90, 0xd9, 0xf1, 0xbd, 0xea, 0xaf, 0x7e, 0xaf, - 0xac, 0x1d, 0x1b, 0x79, 0x3b, 0xfe, 0x33, 0xe5, 0xc3, 0x09, 0x48, 0xfe, 0xff, 0xdf, 0xfb, 0x6f, - 0x9a, 0xa8, 0x0d, 0xb2, 0x69, 0x98, 0x1c, 0xc3, 0x6b, 0x71, 0x1c, 0x59, 0x0b, 0x8c, 0xaf, 0xc8, - 0x97, 0x5e, 0x1e, 0x7e, 0xdd, 0x8b, 0x2c, 0x39, 0x24, 0x3f, 0x86, 0xdb, 0xb9, 0x2c, 0x10, 0x2f, - 0x58, 0x7a, 0x69, 0x32, 0xb8, 0x99, 0x4d, 0x06, 0xd1, 0x7a, 0x91, 0x25, 0xca, 0x5f, 0xc3, 0xb3, - 0xbf, 0x25, 0x0a, 0xa5, 0x34, 0x78, 0x14, 0x7d, 0x4b, 0xe3, 0xd7, 0x1a, 0x74, 0x72, 0x87, 0x21, - 0xdb, 0x50, 0x95, 0xf8, 0xa5, 0x65, 0xda, 0x51, 0xb4, 0x96, 0x3a, 0xaf, 0x14, 0x20, 0xef, 0x42, - 0x83, 0xa9, 0x9a, 0x4d, 0x5d, 0xf0, 0x66, 0xae, 0x94, 0x53, 0xf2, 0xb1, 0x18, 0xf9, 0x2e, 0xe8, - 0xb1, 0xd9, 0x72, 0xf5, 0x7a, 0x6c, 0x65, 0xa5, 0x94, 0x08, 0x1a, 0x0c, 0x9a, 0xa9, 0xed, 0xc9, - 0x37, 0x40, 0x9f, 0xd3, 0xa5, 0x2a, 0xba, 0x65, 0xb9, 0xd6, 0x98, 0xd3, 0x25, 0xd6, 0xdb, 0xe4, - 0x36, 0xd4, 0x05, 0x73, 0x42, 0xa5, 0xd1, 0xcb, 0x66, 0x6d, 0x4e, 0x97, 0x3f, 0xa2, 0x21, 0xb9, - 0x0b, 0xeb, 0xa2, 0xa9, 0xb0, 0x1c, 0x8f, 0x53, 0x4b, 0x75, 0x0b, 0x65, 0x13, 0x04, 0xed, 0xd8, - 0xe3, 0xf4, 0xe3, 0xd0, 0xd8, 0x81, 0x76, 0xf6, 0xe0, 0xd1, 0x62, 0x11, 0x44, 0xca, 0xc5, 0x0e, - 0x27, 0xcc, 0xb8, 0x0f, 0x9d, 0xdc, 0x79, 0x89, 0x01, 0x2d, 0x7f, 0x31, 0xb2, 0xce, 0xd9, 0xa5, - 0x85, 0x17, 0x42, 0x27, 0xd2, 0xcd, 0xa6, 0xbf, 0x18, 0x7d, 0xc8, 0x2e, 0x9f, 0x09, 0x92, 0xf1, - 0x14, 0xda, 0xd9, 0x82, 0x59, 0x24, 0xd1, 0xc0, 0x5b, 0xb8, 0x36, 0xae, 0x5f, 0x35, 0xe5, 0x44, - 0xf4, 0xdc, 0x17, 0x9e, 0xf4, 0x9b, 0x74, 0x85, 0x7c, 0xea, 0x71, 0x96, 0x2a, 0xb3, 0xa5, 0x8c, - 0xf1, 0xcb, 0x2a, 0xd4, 0x64, 0xf5, 0x4e, 0x06, 0xd9, 0xde, 0x50, 0x38, 0x8d, 0xd2, 0x94, 0x54, - 0xa5, 0x18, 0x03, 0xf3, 0x1b, 0xf9, 0x06, 0x6b, 0xd8, 0xbc, 0x7a, 0xb1, 0x55, 0x47, 0x50, 0x3b, - 0x7e, 0x9c, 0x74, 0x5b, 0xab, 0x9a, 0x91, 0xa8, 0xb5, 0xab, 0xbc, 0x72, 0x6b, 0x77, 0x1b, 0xea, - 0xee, 0x62, 0x6e, 0xf1, 0x65, 0xa8, 0x92, 0x43, 0xcd, 0x5d, 0xcc, 0x9f, 0x2d, 0xf1, 0xe3, 0x72, - 0x8f, 0xd3, 0x19, 0xb2, 0x64, 0x6a, 0x68, 0x20, 0x41, 0x30, 0x0f, 0xa0, 0x95, 0xc2, 0x7e, 0xc7, - 0x56, 0x35, 0x64, 0x3b, 0xed, 0xa3, 0xc7, 0x8f, 0xd5, 0x2d, 0x9b, 0x71, 0x2d, 0x70, 0x6c, 0x93, - 0xed, 0x6c, 0x27, 0x83, 0x25, 0x43, 0x03, 0x23, 0x21, 0xd5, 0xac, 0x88, 0x82, 0x41, 0x1c, 0x40, - 0xc4, 0x86, 0x14, 0xd1, 0x51, 0xa4, 0x21, 0x08, 0xc8, 0x7c, 0x13, 0x3a, 0x09, 0xea, 0x4a, 0x11, - 0x90, 0xab, 0x24, 0x64, 0x14, 0x7c, 0x07, 0x36, 0x5c, 0xb6, 0xe4, 0x56, 0x5e, 0xba, 0x89, 0xd2, - 0x44, 0xf0, 0x4e, 0xb3, 0x1a, 0xdf, 0x86, 0x76, 0x92, 0x3d, 0x50, 0x76, 0x5d, 0xf6, 0x93, 0x31, - 0x15, 0xc5, 0xee, 0x40, 0x23, 0xae, 0x79, 0x5a, 0x28, 0x50, 0xa7, 0xb2, 0xd4, 0x89, 0xab, 0xa8, - 0x80, 0x85, 0x8b, 0x19, 0x57, 0x8b, 0xb4, 0x51, 0x06, 0xab, 0x28, 0x53, 0xd2, 0x51, 0xf6, 0x1e, - 0xb4, 0xa2, 0xa0, 0x94, 0x72, 0x1d, 0x94, 0x5b, 0x8f, 0x88, 0x28, 0xb4, 0x03, 0x5d, 0x3f, 0xf0, - 0x7c, 0x2f, 0x64, 0x81, 0x45, 0x6d, 0x3b, 0x60, 0x61, 0xd8, 0xeb, 0xca, 0xf5, 0x22, 0xfa, 0xa1, - 0x24, 0x1b, 0xef, 0x42, 0x3d, 0x2a, 0xe6, 0x36, 0xa0, 0x3a, 0x8c, 0x13, 0x48, 0xc5, 0x94, 0x13, - 0x01, 0x1b, 0x87, 0xbe, 0xaf, 0x9e, 0x24, 0xc4, 0xd0, 0xf8, 0x39, 0xd4, 0xd5, 0x07, 0x2b, 0x6c, - 0x54, 0xbf, 0x0f, 0xeb, 0x3e, 0x0d, 0xc4, 0x35, 0xd2, 0xed, 0x6a, 0xd4, 0x2e, 0x9c, 0xd0, 0x80, - 0x3f, 0x65, 0x3c, 0xd3, 0xb5, 0x36, 0x51, 0x5e, 0x92, 0x8c, 0x07, 0xd0, 0xca, 0xc8, 0x88, 0x63, - 0xa1, 0x1f, 0x45, 0x91, 0x86, 0x93, 0x78, 0xe7, 0x52, 0xb2, 0xb3, 0xf1, 0x10, 0xf4, 0xf8, 0xdb, - 0x88, 0xaa, 0x36, 0xba, 0xba, 0xa6, 0xcc, 0x2d, 0xa7, 0xd8, 0x89, 0x7b, 0x9f, 0xb2, 0x40, 0xc5, - 0x84, 0x9c, 0x18, 0xcf, 0x53, 0x99, 0x41, 0x26, 0x72, 0xb2, 0x0b, 0x75, 0x95, 0x19, 0x54, 0x54, - 0x46, 0x3d, 0xf7, 0x09, 0xa6, 0x86, 0xa8, 0xe7, 0x96, 0x89, 0x22, 0x59, 0xb6, 0x94, 0x5e, 0x76, - 0x06, 0x8d, 0x28, 0xfa, 0xb3, 0x49, 0x54, 0xae, 0xd8, 0xcd, 0x27, 0x51, 0xb5, 0x68, 0x22, 0x28, - 0xbc, 0x23, 0x74, 0x26, 0x2e, 0xb3, 0xad, 0x24, 0x84, 0x70, 0x8f, 0x86, 0xd9, 0x91, 0x8c, 0x8f, - 0xa2, 0x78, 0x31, 0xde, 0x81, 0x9a, 0x3c, 0x9b, 0xb0, 0x8f, 0x58, 0x39, 0x2a, 0xf4, 0xc5, 0xb8, - 0x10, 0x49, 0xfe, 0xa0, 0x41, 0x23, 0x4a, 0x9e, 0x85, 0x4a, 0x99, 0x43, 0x97, 0xbe, 0xea, 0xa1, - 0xff, 0xf7, 0x89, 0x67, 0x17, 0x88, 0xcc, 0x2f, 0x17, 0x1e, 0x77, 0xdc, 0x89, 0x25, 0x6d, 0x2d, - 0x73, 0x50, 0x17, 0x39, 0xa7, 0xc8, 0x38, 0x11, 0xf4, 0xfd, 0xcf, 0xaa, 0xd0, 0x39, 0x1c, 0x3e, - 0x3a, 0x3e, 0xf4, 0xfd, 0x99, 0x33, 0xa6, 0xd8, 0x3c, 0xec, 0x41, 0x05, 0xfb, 0xa7, 0x82, 0xf7, - 0xdf, 0x7e, 0x51, 0x23, 0x4f, 0xf6, 0xa1, 0x8a, 0x6d, 0x14, 0x29, 0x7a, 0x06, 0xee, 0x17, 0xf6, - 0xf3, 0x62, 0x13, 0xd9, 0x68, 0x5d, 0x7f, 0x0d, 0xee, 0x17, 0x35, 0xf5, 0xe4, 0x07, 0xa0, 0x27, - 0xfd, 0xcd, 0xaa, 0x37, 0xe1, 0xfe, 0xca, 0xf6, 0x5e, 0xe8, 0x27, 0xb5, 0xe0, 0xaa, 0xa7, 0xcd, - 0xfe, 0xca, 0x3e, 0x98, 0x1c, 0x40, 0x3d, 0xaa, 0xa0, 0x8b, 0x5f, 0x6d, 0xfb, 0x2b, 0x5a, 0x6f, - 0x61, 0x1e, 0xd9, 0xb2, 0x14, 0x3d, 0x2d, 0xf7, 0x0b, 0xdf, 0x07, 0xc8, 0x7d, 0xa8, 0xa9, 0xb2, - 0xa6, 0xf0, 0xe5, 0xb6, 0x5f, 0xdc, 0x40, 0x8b, 0x4b, 0x26, 0x4d, 0xdb, 0xaa, 0xe7, 0xef, 0xfe, - 0xca, 0x87, 0x0c, 0x72, 0x08, 0x90, 0xea, 0x3c, 0x56, 0xbe, 0x6b, 0xf7, 0x57, 0x3f, 0x50, 0x90, - 0x87, 0xd0, 0x48, 0x1e, 0x9d, 0x8a, 0x5f, 0xaa, 0xfb, 0xab, 0xde, 0x0c, 0x86, 0xaf, 0xff, 0xeb, - 0xcf, 0x9b, 0xda, 0x6f, 0xae, 0x36, 0xb5, 0x2f, 0xae, 0x36, 0xb5, 0x2f, 0xaf, 0x36, 0xb5, 0xdf, - 0x5f, 0x6d, 0x6a, 0x7f, 0xba, 0xda, 0xd4, 0x7e, 0xfb, 0x97, 0x4d, 0x6d, 0x54, 0x43, 0xf7, 0x7f, - 0xef, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x85, 0xdc, 0x74, 0x8d, 0x99, 0x19, 0x00, 0x00, +var fileDescriptor_types_a177e47fab90f91d = []byte{ + // 2203 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcf, 0x73, 0x1c, 0x47, + 0xf5, 0xd7, 0xec, 0xef, 0x79, 0xab, 0xfd, 0xe1, 0xb6, 0x6c, 0xaf, 0xf7, 0x9b, 0xaf, 0xe4, 0x1a, + 0x43, 0x22, 0x11, 0x67, 0x95, 0x28, 0x98, 0x92, 0xe3, 0x40, 0x95, 0x56, 0x36, 0x48, 0x95, 0x00, + 0x62, 0x6c, 0x8b, 0x0b, 0x55, 0x53, 0xbd, 0x3b, 0xad, 0xdd, 0x29, 0xed, 0xce, 0x4c, 0x66, 0x7a, + 0x95, 0x15, 0x47, 0xce, 0x39, 0xe4, 0xc0, 0x9f, 0xc0, 0x81, 0x3f, 0x21, 0x47, 0x4e, 0x54, 0x8e, + 0x1c, 0x38, 0x1b, 0x10, 0xc5, 0x01, 0xae, 0x14, 0x55, 0x1c, 0xa9, 0x7e, 0xdd, 0xf3, 0x53, 0xb3, + 0x26, 0x0e, 0x9c, 0xb8, 0x48, 0xd3, 0xfd, 0x3e, 0xaf, 0x7f, 0xbc, 0x7d, 0xef, 0x7d, 0xde, 0x6b, + 0xb8, 0x4d, 0x47, 0x63, 0x67, 0x97, 0x5f, 0xfa, 0x2c, 0x94, 0x7f, 0x07, 0x7e, 0xe0, 0x71, 0x8f, + 0x54, 0x71, 0xd0, 0x7f, 0x67, 0xe2, 0xf0, 0xe9, 0x62, 0x34, 0x18, 0x7b, 0xf3, 0xdd, 0x89, 0x37, + 0xf1, 0x76, 0x51, 0x3a, 0x5a, 0x9c, 0xe1, 0x08, 0x07, 0xf8, 0x25, 0xb5, 0xfa, 0x8f, 0x53, 0x70, + 0xce, 0x5c, 0x9b, 0x05, 0x73, 0xc7, 0xe5, 0xe9, 0xcf, 0x71, 0x70, 0xe9, 0x73, 0x6f, 0x77, 0xce, + 0x82, 0xf3, 0x19, 0x53, 0xff, 0x94, 0xf2, 0xfe, 0xbf, 0x55, 0x9e, 0x39, 0xa3, 0x70, 0x77, 0xec, + 0xcd, 0xe7, 0x9e, 0x9b, 0x3e, 0x6c, 0x7f, 0x6b, 0xe2, 0x79, 0x93, 0x19, 0x4b, 0x0e, 0xc7, 0x9d, + 0x39, 0x0b, 0x39, 0x9d, 0xfb, 0x12, 0x60, 0xfc, 0xb6, 0x02, 0x75, 0x93, 0x7d, 0xb2, 0x60, 0x21, + 0x27, 0xdb, 0x50, 0x61, 0xe3, 0xa9, 0xd7, 0x2b, 0xdd, 0xd3, 0xb6, 0x9b, 0x7b, 0x64, 0x20, 0x17, + 0x52, 0xd2, 0xa7, 0xe3, 0xa9, 0x77, 0xb4, 0x66, 0x22, 0x82, 0xbc, 0x0d, 0xd5, 0xb3, 0xd9, 0x22, + 0x9c, 0xf6, 0xca, 0x08, 0xbd, 0x99, 0x85, 0x7e, 0x5f, 0x88, 0x8e, 0xd6, 0x4c, 0x89, 0x11, 0xcb, + 0x3a, 0xee, 0x99, 0xd7, 0xab, 0x14, 0x2d, 0x7b, 0xec, 0x9e, 0xe1, 0xb2, 0x02, 0x41, 0xf6, 0x01, + 0x42, 0xc6, 0x2d, 0xcf, 0xe7, 0x8e, 0xe7, 0xf6, 0xaa, 0x88, 0xbf, 0x93, 0xc5, 0x3f, 0x63, 0xfc, + 0xc7, 0x28, 0x3e, 0x5a, 0x33, 0xf5, 0x30, 0x1a, 0x08, 0x4d, 0xc7, 0x75, 0xb8, 0x35, 0x9e, 0x52, + 0xc7, 0xed, 0xd5, 0x8a, 0x34, 0x8f, 0x5d, 0x87, 0x1f, 0x0a, 0xb1, 0xd0, 0x74, 0xa2, 0x81, 0xb8, + 0xca, 0x27, 0x0b, 0x16, 0x5c, 0xf6, 0xea, 0x45, 0x57, 0xf9, 0x89, 0x10, 0x89, 0xab, 0x20, 0x86, + 0x3c, 0x86, 0xe6, 0x88, 0x4d, 0x1c, 0xd7, 0x1a, 0xcd, 0xbc, 0xf1, 0x79, 0xaf, 0x81, 0x2a, 0xbd, + 0xac, 0xca, 0x50, 0x00, 0x86, 0x42, 0x7e, 0xb4, 0x66, 0xc2, 0x28, 0x1e, 0x91, 0x3d, 0x68, 0x8c, + 0xa7, 0x6c, 0x7c, 0x6e, 0xf1, 0x65, 0x4f, 0x47, 0xcd, 0x5b, 0x59, 0xcd, 0x43, 0x21, 0x7d, 0xbe, + 0x3c, 0x5a, 0x33, 0xeb, 0x63, 0xf9, 0x49, 0x1e, 0x82, 0xce, 0x5c, 0x5b, 0x6d, 0xd7, 0x44, 0xa5, + 0xdb, 0xb9, 0xdf, 0xc5, 0xb5, 0xa3, 0xcd, 0x1a, 0x4c, 0x7d, 0x93, 0x01, 0xd4, 0x84, 0x33, 0x38, + 0xbc, 0xb7, 0x8e, 0x3a, 0x1b, 0xb9, 0x8d, 0x50, 0x76, 0xb4, 0x66, 0x2a, 0x94, 0x30, 0x9f, 0xcd, + 0x66, 0xce, 0x05, 0x0b, 0xc4, 0xe1, 0x6e, 0x16, 0x99, 0xef, 0x89, 0x94, 0xe3, 0xf1, 0x74, 0x3b, + 0x1a, 0x0c, 0xeb, 0x50, 0xbd, 0xa0, 0xb3, 0x05, 0x33, 0xde, 0x82, 0x66, 0xca, 0x53, 0x48, 0x0f, + 0xea, 0x73, 0x16, 0x86, 0x74, 0xc2, 0x7a, 0xda, 0x3d, 0x6d, 0x5b, 0x37, 0xa3, 0xa1, 0xd1, 0x86, + 0xf5, 0xb4, 0x9f, 0x18, 0xf3, 0x58, 0x51, 0xf8, 0x82, 0x50, 0xbc, 0x60, 0x41, 0x28, 0x1c, 0x40, + 0x29, 0xaa, 0x21, 0xb9, 0x0f, 0x2d, 0xb4, 0x83, 0x15, 0xc9, 0x85, 0x9f, 0x56, 0xcc, 0x75, 0x9c, + 0x3c, 0x55, 0xa0, 0x2d, 0x68, 0xfa, 0x7b, 0x7e, 0x0c, 0x29, 0x23, 0x04, 0xfc, 0x3d, 0x5f, 0x01, + 0x8c, 0x0f, 0xa0, 0x9b, 0x77, 0x25, 0xd2, 0x85, 0xf2, 0x39, 0xbb, 0x54, 0xfb, 0x89, 0x4f, 0xb2, + 0xa1, 0xae, 0x85, 0x7b, 0xe8, 0xa6, 0xba, 0xe3, 0xe7, 0xa5, 0x58, 0x39, 0xf6, 0x26, 0xb2, 0x0f, + 0x15, 0x11, 0x54, 0xa8, 0xdd, 0xdc, 0xeb, 0x0f, 0x64, 0xc4, 0x0d, 0xa2, 0x88, 0x1b, 0x3c, 0x8f, + 0x22, 0x6e, 0xd8, 0xf8, 0xf2, 0xe5, 0xd6, 0xda, 0xe7, 0x7f, 0xd8, 0xd2, 0x4c, 0xd4, 0x20, 0x77, + 0x85, 0x43, 0x50, 0xc7, 0xb5, 0x1c, 0x5b, 0xed, 0x53, 0xc7, 0xf1, 0xb1, 0x4d, 0x0e, 0xa0, 0x3b, + 0xf6, 0xdc, 0x90, 0xb9, 0xe1, 0x22, 0xb4, 0x7c, 0x1a, 0xd0, 0x79, 0xa8, 0x62, 0x2d, 0xfa, 0xf9, + 0x0f, 0x23, 0xf1, 0x09, 0x4a, 0xcd, 0xce, 0x38, 0x3b, 0x41, 0x3e, 0x04, 0xb8, 0xa0, 0x33, 0xc7, + 0xa6, 0xdc, 0x0b, 0xc2, 0x5e, 0xe5, 0x5e, 0x39, 0xa5, 0x7c, 0x1a, 0x09, 0x5e, 0xf8, 0x36, 0xe5, + 0x6c, 0x58, 0x11, 0x27, 0x33, 0x53, 0x78, 0xf2, 0x26, 0x74, 0xa8, 0xef, 0x5b, 0x21, 0xa7, 0x9c, + 0x59, 0xa3, 0x4b, 0xce, 0x42, 0x8c, 0xc7, 0x75, 0xb3, 0x45, 0x7d, 0xff, 0x99, 0x98, 0x1d, 0x8a, + 0x49, 0xc3, 0x8e, 0x7f, 0x4d, 0x0c, 0x15, 0x42, 0xa0, 0x62, 0x53, 0x4e, 0xd1, 0x1a, 0xeb, 0x26, + 0x7e, 0x8b, 0x39, 0x9f, 0xf2, 0xa9, 0xba, 0x23, 0x7e, 0x93, 0xdb, 0x50, 0x9b, 0x32, 0x67, 0x32, + 0xe5, 0x78, 0xad, 0xb2, 0xa9, 0x46, 0xc2, 0xf0, 0x7e, 0xe0, 0x5d, 0x30, 0xcc, 0x16, 0x0d, 0x53, + 0x0e, 0x8c, 0xbf, 0x68, 0x70, 0xe3, 0x5a, 0x78, 0x89, 0x75, 0xa7, 0x34, 0x9c, 0x46, 0x7b, 0x89, + 0x6f, 0xf2, 0xb6, 0x58, 0x97, 0xda, 0x2c, 0x50, 0x59, 0xac, 0xa5, 0x6e, 0x7c, 0x84, 0x93, 0xea, + 0xa2, 0x0a, 0x42, 0x9e, 0x42, 0x77, 0x46, 0x43, 0x6e, 0xc9, 0x28, 0xb0, 0x30, 0x4b, 0x95, 0x33, + 0x91, 0xf9, 0x31, 0x8d, 0xa2, 0x45, 0x38, 0xa7, 0x52, 0x6f, 0xcf, 0x32, 0xb3, 0xe4, 0x08, 0x36, + 0x46, 0x97, 0x3f, 0xa7, 0x2e, 0x77, 0x5c, 0x66, 0x5d, 0xb3, 0x79, 0x47, 0x2d, 0xf5, 0xf4, 0xc2, + 0xb1, 0x99, 0x3b, 0x8e, 0x8c, 0x7d, 0x33, 0x56, 0x89, 0x7f, 0x8c, 0xd0, 0xb8, 0x07, 0xed, 0x6c, + 0x2e, 0x20, 0x6d, 0x28, 0xf1, 0xa5, 0xba, 0x61, 0x89, 0x2f, 0x0d, 0x23, 0xf6, 0xc0, 0x38, 0x20, + 0xaf, 0x61, 0x76, 0xa0, 0x93, 0x4b, 0x0e, 0x29, 0x73, 0x6b, 0x69, 0x73, 0x1b, 0x1d, 0x68, 0x65, + 0x72, 0x82, 0xf1, 0x59, 0x15, 0x1a, 0x26, 0x0b, 0x7d, 0xe1, 0x4c, 0x64, 0x1f, 0x74, 0xb6, 0x1c, + 0x33, 0x99, 0x8e, 0xb5, 0x5c, 0xb2, 0x93, 0x98, 0xa7, 0x91, 0x5c, 0xa4, 0x85, 0x18, 0x4c, 0x76, + 0x32, 0x54, 0x72, 0x33, 0xaf, 0x94, 0xe6, 0x92, 0x07, 0x59, 0x2e, 0xd9, 0xc8, 0x61, 0x73, 0x64, + 0xb2, 0x93, 0x21, 0x93, 0xfc, 0xc2, 0x19, 0x36, 0x79, 0x54, 0xc0, 0x26, 0xf9, 0xe3, 0xaf, 0xa0, + 0x93, 0x47, 0x05, 0x74, 0xd2, 0xbb, 0xb6, 0x57, 0x21, 0x9f, 0x3c, 0xc8, 0xf2, 0x49, 0xfe, 0x3a, + 0x39, 0x42, 0xf9, 0xb0, 0x88, 0x50, 0xee, 0xe6, 0x74, 0x56, 0x32, 0xca, 0xfb, 0xd7, 0x18, 0xe5, + 0x76, 0x4e, 0xb5, 0x80, 0x52, 0x1e, 0x65, 0x72, 0x3d, 0x14, 0xde, 0xad, 0x38, 0xd9, 0x93, 0xef, + 0x5c, 0x67, 0xa3, 0x3b, 0xf9, 0x9f, 0xb6, 0x88, 0x8e, 0x76, 0x73, 0x74, 0x74, 0x2b, 0x7f, 0xca, + 0x1c, 0x1f, 0x25, 0xac, 0xb2, 0x23, 0xe2, 0x3e, 0xe7, 0x69, 0x22, 0x47, 0xb0, 0x20, 0xf0, 0x02, + 0x95, 0xb0, 0xe5, 0xc0, 0xd8, 0x16, 0x99, 0x28, 0xf1, 0xaf, 0x57, 0x30, 0x10, 0x3a, 0x7d, 0xca, + 0xbb, 0x8c, 0x2f, 0xb4, 0x44, 0x17, 0x23, 0x3a, 0x9d, 0xc5, 0x74, 0x95, 0xc5, 0x52, 0xc4, 0x54, + 0xca, 0x12, 0xd3, 0x16, 0x34, 0x45, 0xae, 0xcc, 0x71, 0x0e, 0xf5, 0x23, 0xce, 0x21, 0xdf, 0x82, + 0x1b, 0x98, 0x67, 0x24, 0x7d, 0xa9, 0x40, 0xac, 0x60, 0x20, 0x76, 0x84, 0x40, 0x5a, 0x4c, 0x26, + 0xc0, 0x77, 0xe0, 0x66, 0x0a, 0x2b, 0xd6, 0xc5, 0x1c, 0x27, 0x93, 0x6f, 0x37, 0x46, 0x1f, 0xf8, + 0xfe, 0x11, 0x0d, 0xa7, 0xc6, 0x0f, 0x13, 0x03, 0x25, 0x7c, 0x46, 0xa0, 0x32, 0xf6, 0x6c, 0x79, + 0xef, 0x96, 0x89, 0xdf, 0x82, 0xe3, 0x66, 0xde, 0x04, 0x0f, 0xa7, 0x9b, 0xe2, 0x53, 0xa0, 0xe2, + 0x50, 0xd2, 0x65, 0xcc, 0x18, 0xbf, 0xd4, 0x92, 0xf5, 0x12, 0x8a, 0x2b, 0x62, 0x23, 0xed, 0x3f, + 0x61, 0xa3, 0xd2, 0xeb, 0xb1, 0x91, 0x71, 0xa5, 0x25, 0x3f, 0x59, 0xcc, 0x33, 0x5f, 0xef, 0x8a, + 0xc2, 0x7b, 0x1c, 0xd7, 0x66, 0x4b, 0x34, 0x69, 0xd9, 0x94, 0x83, 0xa8, 0x04, 0xa8, 0xa1, 0x99, + 0xb3, 0x25, 0x40, 0x1d, 0xe7, 0xe4, 0x80, 0xdc, 0x47, 0x7e, 0xf2, 0xce, 0x54, 0xa8, 0xb6, 0x06, + 0xaa, 0x50, 0x3f, 0x11, 0x93, 0xa6, 0x94, 0xa5, 0xb2, 0xad, 0x9e, 0x21, 0xb7, 0x37, 0x40, 0x17, + 0x07, 0x0d, 0x7d, 0x3a, 0x66, 0x18, 0x79, 0xba, 0x99, 0x4c, 0x18, 0x27, 0x40, 0xae, 0x47, 0x3c, + 0xf9, 0x00, 0x2a, 0x9c, 0x4e, 0x84, 0xbd, 0x85, 0xc9, 0xda, 0x03, 0x59, 0xe4, 0x0f, 0x3e, 0x3a, + 0x3d, 0xa1, 0x4e, 0x30, 0xbc, 0x2d, 0x4c, 0xf5, 0xb7, 0x97, 0x5b, 0x6d, 0x81, 0x79, 0xe0, 0xcd, + 0x1d, 0xce, 0xe6, 0x3e, 0xbf, 0x34, 0x51, 0xc7, 0xf8, 0xbb, 0x26, 0x98, 0x20, 0x93, 0x09, 0x0a, + 0x0d, 0x17, 0xb9, 0x7b, 0x29, 0x45, 0xda, 0x5f, 0xcd, 0x98, 0xff, 0x0f, 0x30, 0xa1, 0xa1, 0xf5, + 0x29, 0x75, 0x39, 0xb3, 0x95, 0x45, 0xf5, 0x09, 0x0d, 0x7f, 0x8a, 0x13, 0xa2, 0xc2, 0x11, 0xe2, + 0x45, 0xc8, 0x6c, 0x34, 0x6d, 0xd9, 0xac, 0x4f, 0x68, 0xf8, 0x22, 0x64, 0x76, 0x7c, 0xaf, 0xfa, + 0xeb, 0xdf, 0x2b, 0x6b, 0xc7, 0x46, 0xde, 0x8e, 0xff, 0x48, 0xf9, 0x70, 0x42, 0x92, 0xff, 0xfb, + 0xf7, 0xfe, 0xab, 0x26, 0x6a, 0x83, 0x6c, 0x1a, 0x26, 0xc7, 0x70, 0x23, 0x8e, 0x23, 0x6b, 0x81, + 0xf1, 0x15, 0xf9, 0xd2, 0xab, 0xc3, 0xaf, 0x7b, 0x91, 0x9d, 0x0e, 0xc9, 0x8f, 0xe0, 0x4e, 0x2e, + 0x0b, 0xc4, 0x0b, 0x96, 0x5e, 0x99, 0x0c, 0x6e, 0x65, 0x93, 0x41, 0xb4, 0x5e, 0x64, 0x89, 0xf2, + 0xd7, 0xf0, 0xec, 0x6f, 0x88, 0x42, 0x29, 0x4d, 0x1e, 0x45, 0xbf, 0xa5, 0xf1, 0x2b, 0x0d, 0x3a, + 0xb9, 0xc3, 0x90, 0x6d, 0xa8, 0x4a, 0xfe, 0xd2, 0x32, 0xed, 0x28, 0x5a, 0x4b, 0x9d, 0x57, 0x02, + 0xc8, 0x7b, 0xd0, 0x60, 0xaa, 0x66, 0x53, 0x17, 0xbc, 0x95, 0x2b, 0xe5, 0x14, 0x3e, 0x86, 0x91, + 0x6f, 0x83, 0x1e, 0x9b, 0x2d, 0x57, 0xaf, 0xc7, 0x56, 0x56, 0x4a, 0x09, 0xd0, 0x38, 0x84, 0x66, + 0x6a, 0x7b, 0xf2, 0x7f, 0xa0, 0xcf, 0xe9, 0x52, 0x15, 0xdd, 0xb2, 0x5c, 0x6b, 0xcc, 0xe9, 0x12, + 0xeb, 0x6d, 0x72, 0x07, 0xea, 0x42, 0x38, 0xa1, 0xd2, 0xe8, 0x65, 0xb3, 0x36, 0xa7, 0xcb, 0x1f, + 0xd0, 0xd0, 0xd8, 0x81, 0x76, 0xf6, 0x58, 0x11, 0x34, 0x22, 0x40, 0x09, 0x3d, 0x98, 0x30, 0xe3, + 0x21, 0x74, 0x72, 0xa7, 0x21, 0x06, 0xb4, 0xfc, 0xc5, 0xc8, 0x3a, 0x67, 0x97, 0x16, 0x1e, 0x17, + 0x5d, 0x44, 0x37, 0x9b, 0xfe, 0x62, 0xf4, 0x11, 0xbb, 0x7c, 0x2e, 0xa6, 0x8c, 0x67, 0xd0, 0xce, + 0x96, 0xc3, 0x22, 0x45, 0x06, 0xde, 0xc2, 0xb5, 0x71, 0xfd, 0xaa, 0x29, 0x07, 0xa2, 0xa3, 0xbe, + 0xf0, 0xa4, 0x57, 0xa4, 0xeb, 0xdf, 0x53, 0x8f, 0xb3, 0x54, 0x11, 0x2d, 0x31, 0xc6, 0x2f, 0xaa, + 0x50, 0x93, 0xb5, 0x39, 0x19, 0x64, 0x3b, 0x3f, 0xe1, 0x12, 0x4a, 0x53, 0xce, 0x2a, 0xc5, 0x98, + 0x76, 0xdf, 0xcc, 0xb7, 0x4f, 0xc3, 0xe6, 0xd5, 0xcb, 0xad, 0x3a, 0x52, 0xd6, 0xf1, 0x93, 0xa4, + 0x97, 0x5a, 0xd5, 0x6a, 0x44, 0x8d, 0x5b, 0xe5, 0xb5, 0x1b, 0xb7, 0x3b, 0x50, 0x77, 0x17, 0x73, + 0x8b, 0x2f, 0x43, 0x15, 0xfa, 0x35, 0x77, 0x31, 0x7f, 0xbe, 0xc4, 0x9f, 0x8e, 0x7b, 0x9c, 0xce, + 0x50, 0x24, 0x03, 0xbf, 0x81, 0x13, 0x42, 0xb8, 0x0f, 0xad, 0x14, 0xb3, 0x3b, 0xb6, 0xaa, 0x10, + 0xdb, 0x69, 0x0f, 0x3c, 0x7e, 0xa2, 0x6e, 0xd9, 0x8c, 0x99, 0xfe, 0xd8, 0x26, 0xdb, 0xd9, 0x3e, + 0x05, 0x0b, 0x82, 0x06, 0xfa, 0x79, 0xaa, 0x15, 0x11, 0xe5, 0x80, 0x38, 0x80, 0xf0, 0x7c, 0x09, + 0xd1, 0x11, 0xd2, 0x10, 0x13, 0x28, 0x7c, 0x0b, 0x3a, 0x09, 0xa7, 0x4a, 0x08, 0xc8, 0x55, 0x92, + 0x69, 0x04, 0xbe, 0x0b, 0x1b, 0x2e, 0x5b, 0x72, 0x2b, 0x8f, 0x6e, 0x22, 0x9a, 0x08, 0xd9, 0x69, + 0x56, 0xe3, 0x9b, 0xd0, 0x4e, 0x72, 0x03, 0x62, 0xd7, 0x65, 0xb7, 0x18, 0xcf, 0x22, 0xec, 0x2e, + 0x34, 0xe2, 0x8a, 0xa6, 0x85, 0x80, 0x3a, 0x95, 0x85, 0x4c, 0x5c, 0x23, 0x05, 0x2c, 0x5c, 0xcc, + 0xb8, 0x5a, 0xa4, 0x8d, 0x18, 0xac, 0x91, 0x4c, 0x39, 0x8f, 0xd8, 0xfb, 0xd0, 0x8a, 0x42, 0x4e, + 0xe2, 0x3a, 0x88, 0x5b, 0x8f, 0x26, 0x11, 0xb4, 0x03, 0x5d, 0x3f, 0xf0, 0x7c, 0x2f, 0x64, 0x81, + 0x45, 0x6d, 0x3b, 0x60, 0x61, 0xd8, 0xeb, 0xca, 0xf5, 0xa2, 0xf9, 0x03, 0x39, 0x6d, 0xbc, 0x07, + 0xf5, 0xa8, 0x54, 0xdb, 0x80, 0xea, 0x30, 0x4e, 0x0f, 0x15, 0x53, 0x0e, 0x04, 0x29, 0x1c, 0xf8, + 0xbe, 0x7a, 0x70, 0x10, 0x9f, 0xc6, 0xcf, 0xa0, 0xae, 0x7e, 0xb0, 0xc2, 0x36, 0xf4, 0xbb, 0xb0, + 0xee, 0xd3, 0x40, 0x5c, 0x23, 0xdd, 0x8c, 0x46, 0xcd, 0xc0, 0x09, 0x0d, 0xf8, 0x33, 0xc6, 0x33, + 0x3d, 0x69, 0x13, 0xf1, 0x72, 0xca, 0x78, 0x04, 0xad, 0x0c, 0x46, 0x1c, 0x0b, 0xfd, 0x28, 0x8a, + 0x34, 0x1c, 0xc4, 0x3b, 0x97, 0x92, 0x9d, 0x8d, 0xc7, 0xa0, 0xc7, 0xbf, 0x8d, 0xa8, 0x59, 0xa3, + 0xab, 0x6b, 0xca, 0xdc, 0x72, 0x88, 0x7d, 0xb6, 0xf7, 0x29, 0x0b, 0x54, 0x4c, 0xc8, 0x81, 0xf1, + 0x22, 0x95, 0x19, 0x64, 0x9a, 0x26, 0x0f, 0xa0, 0xae, 0x32, 0x83, 0x8a, 0xca, 0xa8, 0xa3, 0x3e, + 0xc1, 0xd4, 0x10, 0x75, 0xd4, 0x32, 0x51, 0x24, 0xcb, 0x96, 0xd2, 0xcb, 0xce, 0xa0, 0x11, 0x45, + 0x7f, 0x36, 0x45, 0xca, 0x15, 0xbb, 0xf9, 0x14, 0xa9, 0x16, 0x4d, 0x80, 0xc2, 0x3b, 0x42, 0x67, + 0xe2, 0x32, 0xdb, 0x4a, 0x42, 0x08, 0xf7, 0x68, 0x98, 0x1d, 0x29, 0xf8, 0x38, 0x8a, 0x17, 0xe3, + 0x5d, 0xa8, 0xc9, 0xb3, 0x09, 0xfb, 0x88, 0x95, 0xa3, 0x32, 0x5e, 0x7c, 0x17, 0xf2, 0xc4, 0xef, + 0x35, 0x68, 0x44, 0xc9, 0xb3, 0x50, 0x29, 0x73, 0xe8, 0xd2, 0x57, 0x3d, 0xf4, 0x7f, 0x3f, 0xf1, + 0x3c, 0x00, 0x22, 0xf3, 0xcb, 0x85, 0xc7, 0x1d, 0x77, 0x62, 0x49, 0x5b, 0xcb, 0x1c, 0xd4, 0x45, + 0xc9, 0x29, 0x0a, 0x4e, 0xc4, 0xfc, 0xde, 0x67, 0x55, 0xe8, 0x1c, 0x0c, 0x0f, 0x8f, 0x0f, 0x7c, + 0x7f, 0xe6, 0x8c, 0x29, 0xb6, 0x06, 0xbb, 0x50, 0xc1, 0xee, 0xa8, 0xe0, 0x75, 0xb7, 0x5f, 0xd4, + 0xa6, 0x93, 0x3d, 0xa8, 0x62, 0x93, 0x44, 0x8a, 0x1e, 0x79, 0xfb, 0x85, 0xdd, 0xba, 0xd8, 0x44, + 0xb6, 0x51, 0xd7, 0xdf, 0x7a, 0xfb, 0x45, 0x2d, 0x3b, 0xf9, 0x1e, 0xe8, 0x49, 0xf7, 0xb2, 0xea, + 0xc5, 0xb7, 0xbf, 0xb2, 0x79, 0x17, 0xfa, 0x49, 0xa5, 0xb7, 0xea, 0xe1, 0xb2, 0xbf, 0xb2, 0xcb, + 0x25, 0xfb, 0x50, 0x8f, 0xea, 0xe3, 0xe2, 0x37, 0xd9, 0xfe, 0x8a, 0xc6, 0x5a, 0x98, 0x47, 0x36, + 0x24, 0x45, 0x0f, 0xc7, 0xfd, 0xc2, 0xee, 0x9f, 0x3c, 0x84, 0x9a, 0x2a, 0x5a, 0x0a, 0xdf, 0x65, + 0xfb, 0xc5, 0xed, 0xb1, 0xb8, 0x64, 0xd2, 0x92, 0xad, 0x7a, 0xdc, 0xee, 0xaf, 0x7c, 0xa6, 0x20, + 0x07, 0x00, 0xa9, 0xbe, 0x62, 0xe5, 0xab, 0x75, 0x7f, 0xf5, 0xf3, 0x03, 0x79, 0x0c, 0x8d, 0xe4, + 0x49, 0xa9, 0xf8, 0x1d, 0xba, 0xbf, 0xea, 0x45, 0x60, 0xf8, 0xc6, 0x3f, 0xff, 0xb4, 0xa9, 0xfd, + 0xfa, 0x6a, 0x53, 0xfb, 0xe2, 0x6a, 0x53, 0xfb, 0xf2, 0x6a, 0x53, 0xfb, 0xdd, 0xd5, 0xa6, 0xf6, + 0xc7, 0xab, 0x4d, 0xed, 0x37, 0x7f, 0xde, 0xd4, 0x46, 0x35, 0x74, 0xff, 0xf7, 0xff, 0x15, 0x00, + 0x00, 0xff, 0xff, 0x38, 0x2d, 0x52, 0x86, 0x77, 0x19, 0x00, 0x00, } diff --git a/abci/types/types.proto b/abci/types/types.proto index 86f5bbc58..8eeecb392 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -218,8 +218,6 @@ message BlockParams { int64 max_bytes = 1; // Note: must be greater or equal to -1 int64 max_gas = 2; - // Note: must be greater than 0 - int64 time_iota_ms = 3; } // EvidenceParams contains limits on the evidence. diff --git a/consensus/replay.go b/consensus/replay.go index 0d75561b9..6656da625 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -324,7 +324,12 @@ func (h *Handshaker) ReplayBlocks( } if res.ConsensusParams != nil { - state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams) + // Preserve TimeIotaMs since it's not exposed to the application. + timeIotaMs := state.ConsensusParams.Block.TimeIotaMs + { + state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams) + } + state.ConsensusParams.Block.TimeIotaMs = timeIotaMs } sm.SaveState(h.stateDB, state) } diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index 6f624a562..c696c9384 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -456,8 +456,6 @@ Commit are included in the header of the next block. - NOTE: blocks that violate this may be committed if there are Byzantine proposers. It's the application's responsibility to handle this when processing a block! - - `TimeIotaMs (int64)`: Minimum time increment between consecutive blocks (in milliseconds). - ### EvidenceParams diff --git a/state/state_test.go b/state/state_test.go index ff8eed027..eddbe255b 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -992,12 +992,11 @@ func TestApplyUpdates(t *testing.T) { 2: {initParams, abci.ConsensusParams{ Block: &abci.BlockParams{ - MaxBytes: 44, - MaxGas: 55, - TimeIotaMs: 66, + MaxBytes: 44, + MaxGas: 55, }, }, - makeParams(44, 55, 66, 4)}, + makeParams(44, 55, 3, 4)}, 3: {initParams, abci.ConsensusParams{ Evidence: &abci.EvidenceParams{ diff --git a/types/params.go b/types/params.go index ce9a6bc68..162aaeada 100644 --- a/types/params.go +++ b/types/params.go @@ -36,6 +36,7 @@ type BlockParams struct { MaxBytes int64 `json:"max_bytes"` MaxGas int64 `json:"max_gas"` // Minimum time increment between consecutive blocks (in milliseconds) + // Not exposed to the application. TimeIotaMs int64 `json:"time_iota_ms"` } @@ -169,7 +170,6 @@ func (params ConsensusParams) Update(params2 *abci.ConsensusParams) ConsensusPar if params2.Block != nil { res.Block.MaxBytes = params2.Block.MaxBytes res.Block.MaxGas = params2.Block.MaxGas - res.Block.TimeIotaMs = params2.Block.TimeIotaMs } if params2.Evidence != nil { res.Evidence.MaxAge = params2.Evidence.MaxAge diff --git a/types/params_test.go b/types/params_test.go index ade7c89f5..1f2a35120 100644 --- a/types/params_test.go +++ b/types/params_test.go @@ -111,18 +111,17 @@ func TestConsensusParamsUpdate(t *testing.T) { makeParams(1, 2, 10, 3, valEd25519), &abci.ConsensusParams{ Block: &abci.BlockParams{ - MaxBytes: 100, - MaxGas: 200, - TimeIotaMs: 300, + MaxBytes: 100, + MaxGas: 200, }, Evidence: &abci.EvidenceParams{ - MaxAge: 400, + MaxAge: 300, }, Validator: &abci.ValidatorParams{ PubKeyTypes: valSecp256k1, }, }, - makeParams(100, 200, 300, 400, valSecp256k1), + makeParams(100, 200, 10, 300, valSecp256k1), }, } for _, tc := range testCases { diff --git a/types/protobuf.go b/types/protobuf.go index 81b13874f..8cad4608b 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -126,9 +126,8 @@ func (tm2pb) ValidatorUpdates(vals *ValidatorSet) []abci.ValidatorUpdate { func (tm2pb) ConsensusParams(params *ConsensusParams) *abci.ConsensusParams { return &abci.ConsensusParams{ Block: &abci.BlockParams{ - MaxBytes: params.Block.MaxBytes, - MaxGas: params.Block.MaxGas, - TimeIotaMs: params.Block.TimeIotaMs, + MaxBytes: params.Block.MaxBytes, + MaxGas: params.Block.MaxGas, }, Evidence: &abci.EvidenceParams{ MaxAge: params.Evidence.MaxAge, @@ -223,14 +222,17 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) } func (pb2tm) ConsensusParams(csp *abci.ConsensusParams) ConsensusParams { - params := ConsensusParams{} + params := ConsensusParams{ + Block: BlockParams{}, + Evidence: EvidenceParams{}, + Validator: ValidatorParams{}, + } // we must defensively consider any structs may be nil if csp.Block != nil { params.Block = BlockParams{ - MaxBytes: csp.Block.MaxBytes, - MaxGas: csp.Block.MaxGas, - TimeIotaMs: csp.Block.TimeIotaMs, + MaxBytes: csp.Block.MaxBytes, + MaxGas: csp.Block.MaxGas, } } diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 40859d9e3..2e29a5028 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -65,6 +65,8 @@ func TestABCIConsensusParams(t *testing.T) { cp := DefaultConsensusParams() abciCP := TM2PB.ConsensusParams(cp) cp2 := PB2TM.ConsensusParams(abciCP) + // TimeIotaMs is not exposed to the application. + cp2.Block.TimeIotaMs = cp.Block.TimeIotaMs assert.Equal(t, *cp, cp2) } From d741c7b4785c36cef4b0912900b6193db21d00e6 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 11 Mar 2019 22:45:58 +0400 Subject: [PATCH 099/259] limit number of /subscribe clients and queries per client (#3269) * limit number of /subscribe clients and queries per client Add the following config variables (under [rpc] section): * max_subscription_clients * max_subscriptions_per_client * timeout_broadcast_tx_commit Fixes #2826 new HTTPClient interface for subscriptions finalize HTTPClient events interface remove EventSubscriber fix data race ``` WARNING: DATA RACE Read at 0x00c000a36060 by goroutine 129: github.com/tendermint/tendermint/rpc/client.(*Local).Subscribe.func1() /go/src/github.com/tendermint/tendermint/rpc/client/localclient.go:168 +0x1f0 Previous write at 0x00c000a36060 by goroutine 132: github.com/tendermint/tendermint/rpc/client.(*Local).Subscribe() /go/src/github.com/tendermint/tendermint/rpc/client/localclient.go:191 +0x4e0 github.com/tendermint/tendermint/rpc/client.WaitForOneEvent() /go/src/github.com/tendermint/tendermint/rpc/client/helpers.go:64 +0x178 github.com/tendermint/tendermint/rpc/client_test.TestTxEventsSentWithBroadcastTxSync.func1() /go/src/github.com/tendermint/tendermint/rpc/client/event_test.go:139 +0x298 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 Goroutine 129 (running) created at: github.com/tendermint/tendermint/rpc/client.(*Local).Subscribe() /go/src/github.com/tendermint/tendermint/rpc/client/localclient.go:164 +0x4b7 github.com/tendermint/tendermint/rpc/client.WaitForOneEvent() /go/src/github.com/tendermint/tendermint/rpc/client/helpers.go:64 +0x178 github.com/tendermint/tendermint/rpc/client_test.TestTxEventsSentWithBroadcastTxSync.func1() /go/src/github.com/tendermint/tendermint/rpc/client/event_test.go:139 +0x298 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 Goroutine 132 (running) created at: testing.(*T).Run() /usr/local/go/src/testing/testing.go:878 +0x659 github.com/tendermint/tendermint/rpc/client_test.TestTxEventsSentWithBroadcastTxSync() /go/src/github.com/tendermint/tendermint/rpc/client/event_test.go:119 +0x186 testing.tRunner() /usr/local/go/src/testing/testing.go:827 +0x162 ================== ``` lite client works (tested manually) godoc comments httpclient: do not close the out channel use TimeoutBroadcastTxCommit no timeout for unsubscribe but 1s Local (5s HTTP) timeout for resubscribe format code change Subscribe#out cap to 1 and replace config vars with RPCConfig TimeoutBroadcastTxCommit can't be greater than rpcserver.WriteTimeout rpc: Context as first parameter to all functions reformat code fixes after my own review fixes after Ethan's review add test stubs fix config.toml * fixes after manual testing - rpc: do not recommend to use BroadcastTxCommit because it's slow and wastes Tendermint resources (pubsub) - rpc: better error in Subscribe and BroadcastTxCommit - HTTPClient: do not resubscribe if err = ErrAlreadySubscribed * fixes after Ismail's review * Update rpc/grpc/grpc_test.go Co-Authored-By: melekes --- CHANGELOG_PENDING.md | 4 +- config/config.go | 30 ++++ config/toml.go | 13 ++ docs/tendermint-core/configuration.md | 13 ++ libs/pubsub/pubsub.go | 14 ++ libs/pubsub/pubsub_test.go | 4 + lite/proxy/proxy.go | 16 ++- lite/proxy/wrapper.go | 53 ++++++++ node/node.go | 13 +- rpc/client/event_test.go | 6 + rpc/client/helpers.go | 8 +- rpc/client/httpclient.go | 155 +++++++++++---------- rpc/client/interface.go | 15 +- rpc/client/localclient.go | 188 +++++++++++++++++++------- rpc/client/mock/client.go | 29 ++-- rpc/core/abci.go | 5 +- rpc/core/blocks.go | 9 +- rpc/core/consensus.go | 9 +- rpc/core/dev.go | 13 +- rpc/core/events.go | 61 +++++---- rpc/core/health.go | 3 +- rpc/core/mempool.go | 44 ++++-- rpc/core/net.go | 9 +- rpc/core/pipe.go | 8 ++ rpc/core/status.go | 3 +- rpc/core/tx.go | 5 +- rpc/grpc/api.go | 7 +- rpc/grpc/client_server.go | 3 +- rpc/grpc/grpc_test.go | 7 +- rpc/lib/rpc_test.go | 10 +- rpc/lib/server/handlers.go | 97 ++++++------- rpc/lib/server/handlers_test.go | 4 +- rpc/lib/server/parse_test.go | 90 ++++++------ rpc/lib/test/main.go | 4 +- rpc/lib/types/types.go | 44 ++++-- types/event_bus.go | 11 ++ 36 files changed, 657 insertions(+), 350 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 29400929f..a8998c99c 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -7,7 +7,7 @@ Special thanks to external contributors on this release: ### BREAKING CHANGES: * CLI/RPC/Config -- [httpclient] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) +- [rpc/client] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) * Apps @@ -27,6 +27,7 @@ Special thanks to external contributors on this release: - [config] \#2920 Remove `consensus.blocktime_iota` parameter - [genesis] \#2920 Add `time_iota_ms` to block's consensus parameters (not exposed to the application) - [genesis] \#2920 Rename `consensus_params.block_size` to `consensus_params.block` +- [lite] add `/unsubscribe_all` endpoint, which allows you to unsubscribe from all events ### IMPROVEMENTS: - [libs/common] \#3238 exit with zero (0) code upon receiving SIGTERM/SIGINT @@ -41,7 +42,6 @@ Special thanks to external contributors on this release: - leveldb.aliveiters ### BUG FIXES: - - [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection - [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) - [p2p] \#3369 do not panic when filter times out diff --git a/config/config.go b/config/config.go index cfd76060d..540012a5d 100644 --- a/config/config.go +++ b/config/config.go @@ -7,6 +7,7 @@ import ( "time" "github.com/pkg/errors" + rpcserver "github.com/tendermint/tendermint/rpc/lib/server" ) const ( @@ -323,6 +324,19 @@ type RPCConfig struct { // Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files} // 1024 - 40 - 10 - 50 = 924 = ~900 MaxOpenConnections int `mapstructure:"max_open_connections"` + + // Maximum number of unique clientIDs that can /subscribe + // If you're using /broadcast_tx_commit, set to the estimated maximum number + // of broadcast_tx_commit calls per block. + MaxSubscriptionClients int `mapstructure:"max_subscription_clients"` + + // Maximum number of unique queries a given client can /subscribe to + // If you're using GRPC (or Local RPC client) and /broadcast_tx_commit, set + // to the estimated maximum number of broadcast_tx_commit calls per block. + MaxSubscriptionsPerClient int `mapstructure:"max_subscriptions_per_client"` + + // How long to wait for a tx to be committed during /broadcast_tx_commit + TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"` } // DefaultRPCConfig returns a default configuration for the RPC server @@ -337,6 +351,10 @@ func DefaultRPCConfig() *RPCConfig { Unsafe: false, MaxOpenConnections: 900, + + MaxSubscriptionClients: 100, + MaxSubscriptionsPerClient: 5, + TimeoutBroadcastTxCommit: 10 * time.Second, } } @@ -358,6 +376,18 @@ func (cfg *RPCConfig) ValidateBasic() error { if cfg.MaxOpenConnections < 0 { return errors.New("max_open_connections can't be negative") } + if cfg.MaxSubscriptionClients < 0 { + return errors.New("max_subscription_clients can't be negative") + } + if cfg.MaxSubscriptionsPerClient < 0 { + return errors.New("max_subscriptions_per_client can't be negative") + } + if cfg.TimeoutBroadcastTxCommit < 0 { + return errors.New("timeout_broadcast_tx_commit can't be negative") + } + if cfg.TimeoutBroadcastTxCommit > rpcserver.WriteTimeout { + return fmt.Errorf("timeout_broadcast_tx_commit can't be greater than rpc server's write timeout: %v", rpcserver.WriteTimeout) + } return nil } diff --git a/config/toml.go b/config/toml.go index 45b9a6719..9ce7e76c0 100644 --- a/config/toml.go +++ b/config/toml.go @@ -165,6 +165,19 @@ unsafe = {{ .RPC.Unsafe }} # 1024 - 40 - 10 - 50 = 924 = ~900 max_open_connections = {{ .RPC.MaxOpenConnections }} +# Maximum number of unique clientIDs that can /subscribe +# If you're using /broadcast_tx_commit, set to the estimated maximum number +# of broadcast_tx_commit calls per block. +max_subscription_clients = {{ .RPC.MaxSubscriptionClients }} + +# Maximum number of unique queries a given client can /subscribe to +# If you're using GRPC (or Local RPC client) and /broadcast_tx_commit, set to +# the estimated # maximum number of broadcast_tx_commit calls per block. +max_subscriptions_per_client = {{ .RPC.MaxSubscriptionsPerClient }} + +# How long to wait for a tx to be committed during /broadcast_tx_commit. +timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}" + ##### peer to peer configuration options ##### [p2p] diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index 4e188aae7..f1ac753a7 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -111,6 +111,19 @@ unsafe = false # 1024 - 40 - 10 - 50 = 924 = ~900 max_open_connections = 900 +# Maximum number of unique clientIDs that can /subscribe +# If you're using /broadcast_tx_commit, set to the estimated maximum number +# of broadcast_tx_commit calls per block. +max_subscription_clients = 100 + +# Maximum number of unique queries a given client can /subscribe to +# If you're using GRPC (or Local RPC client) and /broadcast_tx_commit, set to +# the estimated # maximum number of broadcast_tx_commit calls per block. +max_subscriptions_per_client = 5 + +# How long to wait for a tx to be committed during /broadcast_tx_commit. +timeout_broadcast_tx_commit = "10s" + ##### peer to peer configuration options ##### [p2p] diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index 8d4d1fb05..f78dac1ba 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -241,6 +241,20 @@ func (s *Server) UnsubscribeAll(ctx context.Context, clientID string) error { } } +// NumClients returns the number of clients. +func (s *Server) NumClients() int { + s.mtx.RLock() + defer s.mtx.RUnlock() + return len(s.subscriptions) +} + +// NumClientSubscriptions returns the number of subscriptions the client has. +func (s *Server) NumClientSubscriptions(clientID string) int { + s.mtx.RLock() + defer s.mtx.RUnlock() + return len(s.subscriptions[clientID]) +} + // 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 { diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index e2bd50e6c..884477563 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -29,6 +29,10 @@ func TestSubscribe(t *testing.T) { ctx := context.Background() subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) require.NoError(t, err) + + assert.Equal(t, 1, s.NumClients()) + assert.Equal(t, 1, s.NumClientSubscriptions(clientID)) + err = s.Publish(ctx, "Ka-Zar") require.NoError(t, err) assertReceive(t, "Ka-Zar", subscription.Out()) diff --git a/lite/proxy/proxy.go b/lite/proxy/proxy.go index 39baf5a48..020e57539 100644 --- a/lite/proxy/proxy.go +++ b/lite/proxy/proxy.go @@ -1,6 +1,7 @@ package proxy import ( + "context" "net/http" amino "github.com/tendermint/go-amino" @@ -34,7 +35,12 @@ func StartProxy(c rpcclient.Client, listenAddr string, logger log.Logger, maxOpe mux := http.NewServeMux() rpcserver.RegisterRPCFuncs(mux, r, cdc, logger) - wm := rpcserver.NewWebsocketManager(r, cdc, rpcserver.EventSubscriber(c)) + unsubscribeFromAllEvents := func(remoteAddr string) { + if err := c.UnsubscribeAll(context.Background(), remoteAddr); err != nil { + logger.Error("Failed to unsubscribe from events", "err", err) + } + } + wm := rpcserver.NewWebsocketManager(r, cdc, rpcserver.OnDisconnect(unsubscribeFromAllEvents)) wm.SetLogger(logger) core.SetLogger(logger) mux.HandleFunc(wsEndpoint, wm.WebsocketHandler) @@ -51,13 +57,11 @@ func StartProxy(c rpcclient.Client, listenAddr string, logger log.Logger, maxOpe // // if we want security, the client must implement it as a secure client func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc { - return map[string]*rpcserver.RPCFunc{ // Subscribe/unsubscribe are reserved for websocket events. - // We can just use the core tendermint impl, which uses the - // EventSwitch we registered in NewWebsocketManager above - "subscribe": rpcserver.NewWSRPCFunc(core.Subscribe, "query"), - "unsubscribe": rpcserver.NewWSRPCFunc(core.Unsubscribe, "query"), + "subscribe": rpcserver.NewWSRPCFunc(c.(Wrapper).SubscribeWS, "query"), + "unsubscribe": rpcserver.NewWSRPCFunc(c.(Wrapper).UnsubscribeWS, "query"), + "unsubscribe_all": rpcserver.NewWSRPCFunc(c.(Wrapper).UnsubscribeAllWS, ""), // info API "status": rpcserver.NewRPCFunc(c.Status, ""), diff --git a/lite/proxy/wrapper.go b/lite/proxy/wrapper.go index c90cdb275..2d333e9fb 100644 --- a/lite/proxy/wrapper.go +++ b/lite/proxy/wrapper.go @@ -1,12 +1,16 @@ package proxy import ( + "context" + "fmt" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/lite" rpcclient "github.com/tendermint/tendermint/rpc/client" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) var _ rpcclient.Client = Wrapper{} @@ -149,6 +153,55 @@ func (w Wrapper) RegisterOpDecoder(typ string, dec merkle.OpDecoder) { w.prt.RegisterOpDecoder(typ, dec) } +// SubscribeWS subscribes for events using the given query and remote address as +// a subscriber, but does not verify responses (UNSAFE)! +func (w Wrapper) SubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) { + out, err := w.Client.Subscribe(context.Background(), ctx.RemoteAddr(), query) + if err != nil { + return nil, err + } + + go func() { + for { + select { + case resultEvent := <-out: + // XXX(melekes) We should have a switch here that performs a validation + // depending on the event's type. + ctx.WSConn.TryWriteRPCResponse( + rpctypes.NewRPCSuccessResponse( + ctx.WSConn.Codec(), + rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", ctx.JSONReq.ID)), + resultEvent, + )) + case <-w.Client.Quit(): + return + } + } + }() + + return &ctypes.ResultSubscribe{}, nil +} + +// UnsubscribeWS calls original client's Unsubscribe using remote address as a +// subscriber. +func (w Wrapper) UnsubscribeWS(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) { + err := w.Client.Unsubscribe(context.Background(), ctx.RemoteAddr(), query) + if err != nil { + return nil, err + } + return &ctypes.ResultUnsubscribe{}, nil +} + +// UnsubscribeAllWS calls original client's UnsubscribeAll using remote address +// as a subscriber. +func (w Wrapper) UnsubscribeAllWS(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) { + err := w.Client.UnsubscribeAll(context.Background(), ctx.RemoteAddr()) + if err != nil { + return nil, err + } + return &ctypes.ResultUnsubscribe{}, nil +} + // // WrappedSwitch creates a websocket connection that auto-verifies any info // // coming through before passing it along. // // diff --git a/node/node.go b/node/node.go index 2b803502f..f3f9dca35 100644 --- a/node/node.go +++ b/node/node.go @@ -26,6 +26,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + tmpubsub "github.com/tendermint/tendermint/libs/pubsub" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/p2p/pex" @@ -658,6 +659,7 @@ func (n *Node) ConfigureRPC() { rpccore.SetConsensusReactor(n.consensusReactor) rpccore.SetEventBus(n.eventBus) rpccore.SetLogger(n.Logger.With("module", "rpc")) + rpccore.SetConfig(*n.config.RPC) } func (n *Node) startRPC() ([]net.Listener, error) { @@ -675,8 +677,15 @@ func (n *Node) startRPC() ([]net.Listener, error) { for i, listenAddr := range listenAddrs { mux := http.NewServeMux() rpcLogger := n.Logger.With("module", "rpc-server") - wm := rpcserver.NewWebsocketManager(rpccore.Routes, coreCodec, rpcserver.EventSubscriber(n.eventBus)) - wm.SetLogger(rpcLogger.With("protocol", "websocket")) + wmLogger := rpcLogger.With("protocol", "websocket") + wm := rpcserver.NewWebsocketManager(rpccore.Routes, coreCodec, + rpcserver.OnDisconnect(func(remoteAddr string) { + err := n.eventBus.UnsubscribeAll(context.Background(), remoteAddr) + if err != nil && err != tmpubsub.ErrSubscriptionNotFound { + wmLogger.Error("Failed to unsubscribe addr from events", "addr", remoteAddr, "err", err) + } + })) + wm.SetLogger(wmLogger) mux.HandleFunc("/websocket", wm.WebsocketHandler) rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, coreCodec, rpcLogger) diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go index 7b00d6ead..b0a40fc2b 100644 --- a/rpc/client/event_test.go +++ b/rpc/client/event_test.go @@ -129,3 +129,9 @@ func testTxEventsSent(t *testing.T, broadcastMethod string) { }) } } + +// Test HTTPClient resubscribes upon disconnect && subscription error. +// Test Local client resubscribes upon subscription error. +func TestClientsResubscribe(t *testing.T) { + // TODO(melekes) +} diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go index ec63fb3be..4889b0740 100644 --- a/rpc/client/helpers.go +++ b/rpc/client/helpers.go @@ -61,7 +61,7 @@ func WaitForOneEvent(c EventsClient, evtTyp string, timeout time.Duration) (type defer cancel() // register for the next event of this type - sub, err := c.Subscribe(ctx, subscriber, types.QueryForEvent(evtTyp)) + eventCh, err := c.Subscribe(ctx, subscriber, types.QueryForEvent(evtTyp).String()) if err != nil { return nil, errors.Wrap(err, "failed to subscribe") } @@ -69,10 +69,8 @@ func WaitForOneEvent(c EventsClient, evtTyp string, timeout time.Duration) (type defer c.UnsubscribeAll(ctx, subscriber) select { - case msg := <-sub.Out(): - return msg.Data().(types.TMEventData), nil - case <-sub.Cancelled(): - return nil, errors.New("subscription was cancelled") + case event := <-eventCh: + return event.Data.(types.TMEventData), nil case <-ctx.Done(): return nil, errors.New("timed out waiting for event") } diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index a1dee9913..e982292e7 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -2,11 +2,14 @@ package client import ( "context" + "strings" "sync" + "time" "github.com/pkg/errors" amino "github.com/tendermint/go-amino" + cmn "github.com/tendermint/tendermint/libs/common" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" ctypes "github.com/tendermint/tendermint/rpc/core/types" @@ -15,13 +18,18 @@ import ( ) /* -HTTP is a Client implementation that communicates -with a tendermint node over json rpc and websockets. +HTTP is a Client implementation that communicates with a tendermint node over +json rpc and websockets. -This is the main implementation you probably want to use in -production code. There are other implementations when calling -the tendermint node in-process (local), or when you want to mock -out the server for test code (mock). +This is the main implementation you probably want to use in production code. +There are other implementations when calling the tendermint node in-process +(Local), or when you want to mock out the server for test code (mock). + +You can subscribe for any event published by Tendermint using Subscribe method. +Note delivery is best-effort. If you don't read events fast enough or network +is slow, Tendermint might cancel the subscription. The client will attempt to +resubscribe (you don't need to do anything). It will keep trying every second +indefinitely until successful. */ type HTTP struct { remote string @@ -249,28 +257,6 @@ func (c *HTTP) Validators(height *int64) (*ctypes.ResultValidators, error) { /** websocket event stuff here... **/ -type subscription struct { - out chan tmpubsub.Message - cancelled chan struct{} - - mtx sync.RWMutex - err error -} - -func (s *subscription) Out() <-chan tmpubsub.Message { - return s.out -} - -func (s *subscription) Cancelled() <-chan struct{} { - return s.cancelled -} - -func (s *subscription) Err() error { - s.mtx.RLock() - defer s.mtx.RUnlock() - return s.err -} - type WSEvents struct { cmn.BaseService cdc *amino.Codec @@ -279,8 +265,8 @@ type WSEvents struct { ws *rpcclient.WSClient mtx sync.RWMutex - // query -> subscription - subscriptions map[string]*subscription + // query -> chan + subscriptions map[string]chan ctypes.ResultEvent } func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents { @@ -288,16 +274,18 @@ func newWSEvents(cdc *amino.Codec, remote, endpoint string) *WSEvents { cdc: cdc, endpoint: endpoint, remote: remote, - subscriptions: make(map[string]*subscription), + subscriptions: make(map[string]chan ctypes.ResultEvent), } wsEvents.BaseService = *cmn.NewBaseService(nil, "WSEvents", wsEvents) return wsEvents } +// OnStart implements cmn.Service by starting WSClient and event loop. func (w *WSEvents) OnStart() error { w.ws = rpcclient.NewWSClient(w.remote, w.endpoint, rpcclient.OnReconnect(func() { - w.redoSubscriptions() + // resubscribe immediately + w.redoSubscriptionsAfter(0 * time.Second) })) w.ws.SetCodec(w.cdc) @@ -310,75 +298,63 @@ func (w *WSEvents) OnStart() error { return nil } -// Stop wraps the BaseService/eventSwitch actions as Start does +// OnStop implements cmn.Service by stopping WSClient. func (w *WSEvents) OnStop() { - err := w.ws.Stop() - if err != nil { - w.Logger.Error("failed to stop WSClient", "err", err) - } + _ = w.ws.Stop() } -func (w *WSEvents) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (types.Subscription, error) { - q := query.String() +// Subscribe implements EventsClient by using WSClient to subscribe given +// subscriber to query. By default, returns a channel with cap=1. Error is +// returned if it fails to subscribe. +// Channel is never closed to prevent clients from seeing an erroneus event. +func (w *WSEvents) Subscribe(ctx context.Context, subscriber, query string, + outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { - err := w.ws.Subscribe(ctx, q) - if err != nil { + if err := w.ws.Subscribe(ctx, query); err != nil { return nil, err } outCap := 1 - if len(outCapacity) > 0 && outCapacity[0] >= 0 { + if len(outCapacity) > 0 { outCap = outCapacity[0] } + outc := make(chan ctypes.ResultEvent, outCap) w.mtx.Lock() // subscriber param is ignored because Tendermint will override it with // remote IP anyway. - w.subscriptions[q] = &subscription{ - out: make(chan tmpubsub.Message, outCap), - cancelled: make(chan struct{}), - } + w.subscriptions[query] = outc w.mtx.Unlock() - return w.subscriptions[q], nil + return outc, nil } -func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error { - q := query.String() - - err := w.ws.Unsubscribe(ctx, q) - if err != nil { +// Unsubscribe implements EventsClient by using WSClient to unsubscribe given +// subscriber from query. +func (w *WSEvents) Unsubscribe(ctx context.Context, subscriber, query string) error { + if err := w.ws.Unsubscribe(ctx, query); err != nil { return err } w.mtx.Lock() - sub, ok := w.subscriptions[q] + _, ok := w.subscriptions[query] if ok { - close(sub.cancelled) - sub.mtx.Lock() - sub.err = errors.New("unsubscribed") - sub.mtx.Unlock() - delete(w.subscriptions, q) + delete(w.subscriptions, query) } w.mtx.Unlock() return nil } +// UnsubscribeAll implements EventsClient by using WSClient to unsubscribe +// given subscriber from all the queries. func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error { - err := w.ws.UnsubscribeAll(ctx) - if err != nil { + if err := w.ws.UnsubscribeAll(ctx); err != nil { return err } w.mtx.Lock() - for _, sub := range w.subscriptions { - close(sub.cancelled) - sub.mtx.Lock() - sub.err = errors.New("unsubscribed") - sub.mtx.Unlock() - } - w.subscriptions = make(map[string]*subscription) + w.subscriptions = make(map[string]chan ctypes.ResultEvent) w.mtx.Unlock() return nil @@ -386,18 +362,21 @@ func (w *WSEvents) UnsubscribeAll(ctx context.Context, subscriber string) error // After being reconnected, it is necessary to redo subscription to server // otherwise no data will be automatically received. -func (w *WSEvents) redoSubscriptions() { +func (w *WSEvents) redoSubscriptionsAfter(d time.Duration) { + time.Sleep(d) + for q := range w.subscriptions { - // NOTE: no timeout for resubscribing - // FIXME: better logging/handling of errors?? - w.ws.Subscribe(context.Background(), q) + err := w.ws.Subscribe(context.Background(), q) + if err != nil { + w.Logger.Error("Failed to resubscribe", "err", err) + } } } -// eventListener is an infinite loop pulling all websocket events -// and pushing them to the EventSwitch. -// -// the goroutine only stops by closing quit +func isErrAlreadySubscribed(err error) bool { + return strings.Contains(err.Error(), tmpubsub.ErrAlreadySubscribed.Error()) +} + func (w *WSEvents) eventListener() { for { select { @@ -405,21 +384,39 @@ func (w *WSEvents) eventListener() { if !ok { return } + if resp.Error != nil { w.Logger.Error("WS error", "err", resp.Error.Error()) + // Error can be ErrAlreadySubscribed or max client (subscriptions per + // client) reached or Tendermint exited. + // We can ignore ErrAlreadySubscribed, but need to retry in other + // cases. + if !isErrAlreadySubscribed(resp.Error) { + // Resubscribe after 1 second to give Tendermint time to restart (if + // crashed). + w.redoSubscriptionsAfter(1 * time.Second) + } continue } + result := new(ctypes.ResultEvent) err := w.cdc.UnmarshalJSON(resp.Result, result) if err != nil { w.Logger.Error("failed to unmarshal response", "err", err) continue } - // NOTE: writing also happens inside mutex so we can't close a channel in - // Unsubscribe/UnsubscribeAll. + w.mtx.RLock() - if sub, ok := w.subscriptions[result.Query]; ok { - sub.out <- tmpubsub.NewMessage(result.Data, result.Tags) + if out, ok := w.subscriptions[result.Query]; ok { + if cap(out) == 0 { + out <- *result + } else { + select { + case out <- *result: + default: + w.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query) + } + } } w.mtx.RUnlock() case <-w.Quit(): diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 7477225e9..605d84ba2 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -21,6 +21,8 @@ implementation. */ import ( + "context" + cmn "github.com/tendermint/tendermint/libs/common" ctypes "github.com/tendermint/tendermint/rpc/core/types" "github.com/tendermint/tendermint/types" @@ -91,7 +93,18 @@ type NetworkClient interface { // EventsClient is reactive, you can subscribe to any message, given the proper // string. see tendermint/types/events.go type EventsClient interface { - types.EventBusSubscriber + // Subscribe subscribes given subscriber to query. Returns a channel with + // cap=1 onto which events are published. An error is returned if it fails to + // subscribe. outCapacity can be used optionally to set capacity for the + // channel. Channel is never closed to prevent accidental reads. + // + // ctx cannot be used to unsubscribe. To unsubscribe, use either Unsubscribe + // or UnsubscribeAll. + Subscribe(ctx context.Context, subscriber, query string, outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) + // Unsubscribe unsubscribes given subscriber from query. + Unsubscribe(ctx context.Context, subscriber, query string) error + // UnsubscribeAll unsubscribes given subscriber from all the queries. + UnsubscribeAll(ctx context.Context, subscriber string) error } // MempoolClient shows us data about current mempool state. diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index 33a1ce225..976c9892a 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -2,12 +2,18 @@ package client import ( "context" + "time" + + "github.com/pkg/errors" cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" + tmquery "github.com/tendermint/tendermint/libs/pubsub/query" nm "github.com/tendermint/tendermint/node" "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -24,9 +30,17 @@ are compiled in process. For real clients, you probably want to use client.HTTP. For more powerful control during testing, you probably want the "client/mock" package. + +You can subscribe for any event published by Tendermint using Subscribe method. +Note delivery is best-effort. If you don't read events fast enough, Tendermint +might cancel the subscription. The client will attempt to resubscribe (you +don't need to do anything). It will keep trying indefinitely with exponential +backoff (10ms -> 20ms -> 40ms) until successful. */ type Local struct { *types.EventBus + Logger log.Logger + ctx *rpctypes.Context } // NewLocal configures a client that calls the Node directly. @@ -39,113 +53,189 @@ func NewLocal(node *nm.Node) *Local { node.ConfigureRPC() return &Local{ EventBus: node.EventBus(), + Logger: log.NewNopLogger(), + ctx: &rpctypes.Context{}, } } var ( _ Client = (*Local)(nil) - _ NetworkClient = Local{} + _ NetworkClient = (*Local)(nil) _ EventsClient = (*Local)(nil) ) -func (Local) Status() (*ctypes.ResultStatus, error) { - return core.Status() +// SetLogger allows to set a logger on the client. +func (c *Local) SetLogger(l log.Logger) { + c.Logger = l } -func (Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) { - return core.ABCIInfo() +func (c *Local) Status() (*ctypes.ResultStatus, error) { + return core.Status(c.ctx) +} + +func (c *Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) { + return core.ABCIInfo(c.ctx) } func (c *Local) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) } -func (Local) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { - return core.ABCIQuery(path, data, opts.Height, opts.Prove) +func (c *Local) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { + return core.ABCIQuery(c.ctx, path, data, opts.Height, opts.Prove) } -func (Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { - return core.BroadcastTxCommit(tx) +func (c *Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return core.BroadcastTxCommit(c.ctx, tx) } -func (Local) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - return core.BroadcastTxAsync(tx) +func (c *Local) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + return core.BroadcastTxAsync(c.ctx, tx) } -func (Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - return core.BroadcastTxSync(tx) +func (c *Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + return core.BroadcastTxSync(c.ctx, tx) } -func (Local) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { - return core.UnconfirmedTxs(limit) +func (c *Local) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { + return core.UnconfirmedTxs(c.ctx, limit) } -func (Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { - return core.NumUnconfirmedTxs() +func (c *Local) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { + return core.NumUnconfirmedTxs(c.ctx) } -func (Local) NetInfo() (*ctypes.ResultNetInfo, error) { - return core.NetInfo() +func (c *Local) NetInfo() (*ctypes.ResultNetInfo, error) { + return core.NetInfo(c.ctx) } -func (Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { - return core.DumpConsensusState() +func (c *Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { + return core.DumpConsensusState(c.ctx) } -func (Local) ConsensusState() (*ctypes.ResultConsensusState, error) { - return core.ConsensusState() +func (c *Local) ConsensusState() (*ctypes.ResultConsensusState, error) { + return core.ConsensusState(c.ctx) } -func (Local) Health() (*ctypes.ResultHealth, error) { - return core.Health() +func (c *Local) Health() (*ctypes.ResultHealth, error) { + return core.Health(c.ctx) } -func (Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { - return core.UnsafeDialSeeds(seeds) +func (c *Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { + return core.UnsafeDialSeeds(c.ctx, seeds) } -func (Local) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { - return core.UnsafeDialPeers(peers, persistent) +func (c *Local) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { + return core.UnsafeDialPeers(c.ctx, peers, persistent) } -func (Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { - return core.BlockchainInfo(minHeight, maxHeight) +func (c *Local) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { + return core.BlockchainInfo(c.ctx, minHeight, maxHeight) } -func (Local) Genesis() (*ctypes.ResultGenesis, error) { - return core.Genesis() +func (c *Local) Genesis() (*ctypes.ResultGenesis, error) { + return core.Genesis(c.ctx) } -func (Local) Block(height *int64) (*ctypes.ResultBlock, error) { - return core.Block(height) +func (c *Local) Block(height *int64) (*ctypes.ResultBlock, error) { + return core.Block(c.ctx, height) } -func (Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { - return core.BlockResults(height) +func (c *Local) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { + return core.BlockResults(c.ctx, height) } -func (Local) Commit(height *int64) (*ctypes.ResultCommit, error) { - return core.Commit(height) +func (c *Local) Commit(height *int64) (*ctypes.ResultCommit, error) { + return core.Commit(c.ctx, height) } -func (Local) Validators(height *int64) (*ctypes.ResultValidators, error) { - return core.Validators(height) +func (c *Local) Validators(height *int64) (*ctypes.ResultValidators, error) { + return core.Validators(c.ctx, height) } -func (Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { - return core.Tx(hash, prove) +func (c *Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { + return core.Tx(c.ctx, hash, prove) } -func (Local) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { - return core.TxSearch(query, prove, page, perPage) +func (c *Local) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { + return core.TxSearch(c.ctx, query, prove, page, perPage) } -func (c *Local) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (types.Subscription, error) { - return c.EventBus.Subscribe(ctx, subscriber, query, outCapacity...) +func (c *Local) Subscribe(ctx context.Context, subscriber, query string, outCapacity ...int) (out <-chan ctypes.ResultEvent, err error) { + q, err := tmquery.New(query) + if err != nil { + return nil, errors.Wrap(err, "failed to parse query") + } + sub, err := c.EventBus.Subscribe(ctx, subscriber, q) + if err != nil { + return nil, errors.Wrap(err, "failed to subscribe") + } + + outCap := 1 + if len(outCapacity) > 0 { + outCap = outCapacity[0] + } + + outc := make(chan ctypes.ResultEvent, outCap) + go c.eventsRoutine(sub, subscriber, q, outc) + + return outc, nil } -func (c *Local) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error { - return c.EventBus.Unsubscribe(ctx, subscriber, query) +func (c *Local) eventsRoutine(sub types.Subscription, subscriber string, q tmpubsub.Query, outc chan<- ctypes.ResultEvent) { + for { + select { + case msg := <-sub.Out(): + result := ctypes.ResultEvent{Query: q.String(), Data: msg.Data(), Tags: msg.Tags()} + if cap(outc) == 0 { + outc <- result + } else { + select { + case outc <- result: + default: + c.Logger.Error("wanted to publish ResultEvent, but out channel is full", "result", result, "query", result.Query) + } + } + case <-sub.Cancelled(): + if sub.Err() == tmpubsub.ErrUnsubscribed { + return + } + + c.Logger.Error("subscription was cancelled, resubscribing...", "err", sub.Err(), "query", q.String()) + sub = c.resubscribe(subscriber, q) + if sub == nil { // client was stopped + return + } + case <-c.Quit(): + return + } + } +} + +// Try to resubscribe with exponential backoff. +func (c *Local) resubscribe(subscriber string, q tmpubsub.Query) types.Subscription { + attempts := 0 + for { + if !c.IsRunning() { + return nil + } + + sub, err := c.EventBus.Subscribe(context.Background(), subscriber, q) + if err == nil { + return sub + } + + attempts++ + time.Sleep((10 << uint(attempts)) * time.Millisecond) // 10ms -> 20ms -> 40ms + } +} + +func (c *Local) Unsubscribe(ctx context.Context, subscriber, query string) error { + q, err := tmquery.New(query) + if err != nil { + return errors.Wrap(err, "failed to parse query") + } + return c.EventBus.Unsubscribe(ctx, subscriber, q) } func (c *Local) UnsubscribeAll(ctx context.Context, subscriber string) error { diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index ef2d4f197..9c0eb75b8 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -21,6 +21,7 @@ import ( "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -76,11 +77,11 @@ func (c Call) GetResponse(args interface{}) (interface{}, error) { } func (c Client) Status() (*ctypes.ResultStatus, error) { - return core.Status() + return core.Status(&rpctypes.Context{}) } func (c Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) { - return core.ABCIInfo() + return core.ABCIInfo(&rpctypes.Context{}) } func (c Client) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { @@ -88,49 +89,49 @@ func (c Client) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQue } func (c Client) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts client.ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { - return core.ABCIQuery(path, data, opts.Height, opts.Prove) + return core.ABCIQuery(&rpctypes.Context{}, path, data, opts.Height, opts.Prove) } func (c Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { - return core.BroadcastTxCommit(tx) + return core.BroadcastTxCommit(&rpctypes.Context{}, tx) } func (c Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - return core.BroadcastTxAsync(tx) + return core.BroadcastTxAsync(&rpctypes.Context{}, tx) } func (c Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - return core.BroadcastTxSync(tx) + return core.BroadcastTxSync(&rpctypes.Context{}, tx) } func (c Client) NetInfo() (*ctypes.ResultNetInfo, error) { - return core.NetInfo() + return core.NetInfo(&rpctypes.Context{}) } func (c Client) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { - return core.UnsafeDialSeeds(seeds) + return core.UnsafeDialSeeds(&rpctypes.Context{}, seeds) } func (c Client) DialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { - return core.UnsafeDialPeers(peers, persistent) + return core.UnsafeDialPeers(&rpctypes.Context{}, peers, persistent) } func (c Client) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { - return core.BlockchainInfo(minHeight, maxHeight) + return core.BlockchainInfo(&rpctypes.Context{}, minHeight, maxHeight) } func (c Client) Genesis() (*ctypes.ResultGenesis, error) { - return core.Genesis() + return core.Genesis(&rpctypes.Context{}) } func (c Client) Block(height *int64) (*ctypes.ResultBlock, error) { - return core.Block(height) + return core.Block(&rpctypes.Context{}, height) } func (c Client) Commit(height *int64) (*ctypes.ResultCommit, error) { - return core.Commit(height) + return core.Commit(&rpctypes.Context{}, height) } func (c Client) Validators(height *int64) (*ctypes.ResultValidators, error) { - return core.Validators(height) + return core.Validators(&rpctypes.Context{}, height) } diff --git a/rpc/core/abci.go b/rpc/core/abci.go index aa6089b60..ce15ac143 100644 --- a/rpc/core/abci.go +++ b/rpc/core/abci.go @@ -5,6 +5,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/proxy" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) // Query the application for some information. @@ -52,7 +53,7 @@ import ( // | data | []byte | false | true | Data | // | height | int64 | 0 | false | Height (0 means latest) | // | prove | bool | false | false | Includes proof if true | -func ABCIQuery(path string, data cmn.HexBytes, height int64, prove bool) (*ctypes.ResultABCIQuery, error) { +func ABCIQuery(ctx *rpctypes.Context, path string, data cmn.HexBytes, height int64, prove bool) (*ctypes.ResultABCIQuery, error) { resQuery, err := proxyAppQuery.QuerySync(abci.RequestQuery{ Path: path, Data: data, @@ -96,7 +97,7 @@ func ABCIQuery(path string, data cmn.HexBytes, height int64, prove bool) (*ctype // "jsonrpc": "2.0" // } // ``` -func ABCIInfo() (*ctypes.ResultABCIInfo, error) { +func ABCIInfo(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) { resInfo, err := proxyAppQuery.InfoSync(proxy.RequestInfo) if err != nil { return nil, err diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 906aea7be..40b6811db 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -5,6 +5,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -68,7 +69,7 @@ import ( // ``` // // -func BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { +func BlockchainInfo(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { // maximum 20 block metas const limit int64 = 20 @@ -226,7 +227,7 @@ func filterMinMax(height, min, max, limit int64) (int64, int64, error) { // "jsonrpc": "2.0" // } // ``` -func Block(heightPtr *int64) (*ctypes.ResultBlock, error) { +func Block(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlock, error) { storeHeight := blockStore.Height() height, err := getHeight(storeHeight, heightPtr) if err != nil { @@ -313,7 +314,7 @@ func Block(heightPtr *int64) (*ctypes.ResultBlock, error) { // "jsonrpc": "2.0" // } // ``` -func Commit(heightPtr *int64) (*ctypes.ResultCommit, error) { +func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, error) { storeHeight := blockStore.Height() height, err := getHeight(storeHeight, heightPtr) if err != nil { @@ -372,7 +373,7 @@ func Commit(heightPtr *int64) (*ctypes.ResultCommit, error) { // ] // } // ``` -func BlockResults(heightPtr *int64) (*ctypes.ResultBlockResults, error) { +func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { storeHeight := blockStore.Height() height, err := getHeight(storeHeight, heightPtr) if err != nil { diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 81694b7ee..b8a91f107 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -3,6 +3,7 @@ package core import ( cm "github.com/tendermint/tendermint/consensus" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -47,7 +48,7 @@ import ( // "jsonrpc": "2.0" // } // ``` -func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) { +func Validators(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultValidators, error) { // The latest validator that we know is the // NextValidator of the last block. height := consensusState.GetState().LastBlockHeight + 1 @@ -200,7 +201,7 @@ func Validators(heightPtr *int64) (*ctypes.ResultValidators, error) { // } // } // ``` -func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { +func DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState, error) { // Get Peer consensus states. peers := p2pPeers.Peers().List() peerStates := make([]ctypes.PeerStateInfo, len(peers)) @@ -277,7 +278,7 @@ func DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { // } //} //``` -func ConsensusState() (*ctypes.ResultConsensusState, error) { +func ConsensusState(ctx *rpctypes.Context) (*ctypes.ResultConsensusState, error) { // Get self round state. bz, err := consensusState.GetRoundStateSimpleJSON() return &ctypes.ResultConsensusState{RoundState: bz}, err @@ -320,7 +321,7 @@ func ConsensusState() (*ctypes.ResultConsensusState, error) { // } // } // ``` -func ConsensusParams(heightPtr *int64) (*ctypes.ResultConsensusParams, error) { +func ConsensusParams(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultConsensusParams, error) { height := consensusState.GetState().LastBlockHeight + 1 height, err := getHeight(height, heightPtr) if err != nil { diff --git a/rpc/core/dev.go b/rpc/core/dev.go index 0b5154769..71f284f89 100644 --- a/rpc/core/dev.go +++ b/rpc/core/dev.go @@ -5,16 +5,19 @@ import ( "runtime/pprof" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) -func UnsafeFlushMempool() (*ctypes.ResultUnsafeFlushMempool, error) { +// UnsafeFlushMempool removes all transactions from the mempool. +func UnsafeFlushMempool(ctx *rpctypes.Context) (*ctypes.ResultUnsafeFlushMempool, error) { mempool.Flush() return &ctypes.ResultUnsafeFlushMempool{}, nil } var profFile *os.File -func UnsafeStartCPUProfiler(filename string) (*ctypes.ResultUnsafeProfile, error) { +// UnsafeStartCPUProfiler starts a pprof profiler using the given filename. +func UnsafeStartCPUProfiler(ctx *rpctypes.Context, filename string) (*ctypes.ResultUnsafeProfile, error) { var err error profFile, err = os.Create(filename) if err != nil { @@ -27,7 +30,8 @@ func UnsafeStartCPUProfiler(filename string) (*ctypes.ResultUnsafeProfile, error return &ctypes.ResultUnsafeProfile{}, nil } -func UnsafeStopCPUProfiler() (*ctypes.ResultUnsafeProfile, error) { +// UnsafeStopCPUProfiler stops the running pprof profiler. +func UnsafeStopCPUProfiler(ctx *rpctypes.Context) (*ctypes.ResultUnsafeProfile, error) { pprof.StopCPUProfile() if err := profFile.Close(); err != nil { return nil, err @@ -35,7 +39,8 @@ func UnsafeStopCPUProfiler() (*ctypes.ResultUnsafeProfile, error) { return &ctypes.ResultUnsafeProfile{}, nil } -func UnsafeWriteHeapProfile(filename string) (*ctypes.ResultUnsafeProfile, error) { +// UnsafeWriteHeapProfile dumps a heap profile to the given filename. +func UnsafeWriteHeapProfile(ctx *rpctypes.Context, filename string) (*ctypes.ResultUnsafeProfile, error) { memProfFile, err := os.Create(filename) if err != nil { return nil, err diff --git a/rpc/core/events.go b/rpc/core/events.go index 22c7ea782..3ea33fa84 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -6,10 +6,10 @@ import ( "github.com/pkg/errors" + tmpubsub "github.com/tendermint/tendermint/libs/pubsub" tmquery "github.com/tendermint/tendermint/libs/pubsub/query" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctypes "github.com/tendermint/tendermint/rpc/lib/types" - tmtypes "github.com/tendermint/tendermint/types" ) // Subscribe for events via WebSocket. @@ -90,8 +90,15 @@ import ( // | query | string | "" | true | Query | // // -func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscribe, error) { - addr := wsCtx.GetRemoteAddr() +func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, error) { + addr := ctx.RemoteAddr() + + if eventBus.NumClients() >= config.MaxSubscriptionClients { + return nil, fmt.Errorf("max_subscription_clients %d reached", config.MaxSubscriptionClients) + } else if eventBus.NumClientSubscriptions(addr) >= config.MaxSubscriptionsPerClient { + return nil, fmt.Errorf("max_subscriptions_per_client %d reached", config.MaxSubscriptionsPerClient) + } + logger.Info("Subscribe to query", "remote", addr, "query", query) q, err := tmquery.New(query) @@ -99,9 +106,9 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscri return nil, errors.Wrap(err, "failed to parse query") } - ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) + subCtx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) defer cancel() - sub, err := eventBusFor(wsCtx).Subscribe(ctx, addr, q) + sub, err := eventBus.Subscribe(subCtx, addr, q) if err != nil { return nil, err } @@ -111,18 +118,26 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscri select { case msg := <-sub.Out(): resultEvent := &ctypes.ResultEvent{Query: query, Data: msg.Data(), Tags: msg.Tags()} - wsCtx.TryWriteRPCResponse( + ctx.WSConn.TryWriteRPCResponse( rpctypes.NewRPCSuccessResponse( - wsCtx.Codec(), - rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", wsCtx.Request.ID)), + ctx.WSConn.Codec(), + rpctypes.JSONRPCStringID(fmt.Sprintf("%v#event", ctx.JSONReq.ID)), resultEvent, )) case <-sub.Cancelled(): - wsCtx.TryWriteRPCResponse( - rpctypes.RPCServerError(rpctypes.JSONRPCStringID( - fmt.Sprintf("%v#event", wsCtx.Request.ID)), - fmt.Errorf("subscription was cancelled (reason: %v)", sub.Err()), - )) + if sub.Err() != tmpubsub.ErrUnsubscribed { + var reason string + if sub.Err() == nil { + reason = "Tendermint exited" + } else { + reason = sub.Err().Error() + } + ctx.WSConn.TryWriteRPCResponse( + rpctypes.RPCServerError(rpctypes.JSONRPCStringID( + fmt.Sprintf("%v#event", ctx.JSONReq.ID)), + fmt.Errorf("subscription was cancelled (reason: %s)", reason), + )) + } return } } @@ -161,14 +176,14 @@ func Subscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultSubscri // | query | string | "" | true | Query | // // -func Unsubscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultUnsubscribe, error) { - addr := wsCtx.GetRemoteAddr() +func Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe, error) { + addr := ctx.RemoteAddr() logger.Info("Unsubscribe from query", "remote", addr, "query", query) q, err := tmquery.New(query) if err != nil { return nil, errors.Wrap(err, "failed to parse query") } - err = eventBusFor(wsCtx).Unsubscribe(context.Background(), addr, q) + err = eventBus.Unsubscribe(context.Background(), addr, q) if err != nil { return nil, err } @@ -199,20 +214,12 @@ func Unsubscribe(wsCtx rpctypes.WSRPCContext, query string) (*ctypes.ResultUnsub // ``` // // -func UnsubscribeAll(wsCtx rpctypes.WSRPCContext) (*ctypes.ResultUnsubscribe, error) { - addr := wsCtx.GetRemoteAddr() +func UnsubscribeAll(ctx *rpctypes.Context) (*ctypes.ResultUnsubscribe, error) { + addr := ctx.RemoteAddr() logger.Info("Unsubscribe from all", "remote", addr) - err := eventBusFor(wsCtx).UnsubscribeAll(context.Background(), addr) + err := eventBus.UnsubscribeAll(context.Background(), addr) if err != nil { return nil, err } return &ctypes.ResultUnsubscribe{}, nil } - -func eventBusFor(wsCtx rpctypes.WSRPCContext) tmtypes.EventBusSubscriber { - es := wsCtx.GetEventSubscriber() - if es == nil { - es = eventBus - } - return es -} diff --git a/rpc/core/health.go b/rpc/core/health.go index eeb8686bd..41186a045 100644 --- a/rpc/core/health.go +++ b/rpc/core/health.go @@ -2,6 +2,7 @@ package core import ( ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) // Get node health. Returns empty result (200 OK) on success, no response - in @@ -31,6 +32,6 @@ import ( // "jsonrpc": "2.0" // } // ``` -func Health() (*ctypes.ResultHealth, error) { +func Health(ctx *rpctypes.Context) (*ctypes.ResultHealth, error) { return &ctypes.ResultHealth{}, nil } diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 42aa56afd..6ebdbcfce 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -9,7 +9,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" ctypes "github.com/tendermint/tendermint/rpc/core/types" - rpcserver "github.com/tendermint/tendermint/rpc/lib/server" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/types" ) @@ -59,7 +59,7 @@ import ( // | Parameter | Type | Default | Required | Description | // |-----------+------+---------+----------+-----------------| // | tx | Tx | nil | true | The transaction | -func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { +func BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { err := mempool.CheckTx(tx, nil) if err != nil { return nil, err @@ -108,7 +108,7 @@ func BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { // | Parameter | Type | Default | Required | Description | // |-----------+------+---------+----------+-----------------| // | tx | Tx | nil | true | The transaction | -func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { +func BroadcastTxSync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { resCh := make(chan *abci.Response, 1) err := mempool.CheckTx(tx, func(res *abci.Response) { resCh <- res @@ -128,6 +128,11 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { // Returns with the responses from CheckTx and DeliverTx. // +// IMPORTANT: use only for testing and development. In production, use +// BroadcastTxSync or BroadcastTxAsync. You can subscribe for the transaction +// result using JSONRPC via a websocket. See +// https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html +// // CONTRACT: only returns error if mempool.CheckTx() errs or if we timeout // waiting for tx to commit. // @@ -182,18 +187,26 @@ func BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { // | Parameter | Type | Default | Required | Description | // |-----------+------+---------+----------+-----------------| // | tx | Tx | nil | true | The transaction | -func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { +func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + subscriber := ctx.RemoteAddr() + + if eventBus.NumClients() >= config.MaxSubscriptionClients { + return nil, fmt.Errorf("max_subscription_clients %d reached", config.MaxSubscriptionClients) + } else if eventBus.NumClientSubscriptions(subscriber) >= config.MaxSubscriptionsPerClient { + return nil, fmt.Errorf("max_subscriptions_per_client %d reached", config.MaxSubscriptionsPerClient) + } + // Subscribe to tx being committed in block. - ctx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) + subCtx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) defer cancel() q := types.EventQueryTxFor(tx) - deliverTxSub, err := eventBus.Subscribe(ctx, "mempool", q) + deliverTxSub, err := eventBus.Subscribe(subCtx, subscriber, q) if err != nil { err = errors.Wrap(err, "failed to subscribe to tx") logger.Error("Error on broadcast_tx_commit", "err", err) return nil, err } - defer eventBus.Unsubscribe(context.Background(), "mempool", q) + defer eventBus.Unsubscribe(context.Background(), subscriber, q) // Broadcast tx and wait for CheckTx result checkTxResCh := make(chan *abci.Response, 1) @@ -215,8 +228,6 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { } // Wait for the tx to be included in a block or timeout. - // TODO: configurable? - var deliverTxTimeout = rpcserver.WriteTimeout / 2 select { case msg := <-deliverTxSub.Out(): // The tx was included in a block. deliverTxRes := msg.Data().(types.EventDataTx) @@ -227,14 +238,20 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { Height: deliverTxRes.Height, }, nil case <-deliverTxSub.Cancelled(): - err = errors.New("deliverTxSub was cancelled. Did the Tendermint stop?") + var reason string + if deliverTxSub.Err() == nil { + reason = "Tendermint exited" + } else { + reason = deliverTxSub.Err().Error() + } + err = fmt.Errorf("deliverTxSub was cancelled (reason: %s)", reason) logger.Error("Error on broadcastTxCommit", "err", err) return &ctypes.ResultBroadcastTxCommit{ CheckTx: *checkTxRes, DeliverTx: abci.ResponseDeliverTx{}, Hash: tx.Hash(), }, err - case <-time.After(deliverTxTimeout): + case <-time.After(config.TimeoutBroadcastTxCommit): err = errors.New("Timed out waiting for tx to be included in a block") logger.Error("Error on broadcastTxCommit", "err", err) return &ctypes.ResultBroadcastTxCommit{ @@ -281,7 +298,8 @@ func BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { // | Parameter | Type | Default | Required | Description | // |-----------+------+---------+----------+--------------------------------------| // | limit | int | 30 | false | Maximum number of entries (max: 100) | -func UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { +// ``` +func UnconfirmedTxs(ctx *rpctypes.Context, limit int) (*ctypes.ResultUnconfirmedTxs, error) { // reuse per_page validator limit = validatePerPage(limit) @@ -323,7 +341,7 @@ func UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { // } // } // ``` -func NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { +func NumUnconfirmedTxs(ctx *rpctypes.Context) (*ctypes.ResultUnconfirmedTxs, error) { return &ctypes.ResultUnconfirmedTxs{ Count: mempool.Size(), Total: mempool.Size(), diff --git a/rpc/core/net.go b/rpc/core/net.go index e920ea7c3..23bc40e88 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -7,6 +7,7 @@ import ( "github.com/tendermint/tendermint/p2p" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) // Get network info. @@ -153,7 +154,7 @@ import ( // ... // } // ``` -func NetInfo() (*ctypes.ResultNetInfo, error) { +func NetInfo(ctx *rpctypes.Context) (*ctypes.ResultNetInfo, error) { out, in, _ := p2pPeers.NumPeers() peers := make([]ctypes.Peer, 0, out+in) for _, peer := range p2pPeers.Peers().List() { @@ -179,7 +180,7 @@ func NetInfo() (*ctypes.ResultNetInfo, error) { }, nil } -func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { +func UnsafeDialSeeds(ctx *rpctypes.Context, seeds []string) (*ctypes.ResultDialSeeds, error) { if len(seeds) == 0 { return &ctypes.ResultDialSeeds{}, errors.New("No seeds provided") } @@ -192,7 +193,7 @@ func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { return &ctypes.ResultDialSeeds{Log: "Dialing seeds in progress. See /net_info for details"}, nil } -func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { +func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*ctypes.ResultDialPeers, error) { if len(peers) == 0 { return &ctypes.ResultDialPeers{}, errors.New("No peers provided") } @@ -247,6 +248,6 @@ func UnsafeDialPeers(peers []string, persistent bool) (*ctypes.ResultDialPeers, // "jsonrpc": "2.0" // } // ``` -func Genesis() (*ctypes.ResultGenesis, error) { +func Genesis(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { return &ctypes.ResultGenesis{Genesis: genDoc}, nil } diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 236495443..0b7603442 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -1,6 +1,7 @@ package core import ( + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" dbm "github.com/tendermint/tendermint/libs/db" @@ -71,6 +72,8 @@ var ( mempool *mempl.Mempool logger log.Logger + + config cfg.RPCConfig ) func SetStateDB(db dbm.DB) { @@ -133,6 +136,11 @@ func SetEventBus(b *types.EventBus) { eventBus = b } +// SetConfig sets an RPCConfig. +func SetConfig(c cfg.RPCConfig) { + config = c +} + func validatePage(page, perPage, totalCount int) int { if perPage < 1 { return 1 diff --git a/rpc/core/status.go b/rpc/core/status.go index ae22ecd35..aab864667 100644 --- a/rpc/core/status.go +++ b/rpc/core/status.go @@ -7,6 +7,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -70,7 +71,7 @@ import ( // } // } // ``` -func Status() (*ctypes.ResultStatus, error) { +func Status(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { var latestHeight int64 if consensusReactor.FastSync() { latestHeight = blockStore.Height() diff --git a/rpc/core/tx.go b/rpc/core/tx.go index aa439218b..575553f85 100644 --- a/rpc/core/tx.go +++ b/rpc/core/tx.go @@ -7,6 +7,7 @@ import ( tmquery "github.com/tendermint/tendermint/libs/pubsub/query" ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" "github.com/tendermint/tendermint/state/txindex/null" "github.com/tendermint/tendermint/types" ) @@ -77,7 +78,7 @@ import ( // - `index`: `int` - index of the transaction // - `height`: `int` - height of the block where this transaction was in // - `hash`: `[]byte` - hash of the transaction -func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { +func Tx(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { // if index is disabled, return error if _, ok := txIndexer.(*null.TxIndex); ok { @@ -183,7 +184,7 @@ func Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { // - `index`: `int` - index of the transaction // - `height`: `int` - height of the block where this transaction was in // - `hash`: `[]byte` - hash of the transaction -func TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { +func TxSearch(ctx *rpctypes.Context, query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { // if index is disabled, return error if _, ok := txIndexer.(*null.TxIndex); ok { return nil, fmt.Errorf("Transaction indexing is disabled") diff --git a/rpc/grpc/api.go b/rpc/grpc/api.go index 0b840e3e9..741d63aff 100644 --- a/rpc/grpc/api.go +++ b/rpc/grpc/api.go @@ -5,6 +5,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" core "github.com/tendermint/tendermint/rpc/core" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) type broadcastAPI struct { @@ -16,12 +17,14 @@ func (bapi *broadcastAPI) Ping(ctx context.Context, req *RequestPing) (*Response } func (bapi *broadcastAPI) BroadcastTx(ctx context.Context, req *RequestBroadcastTx) (*ResponseBroadcastTx, error) { - res, err := core.BroadcastTxCommit(req.Tx) + // NOTE: there's no way to get client's remote address + // see https://stackoverflow.com/questions/33684570/session-and-remote-ip-address-in-grpc-go + res, err := core.BroadcastTxCommit(&rpctypes.Context{}, req.Tx) if err != nil { return nil, err } - return &ResponseBroadcastTx{ + return &ResponseBroadcastTx{ CheckTx: &abci.ResponseCheckTx{ Code: res.CheckTx.Code, Data: res.CheckTx.Data, diff --git a/rpc/grpc/client_server.go b/rpc/grpc/client_server.go index 2bc89864d..922016dd5 100644 --- a/rpc/grpc/client_server.go +++ b/rpc/grpc/client_server.go @@ -14,7 +14,8 @@ type Config struct { MaxOpenConnections int } -// StartGRPCServer starts a new gRPC BroadcastAPIServer using the given net.Listener. +// StartGRPCServer starts a new gRPC BroadcastAPIServer using the given +// net.Listener. // NOTE: This function blocks - you may want to call it in a go-routine. func StartGRPCServer(ln net.Listener) error { grpcServer := grpc.NewServer() diff --git a/rpc/grpc/grpc_test.go b/rpc/grpc/grpc_test.go index b82e52226..1e1f25401 100644 --- a/rpc/grpc/grpc_test.go +++ b/rpc/grpc/grpc_test.go @@ -25,9 +25,8 @@ func TestMain(m *testing.M) { } func TestBroadcastTx(t *testing.T) { - require := require.New(t) res, err := rpctest.GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{Tx: []byte("this is a tx")}) - require.Nil(err, "%+v", err) - require.EqualValues(0, res.CheckTx.Code) - require.EqualValues(0, res.DeliverTx.Code) + require.NoError(t, err) + require.EqualValues(t, 0, res.CheckTx.Code) + require.EqualValues(t, 0, res.DeliverTx.Code) } diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 794ab462c..68c134a73 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -63,23 +63,23 @@ var Routes = map[string]*server.RPCFunc{ // Amino codec required to encode/decode everything above. var RoutesCdc = amino.NewCodec() -func EchoResult(v string) (*ResultEcho, error) { +func EchoResult(ctx *types.Context, v string) (*ResultEcho, error) { return &ResultEcho{v}, nil } -func EchoWSResult(wsCtx types.WSRPCContext, v string) (*ResultEcho, error) { +func EchoWSResult(ctx *types.Context, v string) (*ResultEcho, error) { return &ResultEcho{v}, nil } -func EchoIntResult(v int) (*ResultEchoInt, error) { +func EchoIntResult(ctx *types.Context, v int) (*ResultEchoInt, error) { return &ResultEchoInt{v}, nil } -func EchoBytesResult(v []byte) (*ResultEchoBytes, error) { +func EchoBytesResult(ctx *types.Context, v []byte) (*ResultEchoBytes, error) { return &ResultEchoBytes{v}, nil } -func EchoDataBytesResult(v cmn.HexBytes) (*ResultEchoDataBytes, error) { +func EchoDataBytesResult(ctx *types.Context, v cmn.HexBytes) (*ResultEchoDataBytes, error) { return &ResultEchoDataBytes{v}, nil } diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 80eb4308a..36ea47da7 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -2,7 +2,6 @@ package rpcserver import ( "bytes" - "context" "encoding/hex" "encoding/json" "fmt" @@ -129,20 +128,26 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, cdc *amino.Codec, logger lo WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID, errors.Errorf("Path %s is invalid", r.URL.Path))) return } + rpcFunc := funcMap[request.Method] if rpcFunc == nil || rpcFunc.ws { WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID)) return } - var args []reflect.Value + + ctx := &types.Context{JSONReq: &request, HTTPReq: r} + args := []reflect.Value{reflect.ValueOf(ctx)} if len(request.Params) > 0 { - args, err = jsonParamsToArgsRPC(rpcFunc, cdc, request.Params) + fnArgs, err := jsonParamsToArgs(rpcFunc, cdc, request.Params) if err != nil { WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) return } + args = append(args, fnArgs...) } + returns := rpcFunc.f.Call(args) + logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) result, err := unreflectResult(returns) if err != nil { @@ -205,13 +210,14 @@ func arrayParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, params []json.RawMess return values, nil } -// `raw` is unparsed json (from json.RawMessage) encoding either a map or an array. -// `argsOffset` should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames). +// raw is unparsed json (from json.RawMessage) encoding either a map or an +// array. // // Example: -// rpcFunc.args = [rpctypes.WSRPCContext string] +// rpcFunc.args = [rpctypes.Context string] // rpcFunc.argNames = ["arg"] -func jsonParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, raw []byte, argsOffset int) ([]reflect.Value, error) { +func jsonParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, raw []byte) ([]reflect.Value, error) { + const argsOffset = 1 // TODO: Make more efficient, perhaps by checking the first character for '{' or '['? // First, try to get the map. @@ -232,20 +238,6 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, raw []byte, argsOffset return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err) } -// Convert a []interface{} OR a map[string]interface{} to properly typed values -func jsonParamsToArgsRPC(rpcFunc *RPCFunc, cdc *amino.Codec, params json.RawMessage) ([]reflect.Value, error) { - return jsonParamsToArgs(rpcFunc, cdc, params, 0) -} - -// Same as above, but with the first param the websocket connection -func jsonParamsToArgsWS(rpcFunc *RPCFunc, cdc *amino.Codec, params json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) { - values, err := jsonParamsToArgs(rpcFunc, cdc, params, 1) - if err != nil { - return nil, err - } - return append([]reflect.Value{reflect.ValueOf(wsCtx)}, values...), nil -} - // rpc.json //----------------------------------------------------------------------------- // rpc.http @@ -258,15 +250,23 @@ func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(types.JSONRPCStringID(""))) } } + // All other endpoints return func(w http.ResponseWriter, r *http.Request) { logger.Debug("HTTP HANDLER", "req", r) - args, err := httpParamsToArgs(rpcFunc, cdc, r) + + ctx := &types.Context{HTTPReq: r} + args := []reflect.Value{reflect.ValueOf(ctx)} + + fnArgs, err := httpParamsToArgs(rpcFunc, cdc, r) if err != nil { WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(types.JSONRPCStringID(""), errors.Wrap(err, "Error converting http params to arguments"))) return } + args = append(args, fnArgs...) + returns := rpcFunc.f.Call(args) + logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns) result, err := unreflectResult(returns) if err != nil { @@ -280,10 +280,13 @@ func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func // Covert an http query to a list of properly typed values. // To be properly decoded the arg must be a concrete type from tendermint (if its an interface). func httpParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, r *http.Request) ([]reflect.Value, error) { - values := make([]reflect.Value, len(rpcFunc.args)) + // skip types.Context + const argsOffset = 1 + + values := make([]reflect.Value, len(rpcFunc.argNames)) for i, name := range rpcFunc.argNames { - argType := rpcFunc.args[i] + argType := rpcFunc.args[i+argsOffset] values[i] = reflect.Zero(argType) // set default for that type @@ -434,8 +437,8 @@ type wsConnection struct { // Send pings to server with this period. Must be less than readWait, but greater than zero. pingPeriod time.Duration - // object that is used to subscribe / unsubscribe from events - eventSub types.EventSubscriber + // callback which is called upon disconnect + onDisconnect func(remoteAddr string) } // NewWSConnection wraps websocket.Conn. @@ -468,12 +471,11 @@ func NewWSConnection( return wsc } -// EventSubscriber sets object that is used to subscribe / unsubscribe from -// events - not Goroutine-safe. If none given, default node's eventBus will be -// used. -func EventSubscriber(eventSub types.EventSubscriber) func(*wsConnection) { +// OnDisconnect sets a callback which is used upon disconnect - not +// Goroutine-safe. Nop by default. +func OnDisconnect(onDisconnect func(remoteAddr string)) func(*wsConnection) { return func(wsc *wsConnection) { - wsc.eventSub = eventSub + wsc.onDisconnect = onDisconnect } } @@ -527,8 +529,8 @@ func (wsc *wsConnection) OnStop() { // Both read and write loops close the websocket connection when they exit their loops. // The writeChan is never closed, to allow WriteRPCResponse() to fail. - if wsc.eventSub != nil { - wsc.eventSub.UnsubscribeAll(context.TODO(), wsc.remoteAddr) + if wsc.onDisconnect != nil { + wsc.onDisconnect(wsc.remoteAddr) } } @@ -538,11 +540,6 @@ func (wsc *wsConnection) GetRemoteAddr() string { return wsc.remoteAddr } -// GetEventSubscriber implements WSRPCConnection by returning event subscriber. -func (wsc *wsConnection) GetEventSubscriber() types.EventSubscriber { - return wsc.eventSub -} - // WriteRPCResponse pushes a response to the writeChan, and blocks until it is accepted. // It implements WSRPCConnection. It is Goroutine-safe. func (wsc *wsConnection) WriteRPCResponse(resp types.RPCResponse) { @@ -628,27 +625,23 @@ func (wsc *wsConnection) readRoutine() { } // Now, fetch the RPCFunc and execute it. - rpcFunc := wsc.funcMap[request.Method] if rpcFunc == nil { wsc.WriteRPCResponse(types.RPCMethodNotFoundError(request.ID)) continue } - var args []reflect.Value - if rpcFunc.ws { - wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc} - if len(request.Params) > 0 { - args, err = jsonParamsToArgsWS(rpcFunc, wsc.cdc, request.Params, wsCtx) - } - } else { - if len(request.Params) > 0 { - args, err = jsonParamsToArgsRPC(rpcFunc, wsc.cdc, request.Params) + + ctx := &types.Context{JSONReq: &request, WSConn: wsc} + args := []reflect.Value{reflect.ValueOf(ctx)} + if len(request.Params) > 0 { + fnArgs, err := jsonParamsToArgs(rpcFunc, wsc.cdc, request.Params) + if err != nil { + wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) + continue } + args = append(args, fnArgs...) } - if err != nil { - wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) - continue - } + returns := rpcFunc.f.Call(args) // TODO: Need to encode args/returns to string if we want to log them diff --git a/rpc/lib/server/handlers_test.go b/rpc/lib/server/handlers_test.go index b1d3c7888..f8ad06107 100644 --- a/rpc/lib/server/handlers_test.go +++ b/rpc/lib/server/handlers_test.go @@ -28,7 +28,7 @@ import ( func testMux() *http.ServeMux { funcMap := map[string]*rs.RPCFunc{ - "c": rs.NewRPCFunc(func(s string, i int) (string, error) { return "foo", nil }, "s,i"), + "c": rs.NewRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"), } cdc := amino.NewCodec() mux := http.NewServeMux() @@ -195,7 +195,7 @@ func TestWebsocketManagerHandler(t *testing.T) { func newWSServer() *httptest.Server { funcMap := map[string]*rs.RPCFunc{ - "c": rs.NewWSRPCFunc(func(wsCtx types.WSRPCContext, s string, i int) (string, error) { return "foo", nil }, "s,i"), + "c": rs.NewWSRPCFunc(func(ctx *types.Context, s string, i int) (string, error) { return "foo", nil }, "s,i"), } wm := rs.NewWebsocketManager(funcMap, amino.NewCodec()) wm.SetLogger(log.TestingLogger()) diff --git a/rpc/lib/server/parse_test.go b/rpc/lib/server/parse_test.go index 7b0aacdbe..9196bb71b 100644 --- a/rpc/lib/server/parse_test.go +++ b/rpc/lib/server/parse_test.go @@ -10,24 +10,23 @@ import ( "github.com/stretchr/testify/assert" amino "github.com/tendermint/go-amino" cmn "github.com/tendermint/tendermint/libs/common" + types "github.com/tendermint/tendermint/rpc/lib/types" ) func TestParseJSONMap(t *testing.T) { - assert := assert.New(t) - input := []byte(`{"value":"1234","height":22}`) // naive is float,string var p1 map[string]interface{} err := json.Unmarshal(input, &p1) - if assert.Nil(err) { + if assert.Nil(t, err) { h, ok := p1["height"].(float64) - if assert.True(ok, "%#v", p1["height"]) { - assert.EqualValues(22, h) + if assert.True(t, ok, "%#v", p1["height"]) { + assert.EqualValues(t, 22, h) } v, ok := p1["value"].(string) - if assert.True(ok, "%#v", p1["value"]) { - assert.EqualValues("1234", v) + if assert.True(t, ok, "%#v", p1["value"]) { + assert.EqualValues(t, "1234", v) } } @@ -38,14 +37,14 @@ func TestParseJSONMap(t *testing.T) { "height": &tmp, } err = json.Unmarshal(input, &p2) - if assert.Nil(err) { + if assert.Nil(t, err) { h, ok := p2["height"].(float64) - if assert.True(ok, "%#v", p2["height"]) { - assert.EqualValues(22, h) + if assert.True(t, ok, "%#v", p2["height"]) { + assert.EqualValues(t, 22, h) } v, ok := p2["value"].(string) - if assert.True(ok, "%#v", p2["value"]) { - assert.EqualValues("1234", v) + if assert.True(t, ok, "%#v", p2["value"]) { + assert.EqualValues(t, "1234", v) } } @@ -60,14 +59,14 @@ func TestParseJSONMap(t *testing.T) { Value: &cmn.HexBytes{}, } err = json.Unmarshal(input, &p3) - if assert.Nil(err) { + if assert.Nil(t, err) { h, ok := p3.Height.(*int) - if assert.True(ok, "%#v", p3.Height) { - assert.Equal(22, *h) + if assert.True(t, ok, "%#v", p3.Height) { + assert.Equal(t, 22, *h) } v, ok := p3.Value.(*cmn.HexBytes) - if assert.True(ok, "%#v", p3.Value) { - assert.EqualValues([]byte{0x12, 0x34}, *v) + if assert.True(t, ok, "%#v", p3.Value) { + assert.EqualValues(t, []byte{0x12, 0x34}, *v) } } @@ -77,46 +76,44 @@ func TestParseJSONMap(t *testing.T) { Height int `json:"height"` }{} err = json.Unmarshal(input, &p4) - if assert.Nil(err) { - assert.EqualValues(22, p4.Height) - assert.EqualValues([]byte{0x12, 0x34}, p4.Value) + if assert.Nil(t, err) { + assert.EqualValues(t, 22, p4.Height) + assert.EqualValues(t, []byte{0x12, 0x34}, p4.Value) } // so, let's use this trick... // dynamic keys on map, and we can deserialize to the desired types var p5 map[string]*json.RawMessage err = json.Unmarshal(input, &p5) - if assert.Nil(err) { + if assert.Nil(t, err) { var h int err = json.Unmarshal(*p5["height"], &h) - if assert.Nil(err) { - assert.Equal(22, h) + if assert.Nil(t, err) { + assert.Equal(t, 22, h) } var v cmn.HexBytes err = json.Unmarshal(*p5["value"], &v) - if assert.Nil(err) { - assert.Equal(cmn.HexBytes{0x12, 0x34}, v) + if assert.Nil(t, err) { + assert.Equal(t, cmn.HexBytes{0x12, 0x34}, v) } } } func TestParseJSONArray(t *testing.T) { - assert := assert.New(t) - input := []byte(`["1234",22]`) // naive is float,string var p1 []interface{} err := json.Unmarshal(input, &p1) - if assert.Nil(err) { + if assert.Nil(t, err) { v, ok := p1[0].(string) - if assert.True(ok, "%#v", p1[0]) { - assert.EqualValues("1234", v) + if assert.True(t, ok, "%#v", p1[0]) { + assert.EqualValues(t, "1234", v) } h, ok := p1[1].(float64) - if assert.True(ok, "%#v", p1[1]) { - assert.EqualValues(22, h) + if assert.True(t, ok, "%#v", p1[1]) { + assert.EqualValues(t, 22, h) } } @@ -124,22 +121,20 @@ func TestParseJSONArray(t *testing.T) { tmp := 0 p2 := []interface{}{&cmn.HexBytes{}, &tmp} err = json.Unmarshal(input, &p2) - if assert.Nil(err) { + if assert.Nil(t, err) { v, ok := p2[0].(*cmn.HexBytes) - if assert.True(ok, "%#v", p2[0]) { - assert.EqualValues([]byte{0x12, 0x34}, *v) + if assert.True(t, ok, "%#v", p2[0]) { + assert.EqualValues(t, []byte{0x12, 0x34}, *v) } h, ok := p2[1].(*int) - if assert.True(ok, "%#v", p2[1]) { - assert.EqualValues(22, *h) + if assert.True(t, ok, "%#v", p2[1]) { + assert.EqualValues(t, 22, *h) } } } func TestParseJSONRPC(t *testing.T) { - assert := assert.New(t) - - demo := func(height int, name string) {} + demo := func(ctx *types.Context, height int, name string) {} call := NewRPCFunc(demo, "height,name") cdc := amino.NewCodec() @@ -162,14 +157,14 @@ func TestParseJSONRPC(t *testing.T) { for idx, tc := range cases { i := strconv.Itoa(idx) data := []byte(tc.raw) - vals, err := jsonParamsToArgs(call, cdc, data, 0) + vals, err := jsonParamsToArgs(call, cdc, data) if tc.fail { - assert.NotNil(err, i) + assert.NotNil(t, err, i) } else { - assert.Nil(err, "%s: %+v", i, err) - if assert.Equal(2, len(vals), i) { - assert.Equal(tc.height, vals[0].Int(), i) - assert.Equal(tc.name, vals[1].String(), i) + assert.Nil(t, err, "%s: %+v", i, err) + if assert.Equal(t, 2, len(vals), i) { + assert.Equal(t, tc.height, vals[0].Int(), i) + assert.Equal(t, tc.name, vals[1].String(), i) } } @@ -177,8 +172,7 @@ func TestParseJSONRPC(t *testing.T) { } func TestParseURI(t *testing.T) { - - demo := func(height int, name string) {} + demo := func(ctx *types.Context, height int, name string) {} call := NewRPCFunc(demo, "height,name") cdc := amino.NewCodec() diff --git a/rpc/lib/test/main.go b/rpc/lib/test/main.go index b2f94580a..3afc1ac1a 100644 --- a/rpc/lib/test/main.go +++ b/rpc/lib/test/main.go @@ -6,16 +6,18 @@ import ( "os" amino "github.com/tendermint/go-amino" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" rpcserver "github.com/tendermint/tendermint/rpc/lib/server" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" ) var routes = map[string]*rpcserver.RPCFunc{ "hello_world": rpcserver.NewRPCFunc(HelloWorld, "name,num"), } -func HelloWorld(name string, num int) (Result, error) { +func HelloWorld(ctx *rpctypes.Context, name string, num int) (Result, error) { return Result{fmt.Sprintf("hi %s %d", name, num)}, nil } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index d4e82b10c..21623e41a 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -1,18 +1,15 @@ package rpctypes import ( - "context" "encoding/json" "fmt" + "net/http" "reflect" "strings" "github.com/pkg/errors" amino "github.com/tendermint/go-amino" - - tmpubsub "github.com/tendermint/tendermint/libs/pubsub" - tmtypes "github.com/tendermint/tendermint/types" ) // a wrapper to emulate a sum type: jsonrpcid = string | int @@ -236,30 +233,47 @@ func RPCServerError(id jsonrpcid, err error) RPCResponse { //---------------------------------------- -// *wsConnection implements this interface. +// WSRPCConnection represents a websocket connection. type WSRPCConnection interface { + // GetRemoteAddr returns a remote address of the connection. GetRemoteAddr() string + // WriteRPCResponse writes the resp onto connection (BLOCKING). WriteRPCResponse(resp RPCResponse) + // TryWriteRPCResponse tries to write the resp onto connection (NON-BLOCKING). TryWriteRPCResponse(resp RPCResponse) bool - GetEventSubscriber() EventSubscriber + // Codec returns an Amino codec used. Codec() *amino.Codec } -// websocket-only RPCFuncs take this as the first parameter. -type WSRPCContext struct { - Request RPCRequest - WSRPCConnection +// Context is the first parameter for all functions. It carries a json-rpc +// request, http request and websocket connection. +// +// - JSONReq is non-nil when JSONRPC is called over websocket or HTTP. +// - WSConn is non-nil when we're connected via a websocket. +// - HTTPReq is non-nil when URI or JSONRPC is called over HTTP. +type Context struct { + // json-rpc request + JSONReq *RPCRequest + // websocket connection + WSConn WSRPCConnection + // http request + HTTPReq *http.Request } -// EventSubscriber mirrors tendermint/tendermint/types.EventBusSubscriber -type EventSubscriber interface { - Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (tmtypes.Subscription, error) - Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error - UnsubscribeAll(ctx context.Context, subscriber string) error +// RemoteAddr returns either HTTPReq#RemoteAddr or result of the +// WSConn#GetRemoteAddr(). +func (ctx *Context) RemoteAddr() string { + if ctx.HTTPReq != nil { + return ctx.HTTPReq.RemoteAddr + } else if ctx.WSConn != nil { + return ctx.WSConn.GetRemoteAddr() + } + return "" } //---------------------------------------- // SOCKETS + // // Determine if its a unix or tcp socket. // If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port diff --git a/types/event_bus.go b/types/event_bus.go index 2aa84a4a0..da959090f 100644 --- a/types/event_bus.go +++ b/types/event_bus.go @@ -15,6 +15,9 @@ type EventBusSubscriber interface { Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (Subscription, error) Unsubscribe(ctx context.Context, subscriber string, query tmpubsub.Query) error UnsubscribeAll(ctx context.Context, subscriber string) error + + NumClients() int + NumClientSubscriptions(clientID string) int } type Subscription interface { @@ -58,6 +61,14 @@ func (b *EventBus) OnStop() { b.pubsub.Stop() } +func (b *EventBus) NumClients() int { + return b.pubsub.NumClients() +} + +func (b *EventBus) NumClientSubscriptions(clientID string) int { + return b.pubsub.NumClientSubscriptions(clientID) +} + func (b *EventBus) Subscribe(ctx context.Context, subscriber string, query tmpubsub.Query, outCapacity ...int) (Subscription, error) { return b.pubsub.Subscribe(ctx, subscriber, query, outCapacity...) } From 303557203402f0a1b6db4a55cb2c6562765058b1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 11 Mar 2019 22:52:09 +0400 Subject: [PATCH 100/259] cs: comment out log.Error to avoid TestReactorValidatorSetChanges timing out (#3401) --- consensus/state.go | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index d4a12a0c3..74ec092ff 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -671,8 +671,8 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { defer cs.mtx.Unlock() var ( - err error added bool + err error ) msg, peerID := mi.Msg, mi.PeerID switch msg := msg.(type) { @@ -714,11 +714,15 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) { // the peer is sending us CatchupCommit precommits. // We could make note of this and help filter in broadcastHasVoteMessage(). default: - cs.Logger.Error("Unknown msg type", reflect.TypeOf(msg)) + cs.Logger.Error("Unknown msg type", "type", reflect.TypeOf(msg)) + return } + if err != nil { - cs.Logger.Error("Error with msg", "height", cs.Height, "round", cs.Round, - "peer", peerID, "err", err, "msg", msg) + // Causes TestReactorValidatorSetChanges to timeout + // https://github.com/tendermint/tendermint/issues/3406 + // cs.Logger.Error("Error with msg", "height", cs.Height, "round", cs.Round, + // "peer", peerID, "err", err, "msg", msg) } } From 676212fa8fdd1ddf29e9c352129c97da3c113769 Mon Sep 17 00:00:00 2001 From: srmo Date: Mon, 11 Mar 2019 20:06:03 +0100 Subject: [PATCH 101/259] cmd: make sure to have 'testnet' create the data directory for nonvals (#3409) Fixes #3408 --- CHANGELOG_PENDING.md | 2 ++ cmd/tendermint/commands/testnet.go | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index a8998c99c..c4136b558 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -3,6 +3,7 @@ ** Special thanks to external contributors on this release: +@srmo ### BREAKING CHANGES: @@ -45,3 +46,4 @@ Special thanks to external contributors on this release: - [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection - [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) - [p2p] \#3369 do not panic when filter times out +- [cmd] \#3408 Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index c3ef86190..e34b8d305 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -115,6 +115,12 @@ func testnetFiles(cmd *cobra.Command, args []string) error { return err } + err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm) + if err != nil { + _ = os.RemoveAll(outputDir) + return err + } + initFilesWithConfig(config) } From ad3e990c6a424d939b181a2a7c911eb4592c0c79 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 11 Mar 2019 23:59:00 +0400 Subject: [PATCH 102/259] fix GO_VERSION in installation scripts (#3411) there is no such file https://storage.googleapis.com/golang/go1.12.0.linux-amd64.tar.gz Fixes #3405 --- scripts/install/install_tendermint_arm.sh | 2 +- scripts/install/install_tendermint_bsd.sh | 2 +- scripts/install/install_tendermint_ubuntu.sh | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/install/install_tendermint_arm.sh b/scripts/install/install_tendermint_arm.sh index 703a736f4..b260d8d07 100644 --- a/scripts/install/install_tendermint_arm.sh +++ b/scripts/install/install_tendermint_arm.sh @@ -5,7 +5,7 @@ REPO=github.com/tendermint/tendermint # change this to a specific release or branch BRANCH=master -GO_VERSION=1.12.0 +GO_VERSION=1.12 sudo apt-get update -y diff --git a/scripts/install/install_tendermint_bsd.sh b/scripts/install/install_tendermint_bsd.sh index ebada72ec..b76b94855 100644 --- a/scripts/install/install_tendermint_bsd.sh +++ b/scripts/install/install_tendermint_bsd.sh @@ -16,7 +16,7 @@ set BRANCH=master set REPO=github.com/tendermint/tendermint -set GO_VERSION=1.12.0 +set GO_VERSION=1.12 sudo pkg update diff --git a/scripts/install/install_tendermint_ubuntu.sh b/scripts/install/install_tendermint_ubuntu.sh index 20e611294..3fe6ea8ed 100644 --- a/scripts/install/install_tendermint_ubuntu.sh +++ b/scripts/install/install_tendermint_ubuntu.sh @@ -13,7 +13,7 @@ REPO=github.com/tendermint/tendermint # change this to a specific release or branch BRANCH=master -GO_VERSION=1.12.0 +GO_VERSION=1.12 sudo apt-get update -y sudo apt-get install -y make From e42f833fd4ce6cb886dbf594dcb2f3af61918325 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 12 Mar 2019 16:20:59 +0400 Subject: [PATCH 103/259] Merge master back to develop (#3412) * libs/db: close batch (#3397) ClevelDB requires closing when WriteBatch is no longer needed, https://godoc.org/github.com/jmhodges/levigo#WriteBatch.Close Fixes the memory leak in https://github.com/cosmos/cosmos-sdk/issues/3842 * update changelog and bump version to 0.30.2 --- CHANGELOG.md | 19 +++++++++++++++++++ version/version.go | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e8761aa..44ecdf388 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## v0.30.2 + +*March 10th, 2019* + +This release fixes a CLevelDB memory leak. It was happening because we were not +closing the WriteBatch object after use. See [levigo's +godoc](https://godoc.org/github.com/jmhodges/levigo#WriteBatch.Close) for the +Close method. Special thanks goes to @Stumble who both reported an issue in +[cosmos-sdk](https://github.com/cosmos/cosmos-sdk/issues/3842) and provided a +fix here. + +### BREAKING CHANGES: + +* Go API +- [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Add Close() method to Batch interface (@Stumble) + +### BUG FIXES: +- [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Fix CLevelDB memory leak (@Stumble) + ## v0.30.1 *February 20th, 2019* diff --git a/version/version.go b/version/version.go index 1f30978c1..1b0a36ae1 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.30.1" + TMCoreSemVer = "0.30.2" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From 85c023db88a8517587f0e8499b1e89f0eea1112d Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 12 Mar 2019 20:07:26 +0100 Subject: [PATCH 104/259] Prep release v0.31.0: - update changelog, reset pending - bump versions - add external contributors (partly manually) --- CHANGELOG.md | 50 ++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 31 ++------------------------- version/version.go | 2 +- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44ecdf388..20e5a6fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,55 @@ # Changelog +## v0.31.0 + +*March 13th, 2019* + +Special thanks to external contributors on this release: +@danil-lashin, @guagualvcha, @jleni, @siburu, @silasdavis, @srmo, @Stumble, @svenstaro + +### BREAKING CHANGES: + +* CLI/RPC/Config +- [rpc/client] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) + +* Apps + +* Go API +- [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore + * previously it was dumping "captured ..." msg to os.Stdout + * TrapSignal should not be responsible for blocking thread of execution + +* Blockchain Protocol + +* P2P Protocol + +### FEATURES: +- [mempool] [\#3079](https://github.com/tendermint/tendermint/issues/3079) bound mempool memory usage (`mempool.max_txs_bytes` is set to 1GB by default; see config.toml) + mempool's current `txs_total_bytes` is exposed via `total_bytes` field in + `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. +- [config] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Remove `consensus.blocktime_iota` parameter +- [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Add `time_iota_ms` to block's consensus parameters (not exposed to the application) +- [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Rename `consensus_params.block_size` to `consensus_params.block` +- [lite] add `/unsubscribe_all` endpoint, which allows you to unsubscribe from all events + +### IMPROVEMENTS: +- [libs/common] [\#3238](https://github.com/tendermint/tendermint/issues/3238) exit with zero (0) code upon receiving SIGTERM/SIGINT +- [libs/db] [\#3378](https://github.com/tendermint/tendermint/issues/3378) CLevelDB#Stats now returns the following properties: + - leveldb.num-files-at-level{n} + - leveldb.stats + - leveldb.sstables + - leveldb.blockpool + - leveldb.cachedblock + - leveldb.openedtables + - leveldb.alivesnaps + - leveldb.aliveiters + +### BUG FIXES: +- [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection +- [libs/pubsub] [\#951](https://github.com/tendermint/tendermint/issues/951), [\#1880](https://github.com/tendermint/tendermint/issues/1880) use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) +- [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) do not panic when filter times out +- [cmd] [\#3408](https://github.com/tendermint/tendermint/issues/3408) Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) + ## v0.30.2 *March 10th, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c4136b558..470282aa9 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,49 +1,22 @@ -## v0.31.0 +## v0.32.0 ** -Special thanks to external contributors on this release: -@srmo - ### BREAKING CHANGES: * CLI/RPC/Config -- [rpc/client] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) * Apps * Go API -- [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore - * previously it was dumping "captured ..." msg to os.Stdout - * TrapSignal should not be responsible for blocking thread of execution * Blockchain Protocol * P2P Protocol ### FEATURES: -- [mempool] \#3079 bound mempool memory usage (`mempool.max_txs_bytes` is set to 1GB by default; see config.toml) - mempool's current `txs_total_bytes` is exposed via `total_bytes` field in - `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. -- [config] \#2920 Remove `consensus.blocktime_iota` parameter -- [genesis] \#2920 Add `time_iota_ms` to block's consensus parameters (not exposed to the application) -- [genesis] \#2920 Rename `consensus_params.block_size` to `consensus_params.block` -- [lite] add `/unsubscribe_all` endpoint, which allows you to unsubscribe from all events ### IMPROVEMENTS: -- [libs/common] \#3238 exit with zero (0) code upon receiving SIGTERM/SIGINT -- [libs/db] \#3378 CLevelDB#Stats now returns the following properties: - - leveldb.num-files-at-level{n} - - leveldb.stats - - leveldb.sstables - - leveldb.blockpool - - leveldb.cachedblock - - leveldb.openedtables - - leveldb.alivesnaps - - leveldb.aliveiters ### BUG FIXES: -- [p2p/conn] \#3347 Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection -- [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) -- [p2p] \#3369 do not panic when filter times out -- [cmd] \#3408 Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) + diff --git a/version/version.go b/version/version.go index 1b0a36ae1..830939580 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.30.2" + TMCoreSemVer = "0.31.0" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.15.0" From a59930a327065f2d091840586da6c35b6fd07472 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 13 Mar 2019 16:09:05 +0400 Subject: [PATCH 105/259] localnet: fix $LOG variable (#3423) Fixes #3421 Before: it was creating a file named ${LOG:-tendermint.log} in .build/nodeX After: it creates a file named tendermint.log --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 61862e5c1..ccc802040 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -8,7 +8,7 @@ services: - "26656-26657:26656-26657" environment: - ID=0 - - LOG=$${LOG:-tendermint.log} + - LOG=${LOG:-tendermint.log} volumes: - ./build:/tendermint:Z networks: @@ -22,7 +22,7 @@ services: - "26659-26660:26656-26657" environment: - ID=1 - - LOG=$${LOG:-tendermint.log} + - LOG=${LOG:-tendermint.log} volumes: - ./build:/tendermint:Z networks: @@ -34,7 +34,7 @@ services: image: "tendermint/localnode" environment: - ID=2 - - LOG=$${LOG:-tendermint.log} + - LOG=${LOG:-tendermint.log} ports: - "26661-26662:26656-26657" volumes: @@ -48,7 +48,7 @@ services: image: "tendermint/localnode" environment: - ID=3 - - LOG=$${LOG:-tendermint.log} + - LOG=${LOG:-tendermint.log} ports: - "26663-26664:26656-26657" volumes: From 745713330736c5c751450245d88b8037cbee3aa6 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 14 Mar 2019 15:00:58 +0400 Subject: [PATCH 106/259] grpcdb: close Iterator/ReverseIterator after use (#3424) Fixes #3402 --- CHANGELOG_PENDING.md | 1 + libs/db/remotedb/grpcdb/server.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c4136b558..e9d4a9255 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -47,3 +47,4 @@ Special thanks to external contributors on this release: - [libs/pubsub] \#951, \#1880 use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) - [p2p] \#3369 do not panic when filter times out - [cmd] \#3408 Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) +- [libs/db/remotedb/grpcdb] \#3402 Close Iterator/ReverseIterator after use diff --git a/libs/db/remotedb/grpcdb/server.go b/libs/db/remotedb/grpcdb/server.go index bfe65e610..a032292b3 100644 --- a/libs/db/remotedb/grpcdb/server.go +++ b/libs/db/remotedb/grpcdb/server.go @@ -138,6 +138,7 @@ func (s *server) SetSync(ctx context.Context, in *protodb.Entity) (*protodb.Noth func (s *server) Iterator(query *protodb.Entity, dis protodb.DB_IteratorServer) error { it := s.db.Iterator(query.Start, query.End) + defer it.Close() return s.handleIterator(it, dis.Send) } @@ -162,6 +163,7 @@ func (s *server) handleIterator(it db.Iterator, sendFunc func(*protodb.Iterator) func (s *server) ReverseIterator(query *protodb.Entity, dis protodb.DB_ReverseIteratorServer) error { it := s.db.ReverseIterator(query.Start, query.End) + defer it.Close() return s.handleIterator(it, dis.Send) } From 5483ac6b0a2a75bc140487e8405392533a3f1687 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Thu, 14 Mar 2019 12:17:49 +0100 Subject: [PATCH 107/259] minor changes / fixes to release 0.31.0 (#3422) * bump ABCIVersion due to renaming BlockSizeParams -> BlockParams (https://github.com/tendermint/tendermint/pull/3417#discussion_r264974791) * Move changelog on consensus params entry to breaking * Add @melekes' suggestion for breaking change in pubsub into upgrading.md * Add changelog entry for #3351 * Add changelog entry for #3358 & #3359 * Add changelog entry for #3397 * remove changelog entry for #3397 (was already released in 0.30.2) * move 3351 to improvements * Update changelog comment --- CHANGELOG.md | 7 ++++++- UPGRADING.md | 18 ++++++++++++++++++ version/version.go | 2 +- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20e5a6fb3..c73a02545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,13 @@ Special thanks to external contributors on this release: * Apps +- [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Rename `consensus_params.block_size` to `consensus_params.block` in ABCI ConsensusParams + * Go API - [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore * previously it was dumping "captured ..." msg to os.Stdout * TrapSignal should not be responsible for blocking thread of execution +- [libs/db] [\#3397](https://github.com/tendermint/tendermint/pull/3397) Add possibility to `Close()` `Batch` to prevent memory leak when using ClevelDB. (@Stumble) * Blockchain Protocol @@ -29,7 +32,6 @@ Special thanks to external contributors on this release: `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. - [config] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Remove `consensus.blocktime_iota` parameter - [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Add `time_iota_ms` to block's consensus parameters (not exposed to the application) -- [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Rename `consensus_params.block_size` to `consensus_params.block` - [lite] add `/unsubscribe_all` endpoint, which allows you to unsubscribe from all events ### IMPROVEMENTS: @@ -43,12 +45,15 @@ Special thanks to external contributors on this release: - leveldb.openedtables - leveldb.alivesnaps - leveldb.aliveiters +- [privval] [\#3351](https://github.com/tendermint/tendermint/pull/3351) First part of larger refactoring that clarifies and separates concerns in the privval package. ### BUG FIXES: - [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection - [libs/pubsub] [\#951](https://github.com/tendermint/tendermint/issues/951), [\#1880](https://github.com/tendermint/tendermint/issues/1880) use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) - [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) do not panic when filter times out - [cmd] [\#3408](https://github.com/tendermint/tendermint/issues/3408) Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) +- [blockchain] [\#3358](https://github.com/tendermint/tendermint/pull/3358) Fix timer leak in `BlockPool` (@guagualvcha) +- [p2p] [\#3359](https://github.com/tendermint/tendermint/pull/3359) Fix reconnecting report duplicate ID error due to race condition between adding peer to peerSet and starting it (@guagualvcha) ## v0.30.2 diff --git a/UPGRADING.md b/UPGRADING.md index f3fecb5e0..ae1c88897 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,24 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. +## v0.31.0 + +Since the pubsub no longer blocks on sending, some WS clients might stop working as expected. +If your WS client is not consuming events fast enough, Tendermint can terminate the subscription. +In this case, the WS client will receive an error with description: + +```json +{ + "jsonrpc": "2.0", + "id": "{ID}#event", + "error": { + "code": -32000, + "msg": "Server error", + "data": "subscription was cancelled (reason: client is not pulling messages fast enough)" // or "subscription was cancelled (reason: Tendermint exited)" + } +} +``` + ## v0.30.0 This release contains a breaking change to both the block and p2p protocols, diff --git a/version/version.go b/version/version.go index 830939580..b2202206c 100644 --- a/version/version.go +++ b/version/version.go @@ -23,7 +23,7 @@ const ( TMCoreSemVer = "0.31.0" // ABCISemVer is the semantic version of the ABCI library - ABCISemVer = "0.15.0" + ABCISemVer = "0.16.0" ABCIVersion = ABCISemVer ) From 52c4e15eb25e8a3d6e77f90239710de3655d4ec6 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Thu, 14 Mar 2019 16:07:06 +0100 Subject: [PATCH 108/259] changelog: more review fixes/release/v0.31.0 (#3427) * Update release summary * Add pubsub config changes * Add link to issue for pubsub changes --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c73a02545..4c41abab8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,19 @@ Special thanks to external contributors on this release: @danil-lashin, @guagualvcha, @jleni, @siburu, @silasdavis, @srmo, @Stumble, @svenstaro +This release brings pubsub 2.0, limits the mempool size to 1GB (max_txs_bytes) and number of `/subscribe` WebSocket +clients (`max_subscription_clients`) and adds `/unsubscribe_all` endpoint to the lite client. +It also contains many smaller improvements and bug-fixes. +Pubsub 2.0 is an improved version of the older pubsub, which is a) non-blocking b) has nicer API. +Note our HttpClient's interface got updated to reflect the pubsub changes and now also has a better API for WebSocket subscriptions. + ### BREAKING CHANGES: * CLI/RPC/Config - [rpc/client] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) +- [config] [\#2826](https://github.com/tendermint/tendermint/issues/2826) Add `rpc.max_subscription_clients` config parameter to control how many unique clientIDs can `/subscribe` at the same time +- [config] [\#2826](https://github.com/tendermint/tendermint/issues/2826) Add `rpc.max_subscriptions_per_client` config parameter to control how many unique queries a given client can `/subscribe` to +- [config] [\#2826](https://github.com/tendermint/tendermint/issues/2826) Add `rpc.max_subscription_clients` config parameter allowing you to change time to wait for a tx to be committed during `/broadcast_tx_commit` * Apps From 551b6322f5a74f578a8487001e805e4e1da6394d Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sat, 16 Mar 2019 19:24:12 -0400 Subject: [PATCH 109/259] Update v0.31.0 release notes (#3434) * changelog: fix formatting * update release notes * update changelog * linkify * update UPGRADING --- CHANGELOG.md | 78 ++++++++++++++++++++++++++++++++++------------------ UPGRADING.md | 27 ++++++++++++++++-- 2 files changed, 76 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c41abab8..114879478 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,48 +2,70 @@ ## v0.31.0 -*March 13th, 2019* +*March 16th, 2019* Special thanks to external contributors on this release: -@danil-lashin, @guagualvcha, @jleni, @siburu, @silasdavis, @srmo, @Stumble, @svenstaro +@danil-lashin, @guagualvcha, @siburu, @silasdavis, @srmo, @Stumble, @svenstaro -This release brings pubsub 2.0, limits the mempool size to 1GB (max_txs_bytes) and number of `/subscribe` WebSocket -clients (`max_subscription_clients`) and adds `/unsubscribe_all` endpoint to the lite client. -It also contains many smaller improvements and bug-fixes. -Pubsub 2.0 is an improved version of the older pubsub, which is a) non-blocking b) has nicer API. -Note our HttpClient's interface got updated to reflect the pubsub changes and now also has a better API for WebSocket subscriptions. +This release is primarily about the new pubsub implementation, dubbed `pubsub 2.0`, and related changes, +like configurable limits on the number of active RPC subscriptions at a time (`max_subscription_clients`). +Pubsub 2.0 is an improved version of the older pubsub that is non-blocking and has a nicer API. +Note the improved pubsub API also resulted in some improvements to the HTTPClient interface and the API for WebSocket subscriptions. +This release also adds a configurable limit to the mempool size, `max_txs_bytes`, with +default 1GB, and includes many smaller improvements and bug-fixes. + +See the [v0.31.0 +Milestone](https://github.com/tendermint/tendermint/milestone/19?closed=1) for +more details. + +Friendly reminder, we have a [bug bounty +program](https://hackerone.com/tendermint). ### BREAKING CHANGES: * CLI/RPC/Config -- [rpc/client] Update Subscribe interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) -- [config] [\#2826](https://github.com/tendermint/tendermint/issues/2826) Add `rpc.max_subscription_clients` config parameter to control how many unique clientIDs can `/subscribe` at the same time -- [config] [\#2826](https://github.com/tendermint/tendermint/issues/2826) Add `rpc.max_subscriptions_per_client` config parameter to control how many unique queries a given client can `/subscribe` to -- [config] [\#2826](https://github.com/tendermint/tendermint/issues/2826) Add `rpc.max_subscription_clients` config parameter allowing you to change time to wait for a tx to be committed during `/broadcast_tx_commit` + - [config] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Remove `consensus.blocktime_iota` parameter + - [rpc] [\#3227](https://github.com/tendermint/tendermint/issues/3227) New PubSub design does not block on clients when publishing + messages. Slow clients may miss messages and receive an error, terminating + the subscription. + - [rpc] [\#3269](https://github.com/tendermint/tendermint/issues/2826) Limit number of unique clientIDs with open subscriptions. Configurable via `rpc.max_subscription_clients` + - [rpc] [\#3269](https://github.com/tendermint/tendermint/issues/2826) Limit number of unique queries a given client can subscribe to at once. Configurable via `rpc.max_subscriptions_per_client`. + - [rpc/client] [\#3269](https://github.com/tendermint/tendermint/issues/3269) Update `EventsClient` interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md). This includes `Subscribe`, `Unsubscribe`, and `UnsubscribeAll` methods. * Apps - -- [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Rename `consensus_params.block_size` to `consensus_params.block` in ABCI ConsensusParams + - [abci] [\#3403](https://github.com/tendermint/tendermint/issues/3403) Remove `time_iota_ms` from BlockParams. This is a + ConsensusParam but need not be exposed to the app for now. + - [abci] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Rename `consensus_params.block_size` to `consensus_params.block` in ABCI ConsensusParams * Go API -- [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore - * previously it was dumping "captured ..." msg to os.Stdout - * TrapSignal should not be responsible for blocking thread of execution -- [libs/db] [\#3397](https://github.com/tendermint/tendermint/pull/3397) Add possibility to `Close()` `Batch` to prevent memory leak when using ClevelDB. (@Stumble) + - [libs/common] TrapSignal accepts logger as a first parameter and does not block anymore + * previously it was dumping "captured ..." msg to os.Stdout + * TrapSignal should not be responsible for blocking thread of execution + - [libs/db] [\#3397](https://github.com/tendermint/tendermint/pull/3397) Add possibility to `Close()` `Batch` to prevent memory leak when using ClevelDB. (@Stumble) + - [types] [\#3354](https://github.com/tendermint/tendermint/issues/3354) Remove RoundState from EventDataRoundState * Blockchain Protocol * P2P Protocol ### FEATURES: -- [mempool] [\#3079](https://github.com/tendermint/tendermint/issues/3079) bound mempool memory usage (`mempool.max_txs_bytes` is set to 1GB by default; see config.toml) - mempool's current `txs_total_bytes` is exposed via `total_bytes` field in +- [config] [\#3269](https://github.com/tendermint/tendermint/issues/2826) New configuration values for controlling RPC subscriptions: + - `rpc.max_subscription_clients` sets the maximum number of unique clients + with open subscriptions + - `rpc.max_subscriptions_per_client`sets the maximum number of unique + subscriptions from a given client + - `rpc.timeout_broadcast_tx_commit` sets the time to wait for a tx to be committed during `/broadcast_tx_commit` +- [types] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Add `time_iota_ms` to block's consensus parameters (not exposed to the application) +- [lite] [\#3269](https://github.com/tendermint/tendermint/issues/3269) Add `/unsubscribe_all` endpoint to unsubscribe from all events +- [mempool] [\#3079](https://github.com/tendermint/tendermint/issues/3079) Bound mempool memory usage via the `mempool.max_txs_bytes` configuration value. Set to 1GB by default. The mempool's current `txs_total_bytes` is exposed via `total_bytes` field in `/num_unconfirmed_txs` and `/unconfirmed_txs` RPC endpoints. -- [config] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Remove `consensus.blocktime_iota` parameter -- [genesis] [\#2920](https://github.com/tendermint/tendermint/issues/2920) Add `time_iota_ms` to block's consensus parameters (not exposed to the application) -- [lite] add `/unsubscribe_all` endpoint, which allows you to unsubscribe from all events ### IMPROVEMENTS: +- [all] [\#3385](https://github.com/tendermint/tendermint/issues/3385), [\#3386](https://github.com/tendermint/tendermint/issues/3386) Various linting improvements +- [crypto] [\#3371](https://github.com/tendermint/tendermint/issues/3371) Copy in secp256k1 package from go-ethereum instead of importing + go-ethereum (@silasdavis) +- [deps] [\#3382](https://github.com/tendermint/tendermint/issues/3382) Don't pin repos without releases +- [deps] [\#3357](https://github.com/tendermint/tendermint/issues/3357), [\#3389](https://github.com/tendermint/tendermint/issues/3389), [\#3392](https://github.com/tendermint/tendermint/issues/3392) Update gogo/protobuf, golang/protobuf, levigo, golang.org/x/crypto - [libs/common] [\#3238](https://github.com/tendermint/tendermint/issues/3238) exit with zero (0) code upon receiving SIGTERM/SIGINT - [libs/db] [\#3378](https://github.com/tendermint/tendermint/issues/3378) CLevelDB#Stats now returns the following properties: - leveldb.num-files-at-level{n} @@ -57,11 +79,13 @@ Note our HttpClient's interface got updated to reflect the pubsub changes and no - [privval] [\#3351](https://github.com/tendermint/tendermint/pull/3351) First part of larger refactoring that clarifies and separates concerns in the privval package. ### BUG FIXES: -- [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection -- [libs/pubsub] [\#951](https://github.com/tendermint/tendermint/issues/951), [\#1880](https://github.com/tendermint/tendermint/issues/1880) use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) -- [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) do not panic when filter times out -- [cmd] [\#3408](https://github.com/tendermint/tendermint/issues/3408) Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) - [blockchain] [\#3358](https://github.com/tendermint/tendermint/pull/3358) Fix timer leak in `BlockPool` (@guagualvcha) +- [cmd] [\#3408](https://github.com/tendermint/tendermint/issues/3408) Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) +- [libs/pubsub] [\#951](https://github.com/tendermint/tendermint/issues/951), [\#1880](https://github.com/tendermint/tendermint/issues/1880) Use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) +- [lite] [\#3364](https://github.com/tendermint/tendermint/issues/3364) Fix `/validators` and `/abci_query` proxy endpoints + (@guagualvcha) +- [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection +- [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) Do not panic when filter times out - [p2p] [\#3359](https://github.com/tendermint/tendermint/pull/3359) Fix reconnecting report duplicate ID error due to race condition between adding peer to peerSet and starting it (@guagualvcha) ## v0.30.2 @@ -78,7 +102,7 @@ fix here. ### BREAKING CHANGES: * Go API -- [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Add Close() method to Batch interface (@Stumble) + - [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Add Close() method to Batch interface (@Stumble) ### BUG FIXES: - [libs/db] [\#3842](https://github.com/cosmos/cosmos-sdk/issues/3842) Fix CLevelDB memory leak (@Stumble) diff --git a/UPGRADING.md b/UPGRADING.md index ae1c88897..eccb954d3 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -5,8 +5,15 @@ a newer version of Tendermint Core. ## v0.31.0 -Since the pubsub no longer blocks on sending, some WS clients might stop working as expected. -If your WS client is not consuming events fast enough, Tendermint can terminate the subscription. +This release contains a breaking change to the behaviour of the pubsub system. +It also contains some minor breaking changes in the Go API and ABCI. +There are no changes to the block or p2p protocols, so v0.31.0 should work fine +with blockchains created from the v0.30 series. + +### RPC + +The pubsub no longer blocks on publishing. This may cause some WebSocket (WS) clients to stop working as expected. +If your WS client is not consuming events fast enough, Tendermint can terminate the subscription. In this case, the WS client will receive an error with description: ```json @@ -19,8 +26,24 @@ In this case, the WS client will receive an error with description: "data": "subscription was cancelled (reason: client is not pulling messages fast enough)" // or "subscription was cancelled (reason: Tendermint exited)" } } + +Additionally, there are now limits on the number of subscribers and +subscriptions that can be active at once. See the new +`rpc.max_subscription_clients` and `rpc.max_subscriptions_per_client` values to +configure this. ``` +### Applications + +Simple rename of `ConsensusParams.BlockSize` to `ConsensusParams.Block`. + +The `ConsensusParams.Block.TimeIotaMS` field was also removed. It's configured +in the ConsensusParsm in genesis. + +### Go API + +See the [CHANGELOG](CHANGELOG.md). These are relatively straight forward. + ## v0.30.0 This release contains a breaking change to both the block and p2p protocols, From 4162ebe8b586deccc0e7476d8abafb75138bfe58 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 19 Mar 2019 11:38:32 +0400 Subject: [PATCH 110/259] types: refactor PB2TM.ConsensusParams to take BlockTimeIota as an arg (#3442) See https://github.com/tendermint/tendermint/pull/3403/files#r266208947 In #3403 we unexposed BlockTimeIota from the ABCI, but it's still part of the ConsensusParams struct, so we have to remember to add it back after calling PB2TM.ConsensusParams. Instead, PB2TM.ConsensusParams should take it as an argument Fixes #3432 --- consensus/replay.go | 7 +------ types/protobuf.go | 9 ++++++--- types/protobuf_test.go | 4 +--- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/consensus/replay.go b/consensus/replay.go index 6656da625..c8ab8a331 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -324,12 +324,7 @@ func (h *Handshaker) ReplayBlocks( } if res.ConsensusParams != nil { - // Preserve TimeIotaMs since it's not exposed to the application. - timeIotaMs := state.ConsensusParams.Block.TimeIotaMs - { - state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams) - } - state.ConsensusParams.Block.TimeIotaMs = timeIotaMs + state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams, state.ConsensusParams.Block.TimeIotaMs) } sm.SaveState(h.stateDB, state) } diff --git a/types/protobuf.go b/types/protobuf.go index 8cad4608b..e10b91869 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -221,7 +221,9 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) return tmVals, nil } -func (pb2tm) ConsensusParams(csp *abci.ConsensusParams) ConsensusParams { +// BlockParams.TimeIotaMs is not exposed to the application. Therefore a caller +// must provide it. +func (pb2tm) ConsensusParams(csp *abci.ConsensusParams, blockTimeIotaMs int64) ConsensusParams { params := ConsensusParams{ Block: BlockParams{}, Evidence: EvidenceParams{}, @@ -231,8 +233,9 @@ func (pb2tm) ConsensusParams(csp *abci.ConsensusParams) ConsensusParams { // we must defensively consider any structs may be nil if csp.Block != nil { params.Block = BlockParams{ - MaxBytes: csp.Block.MaxBytes, - MaxGas: csp.Block.MaxGas, + MaxBytes: csp.Block.MaxBytes, + MaxGas: csp.Block.MaxGas, + TimeIotaMs: blockTimeIotaMs, } } diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 2e29a5028..152c92d12 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -64,9 +64,7 @@ func TestABCIValidators(t *testing.T) { func TestABCIConsensusParams(t *testing.T) { cp := DefaultConsensusParams() abciCP := TM2PB.ConsensusParams(cp) - cp2 := PB2TM.ConsensusParams(abciCP) - // TimeIotaMs is not exposed to the application. - cp2.Block.TimeIotaMs = cp.Block.TimeIotaMs + cp2 := PB2TM.ConsensusParams(abciCP, cp.Block.TimeIotaMs) assert.Equal(t, *cp, cp2) } From 8e62a3d62a5b04a5a7126d00d75925c990aec9c6 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 19 Mar 2019 12:19:02 +0100 Subject: [PATCH 111/259] Add #3421 to changelog and reorder alphabetically --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f7148184..24955b084 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -81,13 +81,14 @@ program](https://hackerone.com/tendermint). ### BUG FIXES: - [blockchain] [\#3358](https://github.com/tendermint/tendermint/pull/3358) Fix timer leak in `BlockPool` (@guagualvcha) - [cmd] [\#3408](https://github.com/tendermint/tendermint/issues/3408) Fix `testnet` command's panic when creating non-validator configs (using `--n` flag) (@srmo) +- [libs/db/remotedb/grpcdb] [\#3402](https://github.com/tendermint/tendermint/issues/3402) Close Iterator/ReverseIterator after use - [libs/pubsub] [\#951](https://github.com/tendermint/tendermint/issues/951), [\#1880](https://github.com/tendermint/tendermint/issues/1880) Use non-blocking send when dispatching messages [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md) - [lite] [\#3364](https://github.com/tendermint/tendermint/issues/3364) Fix `/validators` and `/abci_query` proxy endpoints (@guagualvcha) - [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection - [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) Do not panic when filter times out - [p2p] [\#3359](https://github.com/tendermint/tendermint/pull/3359) Fix reconnecting report duplicate ID error due to race condition between adding peer to peerSet and starting it (@guagualvcha) -- [libs/db/remotedb/grpcdb] [\#3402](https://github.com/tendermint/tendermint/issues/3402) Close Iterator/ReverseIterator after use +- [test] [\#3421](https://github.com/tendermint/tendermint/issues/3421) Fix logfile names created by localnet via docker-compose.yml ## v0.30.2 From e276f35f86b3980e088a95aa70c96dbddcdf658b Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 19 Mar 2019 14:36:42 +0100 Subject: [PATCH 112/259] remove 3421 from changelog --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24955b084..8968a7a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,7 +88,6 @@ program](https://hackerone.com/tendermint). - [p2p/conn] [\#3347](https://github.com/tendermint/tendermint/issues/3347) Reject all-zero shared secrets in the Diffie-Hellman step of secret-connection - [p2p] [\#3369](https://github.com/tendermint/tendermint/issues/3369) Do not panic when filter times out - [p2p] [\#3359](https://github.com/tendermint/tendermint/pull/3359) Fix reconnecting report duplicate ID error due to race condition between adding peer to peerSet and starting it (@guagualvcha) -- [test] [\#3421](https://github.com/tendermint/tendermint/issues/3421) Fix logfile names created by localnet via docker-compose.yml ## v0.30.2 From 1e3469789dce5a034a21b6e48288f1809a102595 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 20 Mar 2019 00:45:51 +0100 Subject: [PATCH 113/259] Ensure WriteTimeout > TimeoutBroadcastTxCommit (#3443) * Make sure config.TimeoutBroadcastTxCommit < rpcserver.WriteTimeout() * remove redundant comment * libs/rpc/http_server: move Read/WriteTimeout into Config * increase defaults for read/write timeouts Based on this article https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration * WriteTimeout should be larger than TimeoutBroadcastTxCommit * set a deadline for subscribing to txs * extract duration into const * add two changelog entries * Update CHANGELOG_PENDING.md Co-Authored-By: melekes * Update CHANGELOG_PENDING.md Co-Authored-By: melekes * 12 -> 10 * changelog * changelog --- CHANGELOG.md | 6 +++-- config/config.go | 7 +++-- config/toml.go | 3 +++ docs/tendermint-core/configuration.md | 3 +++ lite/proxy/proxy.go | 6 +++-- node/node.go | 17 +++++++++--- rpc/core/events.go | 3 +-- rpc/core/mempool.go | 2 +- rpc/core/pipe.go | 9 ++++--- rpc/lib/rpc_test.go | 9 ++++--- rpc/lib/server/handlers.go | 18 +++++++++++++ rpc/lib/server/http_server.go | 37 ++++++++++++++++----------- rpc/lib/server/http_server_test.go | 12 ++++++--- rpc/lib/test/main.go | 5 ++-- rpc/lib/types/types.go | 27 +++++++++++++++++-- tools/tm-monitor/rpc.go | 5 ++-- 16 files changed, 123 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8968a7a72..f0ba675ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,8 @@ This release is primarily about the new pubsub implementation, dubbed `pubsub 2. like configurable limits on the number of active RPC subscriptions at a time (`max_subscription_clients`). Pubsub 2.0 is an improved version of the older pubsub that is non-blocking and has a nicer API. Note the improved pubsub API also resulted in some improvements to the HTTPClient interface and the API for WebSocket subscriptions. -This release also adds a configurable limit to the mempool size, `max_txs_bytes`, with -default 1GB, and includes many smaller improvements and bug-fixes. +This release also adds a configurable limit to the mempool size (`max_txs_bytes`, default 1GB) +and a configurable timeout for the `/broadcast_tx_commit` endpoint. See the [v0.31.0 Milestone](https://github.com/tendermint/tendermint/milestone/19?closed=1) for @@ -30,6 +30,7 @@ program](https://hackerone.com/tendermint). the subscription. - [rpc] [\#3269](https://github.com/tendermint/tendermint/issues/2826) Limit number of unique clientIDs with open subscriptions. Configurable via `rpc.max_subscription_clients` - [rpc] [\#3269](https://github.com/tendermint/tendermint/issues/2826) Limit number of unique queries a given client can subscribe to at once. Configurable via `rpc.max_subscriptions_per_client`. + - [rpc] [\#3435](https://github.com/tendermint/tendermint/issues/3435) Default ReadTimeout and WriteTimeout changed to 10s. WriteTimeout can increased by setting `rpc.timeout_broadcast_tx_commit` in the config. - [rpc/client] [\#3269](https://github.com/tendermint/tendermint/issues/3269) Update `EventsClient` interface to reflect new pubsub/eventBus API [ADR-33](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-033-pubsub.md). This includes `Subscribe`, `Unsubscribe`, and `UnsubscribeAll` methods. * Apps @@ -43,6 +44,7 @@ program](https://hackerone.com/tendermint). * TrapSignal should not be responsible for blocking thread of execution - [libs/db] [\#3397](https://github.com/tendermint/tendermint/pull/3397) Add possibility to `Close()` `Batch` to prevent memory leak when using ClevelDB. (@Stumble) - [types] [\#3354](https://github.com/tendermint/tendermint/issues/3354) Remove RoundState from EventDataRoundState + - [rpc] [\#3435](https://github.com/tendermint/tendermint/issues/3435) `StartHTTPServer` / `StartHTTPAndTLSServer` now require a Config (use `rpcserver.DefaultConfig`) * Blockchain Protocol diff --git a/config/config.go b/config/config.go index 540012a5d..8342921a6 100644 --- a/config/config.go +++ b/config/config.go @@ -7,7 +7,6 @@ import ( "time" "github.com/pkg/errors" - rpcserver "github.com/tendermint/tendermint/rpc/lib/server" ) const ( @@ -336,6 +335,9 @@ type RPCConfig struct { MaxSubscriptionsPerClient int `mapstructure:"max_subscriptions_per_client"` // How long to wait for a tx to be committed during /broadcast_tx_commit + // WARNING: Using a value larger than 10s will result in increasing the + // global HTTP write timeout, which applies to all connections and endpoints. + // See https://github.com/tendermint/tendermint/issues/3435 TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"` } @@ -385,9 +387,6 @@ func (cfg *RPCConfig) ValidateBasic() error { if cfg.TimeoutBroadcastTxCommit < 0 { return errors.New("timeout_broadcast_tx_commit can't be negative") } - if cfg.TimeoutBroadcastTxCommit > rpcserver.WriteTimeout { - return fmt.Errorf("timeout_broadcast_tx_commit can't be greater than rpc server's write timeout: %v", rpcserver.WriteTimeout) - } return nil } diff --git a/config/toml.go b/config/toml.go index 9ce7e76c0..a0b651d99 100644 --- a/config/toml.go +++ b/config/toml.go @@ -176,6 +176,9 @@ max_subscription_clients = {{ .RPC.MaxSubscriptionClients }} max_subscriptions_per_client = {{ .RPC.MaxSubscriptionsPerClient }} # How long to wait for a tx to be committed during /broadcast_tx_commit. +# WARNING: Using a value larger than 10s will result in increasing the +# global HTTP write timeout, which applies to all connections and endpoints. +# See https://github.com/tendermint/tendermint/issues/3435 timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}" ##### peer to peer configuration options ##### diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index f1ac753a7..aa275c7a1 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -122,6 +122,9 @@ max_subscription_clients = 100 max_subscriptions_per_client = 5 # How long to wait for a tx to be committed during /broadcast_tx_commit. +# WARNING: Using a value larger than 10s will result in increasing the +# global HTTP write timeout, which applies to all connections and endpoints. +# See https://github.com/tendermint/tendermint/issues/3435 timeout_broadcast_tx_commit = "10s" ##### peer to peer configuration options ##### diff --git a/lite/proxy/proxy.go b/lite/proxy/proxy.go index 020e57539..d3c16d4a1 100644 --- a/lite/proxy/proxy.go +++ b/lite/proxy/proxy.go @@ -45,11 +45,13 @@ func StartProxy(c rpcclient.Client, listenAddr string, logger log.Logger, maxOpe core.SetLogger(logger) mux.HandleFunc(wsEndpoint, wm.WebsocketHandler) - l, err := rpcserver.Listen(listenAddr, rpcserver.Config{MaxOpenConnections: maxOpenConnections}) + config := rpcserver.DefaultConfig() + config.MaxOpenConnections = maxOpenConnections + l, err := rpcserver.Listen(listenAddr, config) if err != nil { return err } - return rpcserver.StartHTTPServer(l, mux, logger) + return rpcserver.StartHTTPServer(l, mux, logger, config) } // RPCRoutes just routes everything to the given client, as if it were diff --git a/node/node.go b/node/node.go index f3f9dca35..8f71fa31a 100644 --- a/node/node.go +++ b/node/node.go @@ -689,9 +689,18 @@ func (n *Node) startRPC() ([]net.Listener, error) { mux.HandleFunc("/websocket", wm.WebsocketHandler) rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, coreCodec, rpcLogger) + config := rpcserver.DefaultConfig() + config.MaxOpenConnections = n.config.RPC.MaxOpenConnections + // If necessary adjust global WriteTimeout to ensure it's greater than + // TimeoutBroadcastTxCommit. + // See https://github.com/tendermint/tendermint/issues/3435 + if config.WriteTimeout <= n.config.RPC.TimeoutBroadcastTxCommit { + config.WriteTimeout = n.config.RPC.TimeoutBroadcastTxCommit + 1*time.Second + } + listener, err := rpcserver.Listen( listenAddr, - rpcserver.Config{MaxOpenConnections: n.config.RPC.MaxOpenConnections}, + config, ) if err != nil { return nil, err @@ -711,6 +720,7 @@ func (n *Node) startRPC() ([]net.Listener, error) { listener, rootHandler, rpcLogger, + config, ) listeners[i] = listener } @@ -718,8 +728,9 @@ func (n *Node) startRPC() ([]net.Listener, error) { // we expose a simplified api over grpc for convenience to app devs grpcListenAddr := n.config.RPC.GRPCListenAddress if grpcListenAddr != "" { - listener, err := rpcserver.Listen( - grpcListenAddr, rpcserver.Config{MaxOpenConnections: n.config.RPC.GRPCMaxOpenConnections}) + config := rpcserver.DefaultConfig() + config.MaxOpenConnections = n.config.RPC.MaxOpenConnections + listener, err := rpcserver.Listen(grpcListenAddr, config) if err != nil { return nil, err } diff --git a/rpc/core/events.go b/rpc/core/events.go index 3ea33fa84..6bc5ecc7a 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -105,8 +105,7 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er if err != nil { return nil, errors.Wrap(err, "failed to parse query") } - - subCtx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) + subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout) defer cancel() sub, err := eventBus.Subscribe(subCtx, addr, q) if err != nil { diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 6ebdbcfce..967466e73 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -197,7 +197,7 @@ func BroadcastTxCommit(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadc } // Subscribe to tx being committed in block. - subCtx, cancel := context.WithTimeout(context.Background(), subscribeTimeout) + subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout) defer cancel() q := types.EventQueryTxFor(tx) deliverTxSub, err := eventBus.Subscribe(subCtx, subscriber, q) diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index 0b7603442..ad8afdefc 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -1,6 +1,8 @@ package core import ( + "time" + cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto" @@ -9,7 +11,6 @@ import ( mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" - rpcserver "github.com/tendermint/tendermint/rpc/lib/server" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" @@ -19,9 +20,11 @@ const ( // see README defaultPerPage = 30 maxPerPage = 100 -) -var subscribeTimeout = rpcserver.WriteTimeout / 2 + // SubscribeTimeout is the maximum time we wait to subscribe for an event. + // must be less than the server's write timeout (see rpcserver.DefaultConfig) + SubscribeTimeout = 5 * time.Second +) //---------------------------------------------- // These interfaces are used by RPC and must be thread safe diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go index 68c134a73..3fa4de47f 100644 --- a/rpc/lib/rpc_test.go +++ b/rpc/lib/rpc_test.go @@ -121,11 +121,12 @@ func setup() { wm := server.NewWebsocketManager(Routes, RoutesCdc, server.ReadWait(5*time.Second), server.PingPeriod(1*time.Second)) wm.SetLogger(tcpLogger) mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler) - listener1, err := server.Listen(tcpAddr, server.Config{}) + config := server.DefaultConfig() + listener1, err := server.Listen(tcpAddr, config) if err != nil { panic(err) } - go server.StartHTTPServer(listener1, mux, tcpLogger) + go server.StartHTTPServer(listener1, mux, tcpLogger, config) unixLogger := logger.With("socket", "unix") mux2 := http.NewServeMux() @@ -133,11 +134,11 @@ func setup() { wm = server.NewWebsocketManager(Routes, RoutesCdc) wm.SetLogger(unixLogger) mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler) - listener2, err := server.Listen(unixAddr, server.Config{}) + listener2, err := server.Listen(unixAddr, config) if err != nil { panic(err) } - go server.StartHTTPServer(listener2, mux2, unixLogger) + go server.StartHTTPServer(listener2, mux2, unixLogger, config) // wait for servers to start time.Sleep(time.Second * 2) diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 36ea47da7..6391b0090 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -2,6 +2,7 @@ package rpcserver import ( "bytes" + "context" "encoding/hex" "encoding/json" "fmt" @@ -439,6 +440,9 @@ type wsConnection struct { // callback which is called upon disconnect onDisconnect func(remoteAddr string) + + ctx context.Context + cancel context.CancelFunc } // NewWSConnection wraps websocket.Conn. @@ -532,6 +536,10 @@ func (wsc *wsConnection) OnStop() { if wsc.onDisconnect != nil { wsc.onDisconnect(wsc.remoteAddr) } + + if wsc.ctx != nil { + wsc.cancel() + } } // GetRemoteAddr returns the remote address of the underlying connection. @@ -569,6 +577,16 @@ func (wsc *wsConnection) Codec() *amino.Codec { return wsc.cdc } +// Context returns the connection's context. +// The context is canceled when the client's connection closes. +func (wsc *wsConnection) Context() context.Context { + if wsc.ctx != nil { + return wsc.ctx + } + wsc.ctx, wsc.cancel = context.WithCancel(context.Background()) + return wsc.ctx +} + // Read from the socket and subscribe to or unsubscribe from events func (wsc *wsConnection) readRoutine() { defer func() { diff --git a/rpc/lib/server/http_server.go b/rpc/lib/server/http_server.go index 9db69b6ff..c4bb6fa17 100644 --- a/rpc/lib/server/http_server.go +++ b/rpc/lib/server/http_server.go @@ -18,9 +18,23 @@ import ( types "github.com/tendermint/tendermint/rpc/lib/types" ) -// Config is an RPC server configuration. +// Config is a RPC server configuration. type Config struct { + // see netutil.LimitListener MaxOpenConnections int + // mirrors http.Server#ReadTimeout + ReadTimeout time.Duration + // mirrors http.Server#WriteTimeout + WriteTimeout time.Duration +} + +// DefaultConfig returns a default configuration. +func DefaultConfig() *Config { + return &Config{ + MaxOpenConnections: 0, // unlimited + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } } const ( @@ -30,25 +44,17 @@ const ( // same as the net/http default maxHeaderBytes = 1 << 20 - - // Timeouts for reading/writing to the http connection. - // Public so handlers can read them - - // /broadcast_tx_commit has it's own timeout, which should - // be less than the WriteTimeout here. - // TODO: use a config instead. - ReadTimeout = 3 * time.Second - WriteTimeout = 20 * time.Second ) // StartHTTPServer takes a listener and starts an HTTP server with the given handler. // It wraps handler with RecoverAndLogHandler. // NOTE: This function blocks - you may want to call it in a go-routine. -func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger) error { +func StartHTTPServer(listener net.Listener, handler http.Handler, logger log.Logger, config *Config) error { logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s", listener.Addr())) s := &http.Server{ Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger), - ReadTimeout: ReadTimeout, - WriteTimeout: WriteTimeout, + ReadTimeout: config.ReadTimeout, + WriteTimeout: config.WriteTimeout, MaxHeaderBytes: maxHeaderBytes, } err := s.Serve(listener) @@ -64,13 +70,14 @@ func StartHTTPAndTLSServer( handler http.Handler, certFile, keyFile string, logger log.Logger, + config *Config, ) error { logger.Info(fmt.Sprintf("Starting RPC HTTPS server on %s (cert: %q, key: %q)", listener.Addr(), certFile, keyFile)) s := &http.Server{ Handler: RecoverAndLogHandler(maxBytesHandler{h: handler, n: maxBodyBytes}, logger), - ReadTimeout: ReadTimeout, - WriteTimeout: WriteTimeout, + ReadTimeout: config.ReadTimeout, + WriteTimeout: config.WriteTimeout, MaxHeaderBytes: maxHeaderBytes, } err := s.ServeTLS(listener, certFile, keyFile) @@ -180,7 +187,7 @@ func (h maxBytesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Listen starts a new net.Listener on the given address. // It returns an error if the address is invalid or the call to Listen() fails. -func Listen(addr string, config Config) (listener net.Listener, err error) { +func Listen(addr string, config *Config) (listener net.Listener, err error) { parts := strings.SplitN(addr, "://", 2) if len(parts) != 2 { return nil, errors.Errorf( diff --git a/rpc/lib/server/http_server_test.go b/rpc/lib/server/http_server_test.go index 6b852afae..7f47a30b3 100644 --- a/rpc/lib/server/http_server_test.go +++ b/rpc/lib/server/http_server_test.go @@ -30,10 +30,12 @@ func TestMaxOpenConnections(t *testing.T) { time.Sleep(10 * time.Millisecond) fmt.Fprint(w, "some body") }) - l, err := Listen("tcp://127.0.0.1:0", Config{MaxOpenConnections: max}) + config := DefaultConfig() + config.MaxOpenConnections = max + l, err := Listen("tcp://127.0.0.1:0", config) require.NoError(t, err) defer l.Close() - go StartHTTPServer(l, mux, log.TestingLogger()) + go StartHTTPServer(l, mux, log.TestingLogger(), config) // Make N GET calls to the server. attempts := max * 2 @@ -64,15 +66,17 @@ func TestMaxOpenConnections(t *testing.T) { } func TestStartHTTPAndTLSServer(t *testing.T) { + config := DefaultConfig() + config.MaxOpenConnections = 1 // set up fixtures listenerAddr := "tcp://0.0.0.0:0" - listener, err := Listen(listenerAddr, Config{MaxOpenConnections: 1}) + listener, err := Listen(listenerAddr, config) require.NoError(t, err) mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {}) // test failure - err = StartHTTPAndTLSServer(listener, mux, "", "", log.TestingLogger()) + err = StartHTTPAndTLSServer(listener, mux, "", "", log.TestingLogger(), config) require.IsType(t, (*os.PathError)(nil), err) // TODO: test that starting the server can actually work diff --git a/rpc/lib/test/main.go b/rpc/lib/test/main.go index 3afc1ac1a..2e433b901 100644 --- a/rpc/lib/test/main.go +++ b/rpc/lib/test/main.go @@ -36,9 +36,10 @@ func main() { cmn.TrapSignal(logger, func() {}) rpcserver.RegisterRPCFuncs(mux, routes, cdc, logger) - listener, err := rpcserver.Listen("0.0.0.0:8008", rpcserver.Config{}) + config := rpcserver.DefaultConfig() + listener, err := rpcserver.Listen("0.0.0.0:8008", config) if err != nil { cmn.Exit(err.Error()) } - rpcserver.StartHTTPServer(listener, mux, logger) + rpcserver.StartHTTPServer(listener, mux, logger, config) } diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go index 21623e41a..14317d437 100644 --- a/rpc/lib/types/types.go +++ b/rpc/lib/types/types.go @@ -1,6 +1,7 @@ package rpctypes import ( + "context" "encoding/json" "fmt" "net/http" @@ -243,6 +244,8 @@ type WSRPCConnection interface { TryWriteRPCResponse(resp RPCResponse) bool // Codec returns an Amino codec used. Codec() *amino.Codec + // Context returns the connection's context. + Context() context.Context } // Context is the first parameter for all functions. It carries a json-rpc @@ -260,8 +263,12 @@ type Context struct { HTTPReq *http.Request } -// RemoteAddr returns either HTTPReq#RemoteAddr or result of the -// WSConn#GetRemoteAddr(). +// RemoteAddr returns the remote address (usually a string "IP:port"). +// If neither HTTPReq nor WSConn is set, an empty string is returned. +// HTTP: +// http.Request#RemoteAddr +// WS: +// result of GetRemoteAddr func (ctx *Context) RemoteAddr() string { if ctx.HTTPReq != nil { return ctx.HTTPReq.RemoteAddr @@ -271,6 +278,22 @@ func (ctx *Context) RemoteAddr() string { return "" } +// Context returns the request's context. +// The returned context is always non-nil; it defaults to the background context. +// HTTP: +// The context is canceled when the client's connection closes, the request +// is canceled (with HTTP/2), or when the ServeHTTP method returns. +// WS: +// The context is canceled when the client's connections closes. +func (ctx *Context) Context() context.Context { + if ctx.HTTPReq != nil { + return ctx.HTTPReq.Context() + } else if ctx.WSConn != nil { + return ctx.WSConn.Context() + } + return context.Background() +} + //---------------------------------------- // SOCKETS diff --git a/tools/tm-monitor/rpc.go b/tools/tm-monitor/rpc.go index 1a08a9ecd..4412e6e0b 100644 --- a/tools/tm-monitor/rpc.go +++ b/tools/tm-monitor/rpc.go @@ -17,11 +17,12 @@ func startRPC(listenAddr string, m *monitor.Monitor, logger log.Logger) net.List wm := rpc.NewWebsocketManager(routes, nil) mux.HandleFunc("/websocket", wm.WebsocketHandler) rpc.RegisterRPCFuncs(mux, routes, cdc, logger) - listener, err := rpc.Listen(listenAddr, rpc.Config{}) + config := rpc.DefaultConfig() + listener, err := rpc.Listen(listenAddr, config) if err != nil { panic(err) } - go rpc.StartHTTPServer(listener, mux, logger) + go rpc.StartHTTPServer(listener, mux, logger, config) return listener } From a6349f50633a45755041b8230a0f7eb990383cfd Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Wed, 20 Mar 2019 01:56:13 +0200 Subject: [PATCH 114/259] Formalize proposer election algorithm properties (#3140) * Update proposer-selection.md * Fixed typos * fixed typos * Attempt to address some comments * Update proposer-selection.md * Update proposer-selection.md * Update proposer-selection.md Added the normalization step. * Addressed review comments * New example for normalization section Added a new example to better show the need for normalization Added requirement for changing validator set Addressed review comments * Fixed problem with R2 * fixed the math for new validator * test * more small updates * Moved the centering above the round-robin election - the centering is now done before the actual round-robin block - updated examples - cleanup * change to reflect new implementation for new validator --- .../reactors/consensus/proposer-selection.md | 305 ++++++++++++++++-- 1 file changed, 275 insertions(+), 30 deletions(-) diff --git a/docs/spec/reactors/consensus/proposer-selection.md b/docs/spec/reactors/consensus/proposer-selection.md index b5e0b35af..6cb596ec0 100644 --- a/docs/spec/reactors/consensus/proposer-selection.md +++ b/docs/spec/reactors/consensus/proposer-selection.md @@ -2,45 +2,290 @@ This document specifies the Proposer Selection Procedure that is used in Tendermint to choose a round proposer. As Tendermint is “leader-based protocol”, the proposer selection is critical for its correct functioning. -Let denote with `proposer_p(h,r)` a process returned by the Proposer Selection Procedure at the process p, at height h -and round r. Then the Proposer Selection procedure should fulfill the following properties: -`Agreement`: Given a validator set V, and two honest validators, -p and q, for each height h, and each round r, -proposer_p(h,r) = proposer_q(h,r) +At a given block height, the proposer selection algorithm runs with the same validator set at each round . +Between heights, an updated validator set may be specified by the application as part of the ABCIResponses' EndBlock. -`Liveness`: In every consecutive sequence of rounds of size K (K is system parameter), at least a -single round has an honest proposer. +## Requirements for Proposer Selection -`Fairness`: The proposer selection is proportional to the validator voting power, i.e., a validator with more -voting power is selected more frequently, proportional to its power. More precisely, given a set of processes -with the total voting power N, during a sequence of rounds of size N, every process is proposer in a number of rounds -equal to its voting power. +This sections covers the requirements with Rx being mandatory and Ox optional requirements. +The following requirements must be met by the Proposer Selection procedure: -We now look at a few particular cases to understand better how fairness should be implemented. -If we have 4 processes with the following voting power distribution (p0,4), (p1, 2), (p2, 2), (p3, 2) at some round r, -we have the following sequence of proposer selections in the following rounds: +#### R1: Determinism +Given a validator set `V`, and two honest validators `p` and `q`, for each height `h` and each round `r` the following must hold: -`p0, p1, p2, p3, p0, p0, p1, p2, p3, p0, p0, p1, p2, p3, p0, p0, p1, p2, p3, p0, etc` + `proposer_p(h,r) = proposer_q(h,r)` -Let consider now the following scenario where a total voting power of faulty processes is aggregated in a single process -p0: (p0,3), (p1, 1), (p2, 1), (p3, 1), (p4, 1), (p5, 1), (p6, 1), (p7, 1). -In this case the sequence of proposer selections looks like this: +where `proposer_p(h,r)` is the proposer returned by the Proposer Selection Procedure at process `p`, at height `h` and round `r`. -`p0, p1, p2, p3, p0, p4, p5, p6, p7, p0, p0, p1, p2, p3, p0, p4, p5, p6, p7, p0, etc` +#### R2: Fairness +Given a validator set with total voting power P and a sequence S of elections. In any sub-sequence of S with length C*P, a validator v must be elected as proposer P/VP(v) times, i.e. with frequency: -In this case, we see that a number of rounds coordinated by a faulty process is proportional to its voting power. -We consider also the case where we have voting power uniformly distributed among processes, i.e., we have 10 processes -each with voting power of 1. And let consider that there are 3 faulty processes with consecutive addresses, -for example the first 3 processes are faulty. Then the sequence looks like this: + f(v) ~ VP(v) / P -`p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, etc` +where C is a tolerance factor for validator set changes with following values: +- C == 1 if there are no validator set changes +- C ~ k when there are validator changes -In this case, we have 3 consecutive rounds with a faulty proposer. -One special case we consider is the case where a single honest process p0 has most of the voting power, for example: -(p0,100), (p1, 2), (p2, 3), (p3, 4). Then the sequence of proposer selection looks like this: +*[this needs more work]* -p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p0, p1, p0, p0, p0, p0, p0, etc +### Basic Algorithm -This basically means that almost all rounds have the same proposer. But in this case, the process p0 has anyway enough -voting power to decide whatever he wants, so the fact that he coordinates almost all rounds seems correct. +At its core, the proposer selection procedure uses a weighted round-robin algorithm. + +A model that gives a good intuition on how/ why the selection algorithm works and it is fair is that of a priority queue. The validators move ahead in this queue according to their voting power (the higher the voting power the faster a validator moves towards the head of the queue). When the algorithm runs the following happens: +- all validators move "ahead" according to their powers: for each validator, increase the priority by the voting power +- first in the queue becomes the proposer: select the validator with highest priority +- move the proposer back in the queue: decrease the proposer's priority by the total voting power + +Notation: +- vset - the validator set +- n - the number of validators +- VP(i) - voting power of validator i +- A(i) - accumulated priority for validator i +- P - total voting power of set +- avg - average of all validator priorities +- prop - proposer + +Simple view at the Selection Algorithm: + +``` + def ProposerSelection (vset): + + // compute priorities and elect proposer + for each validator i in vset: + A(i) += VP(i) + prop = max(A) + A(prop) -= P +``` + +### Stable Set + +Consider the validator set: + +Validator | p1| p2 +----------|---|--- +VP | 1 | 3 + +Assuming no validator changes, the following table shows the proposer priority computation over a few runs. Four runs of the selection procedure are shown, starting with the 5th the same values are computed. +Each row shows the priority queue and the process place in it. The proposer is the closest to the head, the rightmost validator. As priorities are updated, the validators move right in the queue. The proposer moves left as its priority is reduced after election. + +|Priority Run | -2| -1| 0 | 1| 2 | 3 | 4 | 5 | Alg step +|--------------- |---|---|---- |---|---- |---|---|---|-------- +| | | |p1,p2| | | | | |Initialized to 0 +|run 1 | | | | p1| | p2| | |A(i)+=VP(i) +| | | p2| | p1| | | | |A(p2)-= P +|run 2 | | | | |p1,p2| | | |A(i)+=VP(i) +| | p1| | | | p2| | | |A(p1)-= P +|run 3 | | p1| | | | | | p2|A(i)+=VP(i) +| | | p1| | p2| | | | |A(p2)-= P +|run 4 | | | p1| | | | p2| |A(i)+=VP(i) +| | | |p1,p2| | | | | |A(p2)-= P + +It can be shown that: +- At the end of each run k+1 the sum of the priorities is the same as at end of run k. If a new set's priorities are initialized to 0 then the sum of priorities will be 0 at each run while there are no changes. +- The max distance between priorites is (n-1) * P. *[formal proof not finished]* + +### Validator Set Changes +Between proposer selection runs the validator set may change. Some changes have implications on the proposer election. + +#### Voting Power Change +Consider again the earlier example and assume that the voting power of p1 is changed to 4: + +Validator | p1| p2 +----------|---| --- +VP | 4 | 3 + +Let's also assume that before this change the proposer priorites were as shown in first row (last run). As it can be seen, the selection could run again, without changes, as before. + +|Priority Run| -2 | -1 | 0 | 1 | 2 | Comment +|--------------| ---|--- |------|--- |--- |-------- +| last run | | p2 | | p1 | |__update VP(p1)__ +| next run | | | | | p2 |A(i)+=VP(i) +| | p1 | | | | p2 |A(p1)-= P + +However, when a validator changes power from a high to a low value, some other validator remain far back in the queue for a long time. This scenario is considered again in the Proposer Priority Range section. + +As before: +- At the end of each run k+1 the sum of the priorities is the same as at run k. +- The max distance between priorites is (n-1) * P. + +#### Validator Removal +Consider a new example with set: + +Validator | p1 | p2 | p3 | +--------- |--- |--- |--- | +VP | 1 | 2 | 3 | + +Let's assume that after the last run the proposer priorities were as shown in first row with their sum being 0. After p2 is removed, at the end of next proposer selection run (penultimate row) the sum of priorities is -2 (minus the priority of the removed process). + +The procedure could continue without modifications. However, after a sufficiently large number of modifications in validator set, the priority values would migrate towards maximum or minimum allowed values causing truncations due to overflow detection. +For this reason, the selection procedure adds another __new step__ that centers the current priority values such that the priority sum remains close to 0. + +|Priority Run |-3 | -2 | -1 | 0 | 1 | 2 | 4 |Comment +|--------------- |--- | ---|--- |--- |--- |--- |---|-------- +| last run |p3 | | | | p1 | p2 | |__remove p2__ +| nextrun | | | | | | | | +| __new step__ | | p3 | | | | p1 | |A(i) -= avg, avg = -1 +| | | | | | p3 | p1 | |A(i)+=VP(i) +| | | | p1 | | p3 | | |A(p1)-= P + +The modified selection algorithm is: + + def ProposerSelection (vset): + + // center priorities around zero + avg = sum(A(i) for i in vset)/len(vset) + for each validator i in vset: + A(i) -= avg + + // compute priorities and elect proposer + for each validator i in vset: + A(i) += VP(i) + prop = max(A) + A(prop) -= P + +Observations: +- The sum of priorities is now close to 0. Due to integer division the sum is an integer in (-n, n), where n is the number of validators. + +#### New Validator +When a new validator is added, same problem as the one described for removal appears, the sum of priorities in the new set is not zero. This is fixed with the centering step introduced above. + +One other issue that needs to be addressed is the following. A validator V that has just been elected is moved to the end of the queue. If the validator set is large and/ or other validators have significantly higher power, V will have to wait many runs to be elected. If V removes and re-adds itself to the set, it would make a significant (albeit unfair) "jump" ahead in the queue. + +In order to prevent this, when a new validator is added, its initial priority is set to: + + A(V) = -1.125 * P + +where P is the total voting power of the set including V. + +Curent implementation uses the penalty factor of 1.125 because it provides a small punishment that is efficient to calculate. See [here](https://github.com/tendermint/tendermint/pull/2785#discussion_r235038971) for more details. + +If we consider the validator set where p3 has just been added: + +Validator | p1 | p2 | p3 +----------|--- |--- |--- +VP | 1 | 3 | 8 + +then p3 will start with proposer priority: + + A(p3) = -1.125 * (1 + 3 + 8) ~ -13 + +Note that since current computation uses integer division there is penalty loss when sum of the voting power is less than 8. + +In the next run, p3 will still be ahead in the queue, elected as proposer and moved back in the queue. + +|Priority Run |-13 | -9 | -5 | -2 | -1 | 0 | 1 | 2 | 5 | 6 | 7 |Alg step +|---------------|--- |--- |--- |----|--- |--- |---|---|---|---|---|-------- +|last run | | | | p2 | | | | p1| | | |__add p3__ +| | p3 | | | p2 | | | | p1| | | |A(p3) = -4 +|next run | | p3 | | | | | | p2| | p1| |A(i) -= avg, avg = -4 +| | | | | | p3 | | | | p2| | p1|A(i)+=VP(i) +| | | | p1 | | p3 | | | | p2| | |A(p1)-=P + +### Proposer Priority Range +With the introduction of centering, some interesting cases occur. Low power validators that bind early in a set that includes high power validator(s) benefit from subsequent additions to the set. This is because these early validators run through more right shift operations during centering, operations that increase their priority. + +As an example, consider the set where p2 is added after p1, with priority -1.125 * 80k = -90k. After the selection procedure runs once: + +Validator | p1 | p2 | Comment +----------|-----|---- |--- +VP | 80k | 10 | +A | 0 |-90k | __added p2__ +A |-45k | 45k | __run selection__ + +Then execute the following steps: + +1. Add a new validator p3: + +Validator | p1 | p2 | p3 +----------|-----|--- |---- +VP | 80k | 10 | 10 + +2. Run selection once. The notation '..p'/'p..' means very small deviations compared to column priority. + +|Priority Run | -90k..| -60k | -45k | -15k| 0 | 45k | 75k | 155k | Comment +|--------------|------ |----- |------- |---- |---|---- |----- |------- |--------- +| last run | p3 | | p2 | | | p1 | | | __added p3__ +| next run +| *right_shift*| | p3 | | p2 | | | p1 | | A(i) -= avg,avg=-30k +| | | ..p3| | ..p2| | | | p1 | A(i)+=VP(i) +| | | ..p3| | ..p2| | | p1.. | | A(p1)-=P, P=80k+20 + + +3. Remove p1 and run selection once: + +Validator | p3 | p2 | Comment +----------|----- |---- |-------- +VP | 10 | 10 | +A |-60k |-15k | +A |-22.5k|22.5k| __run selection__ + +At this point, while the total voting power is 20, the distance between priorities is 45k. It will take 4500 runs for p3 to catch up with p2. + +In order to prevent these types of scenarios, the selection algorithm performs scaling of priorities such that the difference between min and max values is smaller than two times the total voting power. + +The modified selection algorithm is: + + def ProposerSelection (vset): + + // scale the priority values + diff = max(A)-min(A) + threshold = 2 * P + if diff > threshold: + scale = diff/threshold + for each validator i in vset: + A(i) = A(i)/scale + + // center priorities around zero + avg = sum(A(i) for i in vset)/len(vset) + for each validator i in vset: + A(i) -= avg + + // compute priorities and elect proposer + for each validator i in vset: + A(i) += VP(i) + prop = max(A) + A(prop) -= P + +Observations: +- With this modification, the maximum distance between priorites becomes 2 * P. + +Note also that even during steady state the priority range may increase beyond 2 * P. The scaling introduced here helps to keep the range bounded. + +### Wrinkles + +#### Validator Power Overflow Conditions +The validator voting power is a positive number stored as an int64. When a validator is added the `1.125 * P` computation must not overflow. As a consequence the code handling validator updates (add and update) checks for overflow conditions making sure the total voting power is never larger than the largest int64 `MAX`, with the property that `1.125 * MAX` is still in the bounds of int64. Fatal error is return when overflow condition is detected. + +#### Proposer Priority Overflow/ Underflow Handling +The proposer priority is stored as an int64. The selection algorithm performs additions and subtractions to these values and in the case of overflows and underflows it limits the values to: + + MaxInt64 = 1 << 63 - 1 + MinInt64 = -1 << 63 + +### Requirement Fulfillment Claims +__[R1]__ + +The proposer algorithm is deterministic giving consistent results across executions with same transactions and validator set modifications. +[WIP - needs more detail] + +__[R2]__ + +Given a set of processes with the total voting power P, during a sequence of elections of length P, the number of times any process is selected as proposer is equal to its voting power. The sequence of the P proposers then repeats. If we consider the validator set: + +Validator | p1| p2 +----------|---|--- +VP | 1 | 3 + +With no other changes to the validator set, the current implementation of proposer selection generates the sequence: +`p2, p1, p2, p2, p2, p1, p2, p2,...` or [`p2, p1, p2, p2`]* +A sequence that starts with any circular permutation of the [`p2, p1, p2, p2`] sub-sequence would also provide the same degree of fairness. In fact these circular permutations show in the sliding window (over the generated sequence) of size equal to the length of the sub-sequence. + +Assigning priorities to each validator based on the voting power and updating them at each run ensures the fairness of the proposer selection. In addition, every time a validator is elected as proposer its priority is decreased with the total voting power. + +Intuitively, a process v jumps ahead in the queue at most (max(A) - min(A))/VP(v) times until it reaches the head and is elected. The frequency is then: + + f(v) ~ VP(v)/(max(A)-min(A)) = 1/k * VP(v)/P + +For current implementation, this means v should be proposer at least VP(v) times out of k * P runs, with scaling factor k=2. From 60b2ae5f5a3e16625af1342e012462448d565394 Mon Sep 17 00:00:00 2001 From: needkane <604476380@qq.com> Date: Wed, 20 Mar 2019 08:00:53 +0800 Subject: [PATCH 115/259] crypto: delete unused code (#3426) --- crypto/doc.go | 3 --- crypto/example_test.go | 7 ------- crypto/hash.go | 8 -------- 3 files changed, 18 deletions(-) diff --git a/crypto/doc.go b/crypto/doc.go index 41b3f3021..95ae0af18 100644 --- a/crypto/doc.go +++ b/crypto/doc.go @@ -37,9 +37,6 @@ // sum := crypto.Sha256([]byte("This is Tendermint")) // fmt.Printf("%x\n", sum) -// Ripemd160 -// sum := crypto.Ripemd160([]byte("This is consensus")) -// fmt.Printf("%x\n", sum) package crypto // TODO: Add more docs in here diff --git a/crypto/example_test.go b/crypto/example_test.go index 904e1c610..f1d0013d4 100644 --- a/crypto/example_test.go +++ b/crypto/example_test.go @@ -26,10 +26,3 @@ func ExampleSha256() { // Output: // f91afb642f3d1c87c17eb01aae5cb65c242dfdbe7cf1066cc260f4ce5d33b94e } - -func ExampleRipemd160() { - sum := crypto.Ripemd160([]byte("This is Tendermint")) - fmt.Printf("%x\n", sum) - // Output: - // 051e22663e8f0fd2f2302f1210f954adff009005 -} diff --git a/crypto/hash.go b/crypto/hash.go index c1fb41f7a..e1d22523f 100644 --- a/crypto/hash.go +++ b/crypto/hash.go @@ -2,8 +2,6 @@ package crypto import ( "crypto/sha256" - - "golang.org/x/crypto/ripemd160" ) func Sha256(bytes []byte) []byte { @@ -11,9 +9,3 @@ func Sha256(bytes []byte) []byte { hasher.Write(bytes) return hasher.Sum(nil) } - -func Ripemd160(bytes []byte) []byte { - hasher := ripemd160.New() - hasher.Write(bytes) - return hasher.Sum(nil) -} From 7af4b5086af9268f7cc8b41f5a174ade675d8ab4 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 20 Mar 2019 03:10:54 +0300 Subject: [PATCH 116/259] Remove RepeatTimer and refactor Switch#Broadcast (#3429) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * p2p: refactor Switch#Broadcast func - call wg.Add only once - do not call peers.List twice! * bad for perfomance * peers list can change in between calls! Refs #3306 * p2p: use time.Ticker instead of RepeatTimer no need in RepeatTimer since we don't Reset them Refs #3306 * libs/common: remove RepeatTimer (also TimerMaker and Ticker interface) "ancient code that’s caused no end of trouble" Ethan I believe there's much simplier way to write a ticker than can be reset https://medium.com/@arpith/resetting-a-ticker-in-go-63858a2c17ec --- CHANGELOG_PENDING.md | 1 + libs/common/repeat_timer.go | 232 ------------------------------- libs/common/repeat_timer_test.go | 136 ------------------ p2p/conn/connection.go | 12 +- p2p/switch.go | 15 +- 5 files changed, 17 insertions(+), 379 deletions(-) delete mode 100644 libs/common/repeat_timer.go delete mode 100644 libs/common/repeat_timer_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 37ae3a510..3cbc63b7b 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,6 +9,7 @@ * Apps * Go API +- [libs/common] Remove RepeatTimer (also TimerMaker and Ticker interface) * Blockchain Protocol diff --git a/libs/common/repeat_timer.go b/libs/common/repeat_timer.go deleted file mode 100644 index 5d049738d..000000000 --- a/libs/common/repeat_timer.go +++ /dev/null @@ -1,232 +0,0 @@ -package common - -import ( - "sync" - "time" -) - -// Used by RepeatTimer the first time, -// and every time it's Reset() after Stop(). -type TickerMaker func(dur time.Duration) Ticker - -// Ticker is a basic ticker interface. -type Ticker interface { - - // Never changes, never closes. - Chan() <-chan time.Time - - // Stopping a stopped Ticker will panic. - Stop() -} - -//---------------------------------------- -// defaultTicker - -var _ Ticker = (*defaultTicker)(nil) - -type defaultTicker time.Ticker - -func defaultTickerMaker(dur time.Duration) Ticker { - ticker := time.NewTicker(dur) - return (*defaultTicker)(ticker) -} - -// Implements Ticker -func (t *defaultTicker) Chan() <-chan time.Time { - return t.C -} - -// Implements Ticker -func (t *defaultTicker) Stop() { - ((*time.Ticker)(t)).Stop() -} - -//---------------------------------------- -// LogicalTickerMaker - -// Construct a TickerMaker that always uses `source`. -// It's useful for simulating a deterministic clock. -func NewLogicalTickerMaker(source chan time.Time) TickerMaker { - return func(dur time.Duration) Ticker { - return newLogicalTicker(source, dur) - } -} - -type logicalTicker struct { - source <-chan time.Time - ch chan time.Time - quit chan struct{} -} - -func newLogicalTicker(source <-chan time.Time, interval time.Duration) Ticker { - lt := &logicalTicker{ - source: source, - ch: make(chan time.Time), - quit: make(chan struct{}), - } - go lt.fireRoutine(interval) - return lt -} - -// We need a goroutine to read times from t.source -// and fire on t.Chan() when `interval` has passed. -func (t *logicalTicker) fireRoutine(interval time.Duration) { - source := t.source - - // Init `lasttime` - lasttime := time.Time{} - select { - case lasttime = <-source: - case <-t.quit: - return - } - // Init `lasttime` end - - for { - select { - case newtime := <-source: - elapsed := newtime.Sub(lasttime) - if interval <= elapsed { - // Block for determinism until the ticker is stopped. - select { - case t.ch <- newtime: - case <-t.quit: - return - } - // Reset timeleft. - // Don't try to "catch up" by sending more. - // "Ticker adjusts the intervals or drops ticks to make up for - // slow receivers" - https://golang.org/pkg/time/#Ticker - lasttime = newtime - } - case <-t.quit: - return // done - } - } -} - -// Implements Ticker -func (t *logicalTicker) Chan() <-chan time.Time { - return t.ch // immutable -} - -// Implements Ticker -func (t *logicalTicker) Stop() { - close(t.quit) // it *should* panic when stopped twice. -} - -//--------------------------------------------------------------------- - -/* - RepeatTimer repeatedly sends a struct{}{} to `.Chan()` after each `dur` - period. (It's good for keeping connections alive.) - A RepeatTimer must be stopped, or it will keep a goroutine alive. -*/ -type RepeatTimer struct { - name string - ch chan time.Time - tm TickerMaker - - mtx sync.Mutex - dur time.Duration - ticker Ticker - quit chan struct{} -} - -// NewRepeatTimer returns a RepeatTimer with a defaultTicker. -func NewRepeatTimer(name string, dur time.Duration) *RepeatTimer { - return NewRepeatTimerWithTickerMaker(name, dur, defaultTickerMaker) -} - -// NewRepeatTimerWithTicker returns a RepeatTimer with the given ticker -// maker. -func NewRepeatTimerWithTickerMaker(name string, dur time.Duration, tm TickerMaker) *RepeatTimer { - var t = &RepeatTimer{ - name: name, - ch: make(chan time.Time), - tm: tm, - dur: dur, - ticker: nil, - quit: nil, - } - t.reset() - return t -} - -// receive ticks on ch, send out on t.ch -func (t *RepeatTimer) fireRoutine(ch <-chan time.Time, quit <-chan struct{}) { - for { - select { - case tick := <-ch: - select { - case t.ch <- tick: - case <-quit: - return - } - case <-quit: // NOTE: `t.quit` races. - return - } - } -} - -func (t *RepeatTimer) Chan() <-chan time.Time { - return t.ch -} - -func (t *RepeatTimer) Stop() { - t.mtx.Lock() - defer t.mtx.Unlock() - - t.stop() -} - -// Wait the duration again before firing. -func (t *RepeatTimer) Reset() { - t.mtx.Lock() - defer t.mtx.Unlock() - - t.reset() -} - -//---------------------------------------- -// Misc. - -// CONTRACT: (non-constructor) caller should hold t.mtx. -func (t *RepeatTimer) reset() { - if t.ticker != nil { - t.stop() - } - t.ticker = t.tm(t.dur) - t.quit = make(chan struct{}) - go t.fireRoutine(t.ticker.Chan(), t.quit) -} - -// CONTRACT: caller should hold t.mtx. -func (t *RepeatTimer) stop() { - if t.ticker == nil { - /* - Similar to the case of closing channels twice: - https://groups.google.com/forum/#!topic/golang-nuts/rhxMiNmRAPk - Stopping a RepeatTimer twice implies that you do - not know whether you are done or not. - If you're calling stop on a stopped RepeatTimer, - you probably have race conditions. - */ - panic("Tried to stop a stopped RepeatTimer") - } - t.ticker.Stop() - t.ticker = nil - /* - From https://golang.org/pkg/time/#Ticker: - "Stop the ticker to release associated resources" - "After Stop, no more ticks will be sent" - So we shouldn't have to do the below. - - select { - case <-t.ch: - // read off channel if there's anything there - default: - } - */ - close(t.quit) -} diff --git a/libs/common/repeat_timer_test.go b/libs/common/repeat_timer_test.go deleted file mode 100644 index f2a7b16c3..000000000 --- a/libs/common/repeat_timer_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package common - -import ( - "sync" - "testing" - "time" - - "github.com/fortytw2/leaktest" - "github.com/stretchr/testify/assert" -) - -func TestDefaultTicker(t *testing.T) { - ticker := defaultTickerMaker(time.Millisecond * 10) - <-ticker.Chan() - ticker.Stop() -} - -func TestRepeatTimer(t *testing.T) { - - ch := make(chan time.Time, 100) - mtx := new(sync.Mutex) - - // tick() fires from start to end - // (exclusive) in milliseconds with incr. - // It locks on mtx, so subsequent calls - // run in series. - tick := func(startMs, endMs, incrMs time.Duration) { - mtx.Lock() - go func() { - for tMs := startMs; tMs < endMs; tMs += incrMs { - lt := time.Time{} - lt = lt.Add(tMs * time.Millisecond) - ch <- lt - } - mtx.Unlock() - }() - } - - // tock consumes Ticker.Chan() events and checks them against the ms in "timesMs". - tock := func(t *testing.T, rt *RepeatTimer, timesMs []int64) { - - // Check against timesMs. - for _, timeMs := range timesMs { - tyme := <-rt.Chan() - sinceMs := tyme.Sub(time.Time{}) / time.Millisecond - assert.Equal(t, timeMs, int64(sinceMs)) - } - - // TODO detect number of running - // goroutines to ensure that - // no other times will fire. - // See https://github.com/tendermint/tendermint/libs/issues/120. - time.Sleep(time.Millisecond * 100) - done := true - select { - case <-rt.Chan(): - done = false - default: - } - assert.True(t, done) - } - - tm := NewLogicalTickerMaker(ch) - rt := NewRepeatTimerWithTickerMaker("bar", time.Second, tm) - - /* NOTE: Useful for debugging deadlocks... - go func() { - time.Sleep(time.Second * 3) - trace := make([]byte, 102400) - count := runtime.Stack(trace, true) - fmt.Printf("Stack of %d bytes: %s\n", count, trace) - }() - */ - - tick(0, 1000, 10) - tock(t, rt, []int64{}) - tick(1000, 2000, 10) - tock(t, rt, []int64{1000}) - tick(2005, 5000, 10) - tock(t, rt, []int64{2005, 3005, 4005}) - tick(5001, 5999, 1) - // Read 5005 instead of 5001 because - // it's 1 second greater than 4005. - tock(t, rt, []int64{5005}) - tick(6000, 7005, 1) - tock(t, rt, []int64{6005}) - tick(7033, 8032, 1) - tock(t, rt, []int64{7033}) - - // After a reset, nothing happens - // until two ticks are received. - rt.Reset() - tock(t, rt, []int64{}) - tick(8040, 8041, 1) - tock(t, rt, []int64{}) - tick(9555, 9556, 1) - tock(t, rt, []int64{9555}) - - // After a stop, nothing more is sent. - rt.Stop() - tock(t, rt, []int64{}) - - // Another stop panics. - assert.Panics(t, func() { rt.Stop() }) -} - -func TestRepeatTimerReset(t *testing.T) { - // check that we are not leaking any go-routines - defer leaktest.Check(t)() - - timer := NewRepeatTimer("test", 20*time.Millisecond) - defer timer.Stop() - - // test we don't receive tick before duration ms. - select { - case <-timer.Chan(): - t.Fatal("did not expect to receive tick") - default: - } - - timer.Reset() - - // test we receive tick after Reset is called - select { - case <-timer.Chan(): - // all good - case <-time.After(40 * time.Millisecond): - t.Fatal("expected to receive tick after reset") - } - - // just random calls - for i := 0; i < 100; i++ { - time.Sleep(time.Duration(RandIntn(40)) * time.Millisecond) - timer.Reset() - } -} diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index c1e90ab76..e0ce062ab 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -95,13 +95,13 @@ type MConnection struct { stopMtx sync.Mutex flushTimer *cmn.ThrottleTimer // flush writes as necessary but throttled. - pingTimer *cmn.RepeatTimer // send pings periodically + pingTimer *time.Ticker // send pings periodically // close conn if pong is not received in pongTimeout pongTimer *time.Timer pongTimeoutCh chan bool // true - timeout, false - peer sent pong - chStatsTimer *cmn.RepeatTimer // update channel stats periodically + chStatsTimer *time.Ticker // update channel stats periodically created time.Time // time of creation @@ -201,9 +201,9 @@ func (c *MConnection) OnStart() error { return err } c.flushTimer = cmn.NewThrottleTimer("flush", c.config.FlushThrottle) - c.pingTimer = cmn.NewRepeatTimer("ping", c.config.PingInterval) + c.pingTimer = time.NewTicker(c.config.PingInterval) c.pongTimeoutCh = make(chan bool, 1) - c.chStatsTimer = cmn.NewRepeatTimer("chStats", updateStats) + c.chStatsTimer = time.NewTicker(updateStats) c.quitSendRoutine = make(chan struct{}) c.doneSendRoutine = make(chan struct{}) go c.sendRoutine() @@ -401,11 +401,11 @@ FOR_LOOP: // NOTE: flushTimer.Set() must be called every time // something is written to .bufConnWriter. c.flush() - case <-c.chStatsTimer.Chan(): + case <-c.chStatsTimer.C: for _, channel := range c.channels { channel.updateStats() } - case <-c.pingTimer.Chan(): + case <-c.pingTimer.C: c.Logger.Debug("Send Ping") _n, err = cdc.MarshalBinaryLengthPrefixedWriter(c.bufConnWriter, PacketPing{}) if err != nil { diff --git a/p2p/switch.go b/p2p/switch.go index a07f70ce9..9e04fe7ce 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -234,21 +234,26 @@ func (sw *Switch) OnStop() { // // NOTE: Broadcast uses goroutines, so order of broadcast may not be preserved. func (sw *Switch) Broadcast(chID byte, msgBytes []byte) chan bool { - successChan := make(chan bool, len(sw.peers.List())) sw.Logger.Debug("Broadcast", "channel", chID, "msgBytes", fmt.Sprintf("%X", msgBytes)) + + peers := sw.peers.List() var wg sync.WaitGroup - for _, peer := range sw.peers.List() { - wg.Add(1) - go func(peer Peer) { + wg.Add(len(peers)) + successChan := make(chan bool, len(peers)) + + for _, peer := range peers { + go func(p Peer) { defer wg.Done() - success := peer.Send(chID, msgBytes) + success := p.Send(chID, msgBytes) successChan <- success }(peer) } + go func() { wg.Wait() close(successChan) }() + return successChan } From 03085c2da23b179c4a51f59a03cb40aa4e85a613 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Wed, 20 Mar 2019 08:18:18 +0800 Subject: [PATCH 117/259] rpc: client disable compression (#3430) --- rpc/lib/client/http_client.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 97b8dfe7b..cfa26e89c 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -74,7 +74,9 @@ func makeHTTPClient(remoteAddr string) (string, *http.Client) { protocol, address, dialer := makeHTTPDialer(remoteAddr) return protocol + "://" + address, &http.Client{ Transport: &http.Transport{ - Dial: dialer, + // Set to true to prevent GZIP-bomb DoS attacks + DisableCompression: true, + Dial: dialer, }, } } From 926127c774a2c9110c4284938411818918ffecac Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 20 Mar 2019 03:59:33 +0300 Subject: [PATCH 118/259] blockchain: update the maxHeight when a peer is removed (#3350) * blockchain: update the maxHeight when a peer is removed Refs #2699 * add a changelog entry * make linter pass --- CHANGELOG_PENDING.md | 2 ++ blockchain/pool.go | 52 ++++++++++++++++++++++++++++++++--------- blockchain/pool_test.go | 46 +++++++++++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 14 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 3cbc63b7b..de16fcc26 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,3 +20,5 @@ ### IMPROVEMENTS: ### BUG FIXES: + +- [blockchain] \#2699 update the maxHeight when a peer is removed diff --git a/blockchain/pool.go b/blockchain/pool.go index 2cb7dda96..c842c0d13 100644 --- a/blockchain/pool.go +++ b/blockchain/pool.go @@ -69,7 +69,7 @@ type BlockPool struct { height int64 // the lowest key in requesters. // peers peers map[p2p.ID]*bpPeer - maxPeerHeight int64 + maxPeerHeight int64 // the biggest reported height // atomic numPending int32 // number of requests pending assignment or block response @@ -78,6 +78,8 @@ type BlockPool struct { errorsCh chan<- peerError } +// NewBlockPool returns a new BlockPool with the height equal to start. Block +// requests and errors will be sent to requestsCh and errorsCh accordingly. func NewBlockPool(start int64, requestsCh chan<- BlockRequest, errorsCh chan<- peerError) *BlockPool { bp := &BlockPool{ peers: make(map[p2p.ID]*bpPeer), @@ -93,15 +95,15 @@ func NewBlockPool(start int64, requestsCh chan<- BlockRequest, errorsCh chan<- p return bp } +// OnStart implements cmn.Service by spawning requesters routine and recording +// pool's start time. func (pool *BlockPool) OnStart() error { go pool.makeRequestersRoutine() pool.startTime = time.Now() return nil } -func (pool *BlockPool) OnStop() {} - -// Run spawns requesters as needed. +// spawns requesters as needed func (pool *BlockPool) makeRequestersRoutine() { for { if !pool.IsRunning() { @@ -150,6 +152,8 @@ func (pool *BlockPool) removeTimedoutPeers() { } } +// GetStatus returns pool's height, numPending requests and the number of +// requesters. func (pool *BlockPool) GetStatus() (height int64, numPending int32, lenRequesters int) { pool.mtx.Lock() defer pool.mtx.Unlock() @@ -157,6 +161,7 @@ func (pool *BlockPool) GetStatus() (height int64, numPending int32, lenRequester return pool.height, atomic.LoadInt32(&pool.numPending), len(pool.requesters) } +// IsCaughtUp returns true if this node is caught up, false - otherwise. // TODO: relax conditions, prevent abuse. func (pool *BlockPool) IsCaughtUp() bool { pool.mtx.Lock() @@ -170,8 +175,9 @@ func (pool *BlockPool) IsCaughtUp() bool { // Some conditions to determine if we're caught up. // Ensures we've either received a block or waited some amount of time, - // and that we're synced to the highest known height. Note we use maxPeerHeight - 1 - // because to sync block H requires block H+1 to verify the LastCommit. + // and that we're synced to the highest known height. + // Note we use maxPeerHeight - 1 because to sync block H requires block H+1 + // to verify the LastCommit. receivedBlockOrTimedOut := pool.height > 0 || time.Since(pool.startTime) > 5*time.Second ourChainIsLongestAmongPeers := pool.maxPeerHeight == 0 || pool.height >= (pool.maxPeerHeight-1) isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers @@ -260,14 +266,14 @@ func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int } } -// MaxPeerHeight returns the highest height reported by a peer. +// MaxPeerHeight returns the highest reported height. func (pool *BlockPool) MaxPeerHeight() int64 { pool.mtx.Lock() defer pool.mtx.Unlock() return pool.maxPeerHeight } -// Sets the peer's alleged blockchain height. +// SetPeerHeight sets the peer's alleged blockchain height. func (pool *BlockPool) SetPeerHeight(peerID p2p.ID, height int64) { pool.mtx.Lock() defer pool.mtx.Unlock() @@ -286,6 +292,8 @@ func (pool *BlockPool) SetPeerHeight(peerID p2p.ID, height int64) { } } +// RemovePeer removes the peer with peerID from the pool. If there's no peer +// with peerID, function is a no-op. func (pool *BlockPool) RemovePeer(peerID p2p.ID) { pool.mtx.Lock() defer pool.mtx.Unlock() @@ -299,10 +307,32 @@ func (pool *BlockPool) removePeer(peerID p2p.ID) { requester.redo(peerID) } } - if p, exist := pool.peers[peerID]; exist && p.timeout != nil { - p.timeout.Stop() + + peer, ok := pool.peers[peerID] + if ok { + if peer.timeout != nil { + peer.timeout.Stop() + } + + delete(pool.peers, peerID) + + // Find a new peer with the biggest height and update maxPeerHeight if the + // peer's height was the biggest. + if peer.height == pool.maxPeerHeight { + pool.updateMaxPeerHeight() + } } - delete(pool.peers, peerID) +} + +// If no peers are left, maxPeerHeight is set to 0. +func (pool *BlockPool) updateMaxPeerHeight() { + var max int64 + for _, peer := range pool.peers { + if peer.height > max { + max = peer.height + } + } + pool.maxPeerHeight = max } // Pick an available peer with at least the given minHeight. diff --git a/blockchain/pool_test.go b/blockchain/pool_test.go index 75a03f631..e24f6131e 100644 --- a/blockchain/pool_test.go +++ b/blockchain/pool_test.go @@ -1,12 +1,15 @@ package blockchain import ( + "fmt" "testing" "time" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -66,7 +69,7 @@ func makePeers(numPeers int, minHeight, maxHeight int64) testPeers { return peers } -func TestBasic(t *testing.T) { +func TestBlockPoolBasic(t *testing.T) { start := int64(42) peers := makePeers(10, start+1, 1000) errorsCh := make(chan peerError, 1000) @@ -122,7 +125,7 @@ func TestBasic(t *testing.T) { } } -func TestTimeout(t *testing.T) { +func TestBlockPoolTimeout(t *testing.T) { start := int64(42) peers := makePeers(10, start+1, 1000) errorsCh := make(chan peerError, 1000) @@ -180,3 +183,40 @@ func TestTimeout(t *testing.T) { } } } + +func TestBlockPoolRemovePeer(t *testing.T) { + peers := make(testPeers, 10) + for i := 0; i < 10; i++ { + peerID := p2p.ID(fmt.Sprintf("%d", i+1)) + height := int64(i + 1) + peers[peerID] = testPeer{peerID, height, make(chan inputData)} + } + requestsCh := make(chan BlockRequest) + errorsCh := make(chan peerError) + + pool := NewBlockPool(1, requestsCh, errorsCh) + pool.SetLogger(log.TestingLogger()) + err := pool.Start() + require.NoError(t, err) + defer pool.Stop() + + // add peers + for peerID, peer := range peers { + pool.SetPeerHeight(peerID, peer.height) + } + assert.EqualValues(t, 10, pool.MaxPeerHeight()) + + // remove not-existing peer + assert.NotPanics(t, func() { pool.RemovePeer(p2p.ID("Superman")) }) + + // remove peer with biggest height + pool.RemovePeer(p2p.ID("10")) + assert.EqualValues(t, 9, pool.MaxPeerHeight()) + + // remove all peers + for peerID := range peers { + pool.RemovePeer(peerID) + } + + assert.EqualValues(t, 0, pool.MaxPeerHeight()) +} From 81b9bdf40010c6a4e336133ec53fe6e4e6089911 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 20 Mar 2019 08:29:40 -0400 Subject: [PATCH 119/259] comments on validator ordering (#3452) * comments on validator ordering * NextValidatorsHash --- docs/spec/blockchain/blockchain.md | 2 ++ rpc/core/consensus.go | 2 ++ types/validator_set.go | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index 00cccfc2e..60a07d42a 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -332,6 +332,7 @@ block.ValidatorsHash == MerkleRoot(state.Validators) MerkleRoot of the current validator set that is committing the block. This can be used to validate the `LastCommit` included in the next block. +Note the validators are sorted by their address before computing the MerkleRoot. ### NextValidatorsHash @@ -342,6 +343,7 @@ block.NextValidatorsHash == MerkleRoot(state.NextValidators) MerkleRoot of the next validator set that will be the validator set that commits the next block. This is included so that the current validator set gets a chance to sign the next validator sets Merkle root. +Note the validators are sorted by their address before computing the MerkleRoot. ### ConsensusHash diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index b8a91f107..3850999d3 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -10,6 +10,8 @@ import ( // Get the validator set at the given block height. // If no height is provided, it will fetch the current validator set. +// Note the validators are sorted by their address - this is the canonical +// order for the validators in the set as used in computing their Merkle root. // // ```shell // curl 'localhost:26657/validators' diff --git a/types/validator_set.go b/types/validator_set.go index 3d31cf7d0..36ce67f06 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -31,7 +31,8 @@ const ( // ValidatorSet represent a set of *Validator at a given height. // The validators can be fetched by address or index. // The index is in order of .Address, so the indices are fixed -// for all rounds of a given blockchain height. +// for all rounds of a given blockchain height - ie. the validators +// are sorted by their address. // On the other hand, the .ProposerPriority of each validator and // the designated .GetProposer() of a set changes every round, // upon calling .IncrementProposerPriority(). From 660bd4a53e0dd7642a473689c8686c3f83e3a0ca Mon Sep 17 00:00:00 2001 From: tracebundy <745403419@qq.com> Date: Wed, 20 Mar 2019 20:30:49 +0800 Subject: [PATCH 120/259] fix comment (#3454) --- libs/common/service.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/common/service.go b/libs/common/service.go index 96a5e632a..21fb0df3e 100644 --- a/libs/common/service.go +++ b/libs/common/service.go @@ -209,7 +209,7 @@ func (bs *BaseService) Wait() { <-bs.quit } -// String implements Servce by returning a string representation of the service. +// String implements Service by returning a string representation of the service. func (bs *BaseService) String() string { return bs.name } From 1d4afb179b9660bf13705c67b01e20838a4506eb Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 21 Mar 2019 11:05:39 +0100 Subject: [PATCH 121/259] replace PB2TM.ConsensusParams with a call to params#Update (#3448) Fixes #3444 --- consensus/replay.go | 2 +- types/protobuf.go | 33 --------------------------------- types/protobuf_test.go | 2 +- 3 files changed, 2 insertions(+), 35 deletions(-) diff --git a/consensus/replay.go b/consensus/replay.go index c8ab8a331..e47d4892a 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -324,7 +324,7 @@ func (h *Handshaker) ReplayBlocks( } if res.ConsensusParams != nil { - state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams, state.ConsensusParams.Block.TimeIotaMs) + state.ConsensusParams = state.ConsensusParams.Update(res.ConsensusParams) } sm.SaveState(h.stateDB, state) } diff --git a/types/protobuf.go b/types/protobuf.go index e10b91869..c87e82c0a 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -220,36 +220,3 @@ func (pb2tm) ValidatorUpdates(vals []abci.ValidatorUpdate) ([]*Validator, error) } return tmVals, nil } - -// BlockParams.TimeIotaMs is not exposed to the application. Therefore a caller -// must provide it. -func (pb2tm) ConsensusParams(csp *abci.ConsensusParams, blockTimeIotaMs int64) ConsensusParams { - params := ConsensusParams{ - Block: BlockParams{}, - Evidence: EvidenceParams{}, - Validator: ValidatorParams{}, - } - - // we must defensively consider any structs may be nil - if csp.Block != nil { - params.Block = BlockParams{ - MaxBytes: csp.Block.MaxBytes, - MaxGas: csp.Block.MaxGas, - TimeIotaMs: blockTimeIotaMs, - } - } - - if csp.Evidence != nil { - params.Evidence = EvidenceParams{ - MaxAge: csp.Evidence.MaxAge, - } - } - - if csp.Validator != nil { - params.Validator = ValidatorParams{ - PubKeyTypes: csp.Validator.PubKeyTypes, - } - } - - return params -} diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 152c92d12..64caa3f4c 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -64,7 +64,7 @@ func TestABCIValidators(t *testing.T) { func TestABCIConsensusParams(t *testing.T) { cp := DefaultConsensusParams() abciCP := TM2PB.ConsensusParams(cp) - cp2 := PB2TM.ConsensusParams(abciCP, cp.Block.TimeIotaMs) + cp2 := cp.Update(abciCP) assert.Equal(t, *cp, cp2) } From 85be2a554e7e7752bed0b9409ab153bf04e05e7b Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Fri, 22 Mar 2019 09:16:38 -0400 Subject: [PATCH 122/259] tools/tm-signer-harness: update height and round for test harness (#3466) In order to re-enable the test harness for the KMS (see tendermint/kms#227), we need some marginally more realistic proposals and votes. This is because the KMS does some additional sanity checks now to ensure the height and round are increasing over time. --- tools/tm-signer-harness/internal/test_harness.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tools/tm-signer-harness/internal/test_harness.go b/tools/tm-signer-harness/internal/test_harness.go index 005489133..7fefdfb42 100644 --- a/tools/tm-signer-harness/internal/test_harness.go +++ b/tools/tm-signer-harness/internal/test_harness.go @@ -198,8 +198,8 @@ func (th *TestHarness) TestSignProposal() error { hash := tmhash.Sum([]byte("hash")) prop := &types.Proposal{ Type: types.ProposalType, - Height: 12345, - Round: 23456, + Height: 100, + Round: 0, POLRound: -1, BlockID: types.BlockID{ Hash: hash, @@ -240,8 +240,8 @@ func (th *TestHarness) TestSignVote() error { hash := tmhash.Sum([]byte("hash")) vote := &types.Vote{ Type: voteType, - Height: 12345, - Round: 23456, + Height: 101, + Round: 0, BlockID: types.BlockID{ Hash: hash, PartsHeader: types.PartSetHeader{ From 25a3c8b1724c9611d6edc175b1b0d079f5ee28c1 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Sun, 24 Mar 2019 01:08:15 +0800 Subject: [PATCH 123/259] rpc: support tls rpc (#3469) Refs #3419 --- CHANGELOG_PENDING.md | 1 + config/config.go | 29 +++++++++++++++++++++++++++ config/toml.go | 11 ++++++++++ docs/tendermint-core/configuration.md | 11 ++++++++++ node/node.go | 23 +++++++++++++++------ 5 files changed, 69 insertions(+), 6 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index de16fcc26..7cf3ab4e5 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -16,6 +16,7 @@ * P2P Protocol ### FEATURES: +- [rpc] \#3419 Start HTTPS server if `rpc.tls_cert_file` and `rpc.tls_key_file` are provided in the config (@guagualvcha) ### IMPROVEMENTS: diff --git a/config/config.go b/config/config.go index 8342921a6..3ac22adbf 100644 --- a/config/config.go +++ b/config/config.go @@ -339,6 +339,20 @@ type RPCConfig struct { // global HTTP write timeout, which applies to all connections and endpoints. // See https://github.com/tendermint/tendermint/issues/3435 TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"` + + // The name of a file containing certificate that is used to create the HTTPS server. + // + // If the certificate is signed by a certificate authority, + // the certFile should be the concatenation of the server's certificate, any intermediates, + // and the CA's certificate. + // + // NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. + TLSCertFile string `mapstructure:"tls_cert_file"` + + // The name of a file containing matching private key that is used to create the HTTPS server. + // + // NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. + TLSKeyFile string `mapstructure:"tls_key_file"` } // DefaultRPCConfig returns a default configuration for the RPC server @@ -357,6 +371,9 @@ func DefaultRPCConfig() *RPCConfig { MaxSubscriptionClients: 100, MaxSubscriptionsPerClient: 5, TimeoutBroadcastTxCommit: 10 * time.Second, + + TLSCertFile: "", + TLSKeyFile: "", } } @@ -395,6 +412,18 @@ func (cfg *RPCConfig) IsCorsEnabled() bool { return len(cfg.CORSAllowedOrigins) != 0 } +func (cfg RPCConfig) KeyFile() string { + return rootify(filepath.Join(defaultConfigDir, cfg.TLSKeyFile), cfg.RootDir) +} + +func (cfg RPCConfig) CertFile() string { + return rootify(filepath.Join(defaultConfigDir, cfg.TLSCertFile), cfg.RootDir) +} + +func (cfg RPCConfig) IsTLSEnabled() bool { + return cfg.TLSCertFile != "" && cfg.TLSKeyFile != "" +} + //----------------------------------------------------------------------------- // P2PConfig diff --git a/config/toml.go b/config/toml.go index a0b651d99..978255aba 100644 --- a/config/toml.go +++ b/config/toml.go @@ -181,6 +181,17 @@ max_subscriptions_per_client = {{ .RPC.MaxSubscriptionsPerClient }} # See https://github.com/tendermint/tendermint/issues/3435 timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}" +# The name of a file containing certificate that is used to create the HTTPS server. +# If the certificate is signed by a certificate authority, +# the certFile should be the concatenation of the server's certificate, any intermediates, +# and the CA's certificate. +# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. +tls_cert_file = "{{ .RPC.TLSCertFile }}" + +# The name of a file containing matching private key that is used to create the HTTPS server. +# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. +tls_key_file = "{{ .RPC.TLSKeyFile }}" + ##### peer to peer configuration options ##### [p2p] diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index aa275c7a1..d19c272fc 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -127,6 +127,17 @@ max_subscriptions_per_client = 5 # See https://github.com/tendermint/tendermint/issues/3435 timeout_broadcast_tx_commit = "10s" +# The name of a file containing certificate that is used to create the HTTPS server. +# If the certificate is signed by a certificate authority, +# the certFile should be the concatenation of the server's certificate, any intermediates, +# and the CA's certificate. +# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. +tls_cert_file = "" + +# The name of a file containing matching private key that is used to create the HTTPS server. +# NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. +tls_key_file = "" + ##### peer to peer configuration options ##### [p2p] diff --git a/node/node.go b/node/node.go index 8f71fa31a..3501b6a7a 100644 --- a/node/node.go +++ b/node/node.go @@ -715,13 +715,24 @@ func (n *Node) startRPC() ([]net.Listener, error) { }) rootHandler = corsMiddleware.Handler(mux) } + if n.config.RPC.IsTLSEnabled() { + go rpcserver.StartHTTPAndTLSServer( + listener, + rootHandler, + n.config.RPC.CertFile(), + n.config.RPC.KeyFile(), + rpcLogger, + config, + ) + } else { + go rpcserver.StartHTTPServer( + listener, + rootHandler, + rpcLogger, + config, + ) + } - go rpcserver.StartHTTPServer( - listener, - rootHandler, - rpcLogger, - config, - ) listeners[i] = listener } From 6de7effb05581f9bea2e8af06e4e74a85c34bc5f Mon Sep 17 00:00:00 2001 From: Dev Ojha Date: Tue, 26 Mar 2019 01:27:29 -0700 Subject: [PATCH 124/259] mempool no gossip back (#2778) Closes #1798 This is done by making every mempool tx maintain a list of peers who its received the tx from. Instead of using the 20byte peer ID, it instead uses a local map from peerID to uint16 counter, so every peer adds 2 bytes. (Word aligned to probably make it 8 bytes) This also required resetting the callback function on every CheckTx. This likely has performance ramifications for instruction caching. The actual setting operation isn't costly with the removal of defers in this PR. * Make the mempool not gossip txs back to peers its received it from * Fix adversarial memleak * Don't break interface * Update changelog * Forgot to add a mtx * forgot a mutex * Update mempool/reactor.go Co-Authored-By: ValarDragon * Update mempool/mempool.go Co-Authored-By: ValarDragon * Use unknown peer ID Co-Authored-By: ValarDragon * fix compilation * use next wait chan logic when skipping * Minor fixes * Add TxInfo * Add reverse map * Make activeID's auto-reserve 0 * 0 -> UnknownPeerID Co-Authored-By: ValarDragon * Switch to making the normal case set a callback on the reqres object The recheck case is still done via the global callback, and stats are also set via global callback * fix merge conflict * Addres comments * Add cache tests * add cache tests * minor fixes * update metrics in reqResCb and reformat code * goimport -w mempool/reactor.go * mempool: update memTx senders I had to introduce txsMap for quick mempoolTx lookups. * change senders type from []uint16 to sync.Map Fixes DATA RACE: ``` Read at 0x00c0013fcd3a by goroutine 183: github.com/tendermint/tendermint/mempool.(*MempoolReactor).broadcastTxRoutine() /go/src/github.com/tendermint/tendermint/mempool/reactor.go:195 +0x3c7 Previous write at 0x00c0013fcd3a by D[2019-02-27|10:10:49.058] Read PacketMsg switch=3 peer=35bc1e3558c182927b31987eeff3feb3d58a0fc5@127.0.0.1 :46552 conn=MConn{pipe} packet="PacketMsg{30:2B06579D0A143EB78F3D3299DE8213A51D4E11FB05ACE4D6A14F T:1}" goroutine 190: github.com/tendermint/tendermint/mempool.(*Mempool).CheckTxWithInfo() /go/src/github.com/tendermint/tendermint/mempool/mempool.go:387 +0xdc1 github.com/tendermint/tendermint/mempool.(*MempoolReactor).Receive() /go/src/github.com/tendermint/tendermint/mempool/reactor.go:134 +0xb04 github.com/tendermint/tendermint/p2p.createMConnection.func1() /go/src/github.com/tendermint/tendermint/p2p/peer.go:374 +0x25b github.com/tendermint/tendermint/p2p/conn.(*MConnection).recvRoutine() /go/src/github.com/tendermint/tendermint/p2p/conn/connection.go:599 +0xcce Goroutine 183 (running) created at: D[2019-02-27|10:10:49.058] Send switch=2 peer=1efafad5443abeea4b7a8155218e4369525d987e@127.0.0.1:46193 channel=48 conn=MConn{pipe} m sgBytes=2B06579D0A146194480ADAE00C2836ED7125FEE65C1D9DD51049 github.com/tendermint/tendermint/mempool.(*MempoolReactor).AddPeer() /go/src/github.com/tendermint/tendermint/mempool/reactor.go:105 +0x1b1 github.com/tendermint/tendermint/p2p.(*Switch).startInitPeer() /go/src/github.com/tendermint/tendermint/p2p/switch.go:683 +0x13b github.com/tendermint/tendermint/p2p.(*Switch).addPeer() /go/src/github.com/tendermint/tendermint/p2p/switch.go:650 +0x585 github.com/tendermint/tendermint/p2p.(*Switch).addPeerWithConnection() /go/src/github.com/tendermint/tendermint/p2p/test_util.go:145 +0x939 github.com/tendermint/tendermint/p2p.Connect2Switches.func2() /go/src/github.com/tendermint/tendermint/p2p/test_util.go:109 +0x50 I[2019-02-27|10:10:49.058] Added good transaction validator=0 tx=43B4D1F0F03460BD262835C4AA560DB860CFBBE85BD02386D83DAC38C67B3AD7 res="&{CheckTx:gas_w anted:1 }" height=0 total=375 Goroutine 190 (running) created at: github.com/tendermint/tendermint/p2p/conn.(*MConnection).OnStart() /go/src/github.com/tendermint/tendermint/p2p/conn/connection.go:210 +0x313 github.com/tendermint/tendermint/libs/common.(*BaseService).Start() /go/src/github.com/tendermint/tendermint/libs/common/service.go:139 +0x4df github.com/tendermint/tendermint/p2p.(*peer).OnStart() /go/src/github.com/tendermint/tendermint/p2p/peer.go:179 +0x56 github.com/tendermint/tendermint/libs/common.(*BaseService).Start() /go/src/github.com/tendermint/tendermint/libs/common/service.go:139 +0x4df github.com/tendermint/tendermint/p2p.(*peer).Start() :1 +0x43 github.com/tendermint/tendermint/p2p.(*Switch).startInitPeer() ``` * explain the choice of a map DS for senders * extract ids pool/mapper to a separate struct * fix literal copies lock value from senders: sync.Map contains sync.Mutex * use sync.Map#LoadOrStore instead of Load * fixes after Ismail's review * rename resCbNormal to resCbFirstTime --- CHANGELOG_PENDING.md | 2 + docs/spec/reactors/mempool/reactor.md | 2 + mempool/bench_test.go | 13 +++ mempool/cache_test.go | 101 +++++++++++++++++ mempool/mempool.go | 153 ++++++++++++++++++++------ mempool/mempool_test.go | 42 ++----- mempool/reactor.go | 88 +++++++++++++-- mempool/reactor_test.go | 30 ++++- state/services.go | 17 ++- 9 files changed, 362 insertions(+), 86 deletions(-) create mode 100644 mempool/cache_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 7cf3ab4e5..bebc3e6a6 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,6 +20,8 @@ ### IMPROVEMENTS: +- [mempool] \#2778 No longer send txs back to peers who sent it to you + ### BUG FIXES: - [blockchain] \#2699 update the maxHeight when a peer is removed diff --git a/docs/spec/reactors/mempool/reactor.md b/docs/spec/reactors/mempool/reactor.md index fa25eeb3e..d0b19f7ca 100644 --- a/docs/spec/reactors/mempool/reactor.md +++ b/docs/spec/reactors/mempool/reactor.md @@ -12,3 +12,5 @@ for details. Sending incorrectly encoded data or data exceeding `maxMsgSize` will result in stopping the peer. + +The mempool will not send a tx back to any peer which it received it from. \ No newline at end of file diff --git a/mempool/bench_test.go b/mempool/bench_test.go index 8936f8dfb..0cd394cd6 100644 --- a/mempool/bench_test.go +++ b/mempool/bench_test.go @@ -26,6 +26,19 @@ func BenchmarkReap(b *testing.B) { } } +func BenchmarkCheckTx(b *testing.B) { + app := kvstore.NewKVStoreApplication() + cc := proxy.NewLocalClientCreator(app) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() + + for i := 0; i < b.N; i++ { + tx := make([]byte, 8) + binary.BigEndian.PutUint64(tx, uint64(i)) + mempool.CheckTx(tx, nil) + } +} + func BenchmarkCacheInsertTime(b *testing.B) { cache := newMapTxCache(b.N) txs := make([][]byte, b.N) diff --git a/mempool/cache_test.go b/mempool/cache_test.go new file mode 100644 index 000000000..26e560b6e --- /dev/null +++ b/mempool/cache_test.go @@ -0,0 +1,101 @@ +package mempool + +import ( + "crypto/rand" + "crypto/sha256" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/tendermint/tendermint/abci/example/kvstore" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/types" +) + +func TestCacheRemove(t *testing.T) { + cache := newMapTxCache(100) + numTxs := 10 + txs := make([][]byte, numTxs) + for i := 0; i < numTxs; i++ { + // probability of collision is 2**-256 + txBytes := make([]byte, 32) + rand.Read(txBytes) + txs[i] = txBytes + cache.Push(txBytes) + // make sure its added to both the linked list and the map + require.Equal(t, i+1, len(cache.map_)) + require.Equal(t, i+1, cache.list.Len()) + } + for i := 0; i < numTxs; i++ { + cache.Remove(txs[i]) + // make sure its removed from both the map and the linked list + require.Equal(t, numTxs-(i+1), len(cache.map_)) + require.Equal(t, numTxs-(i+1), cache.list.Len()) + } +} + +func TestCacheAfterUpdate(t *testing.T) { + app := kvstore.NewKVStoreApplication() + cc := proxy.NewLocalClientCreator(app) + mempool, cleanup := newMempoolWithApp(cc) + defer cleanup() + + // reAddIndices & txsInCache can have elements > numTxsToCreate + // also assumes max index is 255 for convenience + // txs in cache also checks order of elements + tests := []struct { + numTxsToCreate int + updateIndices []int + reAddIndices []int + txsInCache []int + }{ + {1, []int{}, []int{1}, []int{1, 0}}, // adding new txs works + {2, []int{1}, []int{}, []int{1, 0}}, // update doesn't remove tx from cache + {2, []int{2}, []int{}, []int{2, 1, 0}}, // update adds new tx to cache + {2, []int{1}, []int{1}, []int{1, 0}}, // re-adding after update doesn't make dupe + } + for tcIndex, tc := range tests { + for i := 0; i < tc.numTxsToCreate; i++ { + tx := types.Tx{byte(i)} + err := mempool.CheckTx(tx, nil) + require.NoError(t, err) + } + + updateTxs := []types.Tx{} + for _, v := range tc.updateIndices { + tx := types.Tx{byte(v)} + updateTxs = append(updateTxs, tx) + } + mempool.Update(int64(tcIndex), updateTxs, nil, nil) + + for _, v := range tc.reAddIndices { + tx := types.Tx{byte(v)} + _ = mempool.CheckTx(tx, nil) + } + + cache := mempool.cache.(*mapTxCache) + node := cache.list.Front() + counter := 0 + for node != nil { + require.NotEqual(t, len(tc.txsInCache), counter, + "cache larger than expected on testcase %d", tcIndex) + + nodeVal := node.Value.([sha256.Size]byte) + expectedBz := sha256.Sum256([]byte{byte(tc.txsInCache[len(tc.txsInCache)-counter-1])}) + // Reference for reading the errors: + // >>> sha256('\x00').hexdigest() + // '6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d' + // >>> sha256('\x01').hexdigest() + // '4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a' + // >>> sha256('\x02').hexdigest() + // 'dbc1b4c900ffe48d575b5da5c638040125f65db0fe3e24494b76ea986457d986' + + require.Equal(t, expectedBz, nodeVal, "Equality failed on index %d, tc %d", counter, tcIndex) + counter++ + node = node.Next() + } + require.Equal(t, len(tc.txsInCache), counter, + "cache smaller than expected on testcase %d", tcIndex) + mempool.Flush() + } +} diff --git a/mempool/mempool.go b/mempool/mempool.go index 41ee59cb4..2064b7bce 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -31,6 +31,14 @@ type PreCheckFunc func(types.Tx) error // transaction doesn't require more gas than available for the block. type PostCheckFunc func(types.Tx, *abci.ResponseCheckTx) error +// TxInfo are parameters that get passed when attempting to add a tx to the +// mempool. +type TxInfo struct { + // We don't use p2p.ID here because it's too big. The gain is to store max 2 + // bytes with each tx to identify the sender rather than 20 bytes. + PeerID uint16 +} + /* The mempool pushes new txs onto the proxyAppConn. @@ -148,9 +156,12 @@ func TxID(tx []byte) string { type Mempool struct { config *cfg.MempoolConfig - proxyMtx sync.Mutex - proxyAppConn proxy.AppConnMempool - txs *clist.CList // concurrent linked-list of good txs + proxyMtx sync.Mutex + proxyAppConn proxy.AppConnMempool + txs *clist.CList // concurrent linked-list of good txs + // map for quick access to txs + // Used in CheckTx to record the tx sender. + txsMap map[[sha256.Size]byte]*clist.CElement height int64 // the last block Update()'d to rechecking int32 // for re-checking filtered txs on Update() recheckCursor *clist.CElement // next expected response @@ -161,7 +172,10 @@ type Mempool struct { postCheck PostCheckFunc // Atomic integers - txsBytes int64 // see TxsBytes + + // Used to check if the mempool size is bigger than the allowed limit. + // See TxsBytes + txsBytes int64 // Keep a cache of already-seen txs. // This reduces the pressure on the proxyApp. @@ -189,6 +203,7 @@ func NewMempool( config: config, proxyAppConn: proxyAppConn, txs: clist.New(), + txsMap: make(map[[sha256.Size]byte]*clist.CElement), height: height, rechecking: 0, recheckCursor: nil, @@ -286,8 +301,8 @@ func (mem *Mempool) TxsBytes() int64 { return atomic.LoadInt64(&mem.txsBytes) } -// FlushAppConn flushes the mempool connection to ensure async resCb calls are -// done e.g. from CheckTx. +// FlushAppConn flushes the mempool connection to ensure async reqResCb calls are +// done. E.g. from CheckTx. func (mem *Mempool) FlushAppConn() error { return mem.proxyAppConn.FlushSync() } @@ -304,6 +319,7 @@ func (mem *Mempool) Flush() { e.DetachPrev() } + mem.txsMap = make(map[[sha256.Size]byte]*clist.CElement) _ = atomic.SwapInt64(&mem.txsBytes, 0) } @@ -327,6 +343,13 @@ func (mem *Mempool) TxsWaitChan() <-chan struct{} { // It gets called from another goroutine. // CONTRACT: Either cb will get called, or err returned. func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) { + return mem.CheckTxWithInfo(tx, cb, TxInfo{PeerID: UnknownPeerID}) +} + +// CheckTxWithInfo performs the same operation as CheckTx, but with extra meta data about the tx. +// Currently this metadata is the peer who sent it, +// used to prevent the tx from being gossiped back to them. +func (mem *Mempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo TxInfo) (err error) { mem.proxyMtx.Lock() // use defer to unlock mutex because application (*local client*) might panic defer mem.proxyMtx.Unlock() @@ -357,6 +380,17 @@ func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) { // CACHE if !mem.cache.Push(tx) { + // record the sender + e, ok := mem.txsMap[sha256.Sum256(tx)] + if ok { // tx may be in cache, but not in the mempool + memTx := e.Value.(*mempoolTx) + if _, loaded := memTx.senders.LoadOrStore(txInfo.PeerID, true); loaded { + // TODO: consider punishing peer for dups, + // its non-trivial since invalid txs can become valid, + // but they can spam the same tx with little cost to them atm. + } + } + return ErrTxInCache } // END CACHE @@ -381,27 +415,77 @@ func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) { } reqRes := mem.proxyAppConn.CheckTxAsync(tx) if cb != nil { - reqRes.SetCallback(cb) + composedCallback := func(res *abci.Response) { + mem.reqResCb(tx, txInfo.PeerID)(res) + cb(res) + } + reqRes.SetCallback(composedCallback) + } else { + reqRes.SetCallback(mem.reqResCb(tx, txInfo.PeerID)) } return nil } -// ABCI callback function +// Global callback, which is called in the absence of the specific callback. +// +// In recheckTxs because no reqResCb (specific) callback is set, this callback +// will be called. func (mem *Mempool) resCb(req *abci.Request, res *abci.Response) { if mem.recheckCursor == nil { - mem.resCbNormal(req, res) - } else { - mem.metrics.RecheckTimes.Add(1) - mem.resCbRecheck(req, res) + return } + + mem.metrics.RecheckTimes.Add(1) + mem.resCbRecheck(req, res) + + // update metrics mem.metrics.Size.Set(float64(mem.Size())) } -func (mem *Mempool) resCbNormal(req *abci.Request, res *abci.Response) { +// Specific callback, which allows us to incorporate local information, like +// the peer that sent us this tx, so we can avoid sending it back to the same +// peer. +// +// Used in CheckTxWithInfo to record PeerID who sent us the tx. +func (mem *Mempool) reqResCb(tx []byte, peerID uint16) func(res *abci.Response) { + return func(res *abci.Response) { + if mem.recheckCursor != nil { + return + } + + mem.resCbFirstTime(tx, peerID, res) + + // update metrics + mem.metrics.Size.Set(float64(mem.Size())) + } +} + +func (mem *Mempool) addTx(memTx *mempoolTx) { + e := mem.txs.PushBack(memTx) + mem.txsMap[sha256.Sum256(memTx.tx)] = e + atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx))) + mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx))) +} + +func (mem *Mempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) { + mem.txs.Remove(elem) + elem.DetachPrev() + delete(mem.txsMap, sha256.Sum256(tx)) + atomic.AddInt64(&mem.txsBytes, int64(-len(tx))) + + if removeFromCache { + mem.cache.Remove(tx) + } +} + +// callback, which is called after the app checked the tx for the first time. +// +// The case where the app checks the tx for the second and subsequent times is +// handled by the resCbRecheck callback. +func (mem *Mempool) resCbFirstTime(tx []byte, peerID uint16, res *abci.Response) { switch r := res.Value.(type) { case *abci.Response_CheckTx: - tx := req.GetCheckTx().Tx var postCheckErr error if mem.postCheck != nil { postCheckErr = mem.postCheck(tx, r.CheckTx) @@ -412,15 +496,14 @@ func (mem *Mempool) resCbNormal(req *abci.Request, res *abci.Response) { gasWanted: r.CheckTx.GasWanted, tx: tx, } - mem.txs.PushBack(memTx) - atomic.AddInt64(&mem.txsBytes, int64(len(tx))) + memTx.senders.Store(peerID, true) + mem.addTx(memTx) mem.logger.Info("Added good transaction", "tx", TxID(tx), "res", r, "height", memTx.height, "total", mem.Size(), ) - mem.metrics.TxSizeBytes.Observe(float64(len(tx))) mem.notifyTxsAvailable() } else { // ignore bad transaction @@ -434,6 +517,10 @@ func (mem *Mempool) resCbNormal(req *abci.Request, res *abci.Response) { } } +// callback, which is called after the app rechecked the tx. +// +// The case where the app checks the tx for the first time is handled by the +// resCbFirstTime callback. func (mem *Mempool) resCbRecheck(req *abci.Request, res *abci.Response) { switch r := res.Value.(type) { case *abci.Response_CheckTx: @@ -454,12 +541,8 @@ func (mem *Mempool) resCbRecheck(req *abci.Request, res *abci.Response) { } else { // Tx became invalidated due to newly committed block. mem.logger.Info("Tx is no longer valid", "tx", TxID(tx), "res", r, "err", postCheckErr) - mem.txs.Remove(mem.recheckCursor) - atomic.AddInt64(&mem.txsBytes, int64(-len(tx))) - mem.recheckCursor.DetachPrev() - - // remove from cache (it might be good later) - mem.cache.Remove(tx) + // NOTE: we remove tx from the cache because it might be good later + mem.removeTx(tx, mem.recheckCursor, true) } if mem.recheckCursor == mem.recheckEnd { mem.recheckCursor = nil @@ -627,12 +710,9 @@ func (mem *Mempool) removeTxs(txs types.Txs) []types.Tx { memTx := e.Value.(*mempoolTx) // Remove the tx if it's already in a block. if _, ok := txsMap[string(memTx.tx)]; ok { - // remove from clist - mem.txs.Remove(e) - atomic.AddInt64(&mem.txsBytes, int64(-len(memTx.tx))) - e.DetachPrev() - // NOTE: we don't remove committed txs from the cache. + mem.removeTx(memTx.tx, e, false) + continue } txsLeft = append(txsLeft, memTx.tx) @@ -650,7 +730,7 @@ func (mem *Mempool) recheckTxs(txs []types.Tx) { mem.recheckEnd = mem.txs.Back() // Push txs to proxyAppConn - // NOTE: resCb() may be called concurrently. + // NOTE: reqResCb may be called concurrently. for _, tx := range txs { mem.proxyAppConn.CheckTxAsync(tx) } @@ -663,6 +743,7 @@ func (mem *Mempool) recheckTxs(txs []types.Tx) { type mempoolTx struct { height int64 // height that this tx had been validated in gasWanted int64 // amount of gas this tx states it will require + senders sync.Map // ids of peers who've sent us this tx (as a map for quick lookups) tx types.Tx // } @@ -679,13 +760,13 @@ type txCache interface { Remove(tx types.Tx) } -// mapTxCache maintains a cache of transactions. This only stores -// the hash of the tx, due to memory concerns. +// mapTxCache maintains a LRU cache of transactions. This only stores the hash +// of the tx, due to memory concerns. type mapTxCache struct { mtx sync.Mutex size int map_ map[[sha256.Size]byte]*list.Element - list *list.List // to remove oldest tx when cache gets too big + list *list.List } var _ txCache = (*mapTxCache)(nil) @@ -707,8 +788,8 @@ func (cache *mapTxCache) Reset() { cache.mtx.Unlock() } -// Push adds the given tx to the cache and returns true. It returns false if tx -// is already in the cache. +// Push adds the given tx to the cache and returns true. It returns +// false if tx is already in the cache. func (cache *mapTxCache) Push(tx types.Tx) bool { cache.mtx.Lock() defer cache.mtx.Unlock() @@ -728,8 +809,8 @@ func (cache *mapTxCache) Push(tx types.Tx) bool { cache.list.Remove(popped) } } - cache.list.PushBack(txHash) - cache.map_[txHash] = cache.list.Back() + e := cache.list.PushBack(txHash) + cache.map_[txHash] = e return true } diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 5928fbc56..dc7d595af 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -12,9 +12,10 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" + amino "github.com/tendermint/go-amino" + "github.com/tendermint/tendermint/abci/example/counter" "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" @@ -63,8 +64,9 @@ func ensureFire(t *testing.T, ch <-chan struct{}, timeoutMS int) { } } -func checkTxs(t *testing.T, mempool *Mempool, count int) types.Txs { +func checkTxs(t *testing.T, mempool *Mempool, count int, peerID uint16) types.Txs { txs := make(types.Txs, count) + txInfo := TxInfo{PeerID: peerID} for i := 0; i < count; i++ { txBytes := make([]byte, 20) txs[i] = txBytes @@ -72,7 +74,7 @@ func checkTxs(t *testing.T, mempool *Mempool, count int) types.Txs { if err != nil { t.Error(err) } - if err := mempool.CheckTx(txBytes, nil); err != nil { + if err := mempool.CheckTxWithInfo(txBytes, nil, txInfo); err != nil { // Skip invalid txs. // TestMempoolFilters will fail otherwise. It asserts a number of txs // returned. @@ -92,7 +94,7 @@ func TestReapMaxBytesMaxGas(t *testing.T) { defer cleanup() // Ensure gas calculation behaves as expected - checkTxs(t, mempool, 1) + checkTxs(t, mempool, 1, UnknownPeerID) tx0 := mempool.TxsFront().Value.(*mempoolTx) // assert that kv store has gas wanted = 1. require.Equal(t, app.CheckTx(tx0.tx).GasWanted, int64(1), "KVStore had a gas value neq to 1") @@ -126,7 +128,7 @@ func TestReapMaxBytesMaxGas(t *testing.T) { {20, 20000, 30, 20}, } for tcIndex, tt := range tests { - checkTxs(t, mempool, tt.numTxsToCreate) + checkTxs(t, mempool, tt.numTxsToCreate, UnknownPeerID) got := mempool.ReapMaxBytesMaxGas(tt.maxBytes, tt.maxGas) assert.Equal(t, tt.expectedNumTxs, len(got), "Got %d txs, expected %d, tc #%d", len(got), tt.expectedNumTxs, tcIndex) @@ -167,7 +169,7 @@ func TestMempoolFilters(t *testing.T) { } for tcIndex, tt := range tests { mempool.Update(1, emptyTxArr, tt.preFilter, tt.postFilter) - checkTxs(t, mempool, tt.numTxsToCreate) + checkTxs(t, mempool, tt.numTxsToCreate, UnknownPeerID) require.Equal(t, tt.expectedNumTxs, mempool.Size(), "mempool had the incorrect size, on test case %d", tcIndex) mempool.Flush() } @@ -198,7 +200,7 @@ func TestTxsAvailable(t *testing.T) { ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) // send a bunch of txs, it should only fire once - txs := checkTxs(t, mempool, 100) + txs := checkTxs(t, mempool, 100, UnknownPeerID) ensureFire(t, mempool.TxsAvailable(), timeoutMS) ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) @@ -213,7 +215,7 @@ func TestTxsAvailable(t *testing.T) { ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) // send a bunch more txs. we already fired for this height so it shouldnt fire again - moreTxs := checkTxs(t, mempool, 50) + moreTxs := checkTxs(t, mempool, 50, UnknownPeerID) ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) // now call update with all the txs. it should not fire as there are no txs left @@ -224,7 +226,7 @@ func TestTxsAvailable(t *testing.T) { ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) // send a bunch more txs, it should only fire once - checkTxs(t, mempool, 100) + checkTxs(t, mempool, 100, UnknownPeerID) ensureFire(t, mempool.TxsAvailable(), timeoutMS) ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) } @@ -340,28 +342,6 @@ func TestSerialReap(t *testing.T) { reapCheck(600) } -func TestCacheRemove(t *testing.T) { - cache := newMapTxCache(100) - numTxs := 10 - txs := make([][]byte, numTxs) - for i := 0; i < numTxs; i++ { - // probability of collision is 2**-256 - txBytes := make([]byte, 32) - rand.Read(txBytes) - txs[i] = txBytes - cache.Push(txBytes) - // make sure its added to both the linked list and the map - require.Equal(t, i+1, len(cache.map_)) - require.Equal(t, i+1, cache.list.Len()) - } - for i := 0; i < numTxs; i++ { - cache.Remove(txs[i]) - // make sure its removed from both the map and the linked list - require.Equal(t, numTxs-(i+1), len(cache.map_)) - require.Equal(t, numTxs-(i+1), cache.list.Len()) - } -} - func TestMempoolCloseWAL(t *testing.T) { // 1. Create the temporary directory for mempool and WAL testing. rootDir, err := ioutil.TempDir("", "mempool-test") diff --git a/mempool/reactor.go b/mempool/reactor.go index ff87f0506..555f38b8b 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -3,13 +3,14 @@ package mempool import ( "fmt" "reflect" + "sync" "time" amino "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/libs/clist" - "github.com/tendermint/tendermint/libs/log" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/clist" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -21,13 +22,70 @@ const ( maxTxSize = maxMsgSize - 8 // account for amino overhead of TxMessage peerCatchupSleepIntervalMS = 100 // If peer is behind, sleep this amount + + // UnknownPeerID is the peer ID to use when running CheckTx when there is + // no peer (e.g. RPC) + UnknownPeerID uint16 = 0 ) // MempoolReactor handles mempool tx broadcasting amongst peers. +// It maintains a map from peer ID to counter, to prevent gossiping txs to the +// peers you received it from. type MempoolReactor struct { p2p.BaseReactor config *cfg.MempoolConfig Mempool *Mempool + ids *mempoolIDs +} + +type mempoolIDs struct { + mtx sync.RWMutex + peerMap map[p2p.ID]uint16 + nextID uint16 // assumes that a node will never have over 65536 active peers + activeIDs map[uint16]struct{} // used to check if a given peerID key is used, the value doesn't matter +} + +// Reserve searches for the next unused ID and assignes it to the peer. +func (ids *mempoolIDs) ReserveForPeer(peer p2p.Peer) { + ids.mtx.Lock() + defer ids.mtx.Unlock() + + curID := ids.nextPeerID() + ids.peerMap[peer.ID()] = curID + ids.activeIDs[curID] = struct{}{} +} + +// nextPeerID returns the next unused peer ID to use. +// This assumes that ids's mutex is already locked. +func (ids *mempoolIDs) nextPeerID() uint16 { + _, idExists := ids.activeIDs[ids.nextID] + for idExists { + ids.nextID++ + _, idExists = ids.activeIDs[ids.nextID] + } + curID := ids.nextID + ids.nextID++ + return curID +} + +// Reclaim returns the ID reserved for the peer back to unused pool. +func (ids *mempoolIDs) Reclaim(peer p2p.Peer) { + ids.mtx.Lock() + defer ids.mtx.Unlock() + + removedID, ok := ids.peerMap[peer.ID()] + if ok { + delete(ids.activeIDs, removedID) + delete(ids.peerMap, peer.ID()) + } +} + +// GetForPeer returns an ID reserved for the peer. +func (ids *mempoolIDs) GetForPeer(peer p2p.Peer) uint16 { + ids.mtx.RLock() + defer ids.mtx.RUnlock() + + return ids.peerMap[peer.ID()] } // NewMempoolReactor returns a new MempoolReactor with the given config and mempool. @@ -35,6 +93,11 @@ func NewMempoolReactor(config *cfg.MempoolConfig, mempool *Mempool) *MempoolReac memR := &MempoolReactor{ config: config, Mempool: mempool, + ids: &mempoolIDs{ + peerMap: make(map[p2p.ID]uint16), + activeIDs: map[uint16]struct{}{0: {}}, + nextID: 1, // reserve unknownPeerID(0) for mempoolReactor.BroadcastTx + }, } memR.BaseReactor = *p2p.NewBaseReactor("MempoolReactor", memR) return memR @@ -68,11 +131,13 @@ func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor { // AddPeer implements Reactor. // It starts a broadcast routine ensuring all txs are forwarded to the given peer. func (memR *MempoolReactor) AddPeer(peer p2p.Peer) { + memR.ids.ReserveForPeer(peer) go memR.broadcastTxRoutine(peer) } // RemovePeer implements Reactor. func (memR *MempoolReactor) RemovePeer(peer p2p.Peer, reason interface{}) { + memR.ids.Reclaim(peer) // broadcast routine checks if peer is gone and returns } @@ -89,7 +154,8 @@ func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { switch msg := msg.(type) { case *TxMessage: - err := memR.Mempool.CheckTx(msg.Tx, nil) + peerID := memR.ids.GetForPeer(src) + err := memR.Mempool.CheckTxWithInfo(msg.Tx, nil, TxInfo{PeerID: peerID}) if err != nil { memR.Logger.Info("Could not check tx", "tx", TxID(msg.Tx), "err", err) } @@ -110,6 +176,7 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) { return } + peerID := memR.ids.GetForPeer(peer) var next *clist.CElement for { // This happens because the CElement we were looking at got garbage @@ -146,12 +213,15 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) { continue } - // send memTx - msg := &TxMessage{Tx: memTx.tx} - success := peer.Send(MempoolChannel, cdc.MustMarshalBinaryBare(msg)) - if !success { - time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond) - continue + // ensure peer hasn't already sent us this tx + if _, ok := memTx.senders.Load(peerID); !ok { + // send memTx + msg := &TxMessage{Tx: memTx.tx} + success := peer.Send(MempoolChannel, cdc.MustMarshalBinaryBare(msg)) + if !success { + time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond) + continue + } } select { diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index 51d130187..f16f84479 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -7,15 +7,13 @@ import ( "time" "github.com/fortytw2/leaktest" + "github.com/go-kit/kit/log/term" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "github.com/go-kit/kit/log/term" - "github.com/tendermint/tendermint/abci/example/kvstore" - "github.com/tendermint/tendermint/libs/log" - cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" @@ -102,6 +100,12 @@ func _waitForTxs(t *testing.T, wg *sync.WaitGroup, txs types.Txs, reactorIdx int wg.Done() } +// ensure no txs on reactor after some timeout +func ensureNoTxs(t *testing.T, reactor *MempoolReactor, timeout time.Duration) { + time.Sleep(timeout) // wait for the txs in all mempools + assert.Zero(t, reactor.Mempool.Size()) +} + const ( NUM_TXS = 1000 TIMEOUT = 120 * time.Second // ridiculously high because CircleCI is slow @@ -124,10 +128,26 @@ func TestReactorBroadcastTxMessage(t *testing.T) { // send a bunch of txs to the first reactor's mempool // and wait for them all to be received in the others - txs := checkTxs(t, reactors[0].Mempool, NUM_TXS) + txs := checkTxs(t, reactors[0].Mempool, NUM_TXS, UnknownPeerID) waitForTxs(t, txs, reactors) } +func TestReactorNoBroadcastToSender(t *testing.T) { + config := cfg.TestConfig() + const N = 2 + reactors := makeAndConnectMempoolReactors(config, N) + defer func() { + for _, r := range reactors { + r.Stop() + } + }() + + // send a bunch of txs to the first reactor's mempool, claiming it came from peer + // ensure peer gets no txs + checkTxs(t, reactors[0].Mempool, NUM_TXS, 1) + ensureNoTxs(t, reactors[1], 100*time.Millisecond) +} + func TestBroadcastTxForPeerStopsWhenPeerStops(t *testing.T) { if testing.Short() { t.Skip("skipping test in short mode.") diff --git a/state/services.go b/state/services.go index 02c3aa7d1..07d12c5a1 100644 --- a/state/services.go +++ b/state/services.go @@ -23,6 +23,7 @@ type Mempool interface { Size() int CheckTx(types.Tx, func(*abci.Response)) error + CheckTxWithInfo(types.Tx, func(*abci.Response), mempool.TxInfo) error ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs Update(int64, types.Txs, mempool.PreCheckFunc, mempool.PostCheckFunc) error Flush() @@ -37,11 +38,17 @@ type MockMempool struct{} var _ Mempool = MockMempool{} -func (MockMempool) Lock() {} -func (MockMempool) Unlock() {} -func (MockMempool) Size() int { return 0 } -func (MockMempool) CheckTx(_ types.Tx, _ func(*abci.Response)) error { return nil } -func (MockMempool) ReapMaxBytesMaxGas(_, _ int64) types.Txs { return types.Txs{} } +func (MockMempool) Lock() {} +func (MockMempool) Unlock() {} +func (MockMempool) Size() int { return 0 } +func (MockMempool) CheckTx(_ types.Tx, _ func(*abci.Response)) error { + return nil +} +func (MockMempool) CheckTxWithInfo(_ types.Tx, _ func(*abci.Response), + _ mempool.TxInfo) error { + return nil +} +func (MockMempool) ReapMaxBytesMaxGas(_, _ int64) types.Txs { return types.Txs{} } func (MockMempool) Update( _ int64, _ types.Txs, From 1bb8e02a962a1ffd7ce4e7355f648b644105f649 Mon Sep 17 00:00:00 2001 From: HaoyangLiu Date: Tue, 26 Mar 2019 16:29:06 +0800 Subject: [PATCH 125/259] mempool: fix broadcastTxRoutine leak (#3478) Refs #3306, irisnet@fdbb676 I ran an irishub validator. After the validator node ran several days, I dump the whole goroutine stack. I found that there were hundreds of broadcastTxRoutine. However, the connected peer quantity was less than 30. So I belive that there must be broadcastTxRoutine leakage issue. According to my analysis, I think the root cause of this issue locate in below code: select { case <-next.NextWaitChan(): // see the start of the for loop for nil check next = next.Next() case <-peer.Quit(): return case <-memR.Quit(): return } As we know, if multiple paths are avaliable in the same time, then a random path will be selected. Suppose that next.NextWaitChan() and peer.Quit() are both avaliable, and next.NextWaitChan() is chosen. // send memTx msg := &TxMessage{Tx: memTx.tx} success := peer.Send(MempoolChannel, cdc.MustMarshalBinaryBare(msg)) if !success { time.Sleep(peerCatchupSleepIntervalMS * time.Millisecond) continue } Then next will be non-empty and the peer send operation won't be success. As a result, this go routine will be track into infinite loop and won't be released. My proposal is to check peer.Quit() and memR.Quit() in every loop no matter whether next is nil. --- mempool/reactor.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mempool/reactor.go b/mempool/reactor.go index 555f38b8b..23fec2700 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -179,6 +179,10 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) { peerID := memR.ids.GetForPeer(peer) var next *clist.CElement for { + // In case of both next.NextWaitChan() and peer.Quit() are variable at the same time + if !memR.IsRunning() || !peer.IsRunning() { + return + } // This happens because the CElement we were looking at got garbage // collected (removed). That is, .NextWait() returned nil. Go ahead and // start from the beginning. From a4d9539544ba4377f16e797fea01090bc974e1b5 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 26 Mar 2019 09:44:49 +0100 Subject: [PATCH 126/259] rpc/client: include NetworkClient interface into Client interface (#3473) I think it's nice when the Client interface has all the methods. If someone does not need a particular method/set of methods, she can use individual interfaces (e.g. NetworkClient, MempoolClient) or write her own interface. technically breaking Fixes #3458 --- CHANGELOG_PENDING.md | 1 + rpc/client/httpclient.go | 6 +----- rpc/client/interface.go | 10 ++++------ rpc/client/localclient.go | 6 +----- rpc/client/mock/client.go | 12 ++++++++++++ 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index bebc3e6a6..eaf08928d 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -10,6 +10,7 @@ * Go API - [libs/common] Remove RepeatTimer (also TimerMaker and Ticker interface) +- [rpc/client] \#3458 Include NetworkClient interface into Client interface * Blockchain Protocol diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index e982292e7..55c7b4f17 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -52,11 +52,7 @@ func NewHTTP(remote, wsEndpoint string) *HTTP { } } -var ( - _ Client = (*HTTP)(nil) - _ NetworkClient = (*HTTP)(nil) - _ EventsClient = (*HTTP)(nil) -) +var _ Client = (*HTTP)(nil) func (c *HTTP) Status() (*ctypes.ResultStatus, error) { result := new(ctypes.ResultStatus) diff --git a/rpc/client/interface.go b/rpc/client/interface.go index 605d84ba2..8f9ed9372 100644 --- a/rpc/client/interface.go +++ b/rpc/client/interface.go @@ -72,17 +72,15 @@ type StatusClient interface { type Client interface { cmn.Service ABCIClient - SignClient - HistoryClient - StatusClient EventsClient + HistoryClient + NetworkClient + SignClient + StatusClient } // NetworkClient is general info about the network state. May not // be needed usually. -// -// Not included in the Client interface, but generally implemented -// by concrete implementations. type NetworkClient interface { NetInfo() (*ctypes.ResultNetInfo, error) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index 976c9892a..d57ced311 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -58,11 +58,7 @@ func NewLocal(node *nm.Node) *Local { } } -var ( - _ Client = (*Local)(nil) - _ NetworkClient = (*Local)(nil) - _ EventsClient = (*Local)(nil) -) +var _ Client = (*Local)(nil) // SetLogger allows to set a logger on the client. func (c *Local) SetLogger(l log.Logger) { diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go index 9c0eb75b8..c2e19b6d4 100644 --- a/rpc/client/mock/client.go +++ b/rpc/client/mock/client.go @@ -108,6 +108,18 @@ func (c Client) NetInfo() (*ctypes.ResultNetInfo, error) { return core.NetInfo(&rpctypes.Context{}) } +func (c Client) ConsensusState() (*ctypes.ResultConsensusState, error) { + return core.ConsensusState(&rpctypes.Context{}) +} + +func (c Client) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { + return core.DumpConsensusState(&rpctypes.Context{}) +} + +func (c Client) Health() (*ctypes.ResultHealth, error) { + return core.Health(&rpctypes.Context{}) +} + func (c Client) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { return core.UnsafeDialSeeds(&rpctypes.Context{}, seeds) } From 5a25b75b1d586c6d9c3fdfc940057f7b85089a90 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Wed, 27 Mar 2019 00:13:14 +0800 Subject: [PATCH 127/259] p2p: refactor GetSelectionWithBias for addressbook (#3475) Why submit this pr: we have suffered from infinite loop in addrbook bug which takes us a long time to find out why process become a zombie peer. It have been fixed in #3232. But the ADDRS_LOOP is still there, risk of infinite loop is still exist. The algorithm that to random pick a bucket is not stable, which means the peer may unluckily always choose the wrong bucket for a long time, the time and cpu cost is meaningless. A simple improvement: shuffle bucketsNew and bucketsOld, and pick necessary number of address from them. A stable algorithm. --- p2p/pex/addrbook.go | 125 ++++++++++++++------------------------- p2p/pex/addrbook_test.go | 35 +++++------ 2 files changed, 58 insertions(+), 102 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 3cda9ac74..3cb91c380 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -9,6 +9,7 @@ import ( "encoding/binary" "fmt" "math" + "math/rand" "net" "sync" "time" @@ -405,89 +406,11 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre bookSize*getSelectionPercent/100) numAddresses = cmn.MinInt(maxGetSelection, numAddresses) - selection := make([]*p2p.NetAddress, numAddresses) - - oldBucketToAddrsMap := make(map[int]map[string]struct{}) - var oldIndex int - newBucketToAddrsMap := make(map[int]map[string]struct{}) - var newIndex int - - // initialize counters used to count old and new added addresses. - // len(oldBucketToAddrsMap) cannot be used as multiple addresses can endup in the same bucket. - var oldAddressesAdded int - var newAddressesAdded int - // number of new addresses that, if possible, should be in the beginning of the selection - numRequiredNewAdd := percentageOfNum(biasTowardsNewAddrs, numAddresses) - - selectionIndex := 0 -ADDRS_LOOP: - for selectionIndex < numAddresses { - // biasedTowardsOldAddrs indicates if the selection can switch to old addresses - biasedTowardsOldAddrs := selectionIndex >= numRequiredNewAdd - // An old addresses is selected if: - // - the bias is for old and old addressees are still available or, - // - there are no new addresses or all new addresses have been selected. - // numAddresses <= a.nOld + a.nNew therefore it is guaranteed that there are enough - // addresses to fill the selection - pickFromOldBucket := - (biasedTowardsOldAddrs && oldAddressesAdded < a.nOld) || - a.nNew == 0 || newAddressesAdded >= a.nNew - - bucket := make(map[string]*knownAddress) - - // loop until we pick a random non-empty bucket - for len(bucket) == 0 { - if pickFromOldBucket { - oldIndex = a.rand.Intn(len(a.bucketsOld)) - bucket = a.bucketsOld[oldIndex] - } else { - newIndex = a.rand.Intn(len(a.bucketsNew)) - bucket = a.bucketsNew[newIndex] - } - } - - // pick a random index - randIndex := a.rand.Intn(len(bucket)) - - // loop over the map to return that index - var selectedAddr *p2p.NetAddress - for _, ka := range bucket { - if randIndex == 0 { - selectedAddr = ka.Addr - break - } - randIndex-- - } - - // if we have selected the address before, restart the loop - // otherwise, record it and continue - if pickFromOldBucket { - if addrsMap, ok := oldBucketToAddrsMap[oldIndex]; ok { - if _, ok = addrsMap[selectedAddr.String()]; ok { - continue ADDRS_LOOP - } - } else { - oldBucketToAddrsMap[oldIndex] = make(map[string]struct{}) - } - oldBucketToAddrsMap[oldIndex][selectedAddr.String()] = struct{}{} - oldAddressesAdded++ - } else { - if addrsMap, ok := newBucketToAddrsMap[newIndex]; ok { - if _, ok = addrsMap[selectedAddr.String()]; ok { - continue ADDRS_LOOP - } - } else { - newBucketToAddrsMap[newIndex] = make(map[string]struct{}) - } - newBucketToAddrsMap[newIndex][selectedAddr.String()] = struct{}{} - newAddressesAdded++ - } - - selection[selectionIndex] = selectedAddr - selectionIndex++ - } - + // if there are no enough old addrs, will choose new addr instead. + numRequiredNewAdd := cmn.MaxInt(percentageOfNum(biasTowardsNewAddrs, numAddresses), numAddresses-a.nOld) + selection := a.randomPickAddresses(bucketTypeNew, numRequiredNewAdd) + selection = append(selection, a.randomPickAddresses(bucketTypeOld, numAddresses-len(selection))...) return selection } @@ -726,6 +649,44 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return nil } +func (a *addrBook) randomPickAddresses(bucketType byte, num int) []*p2p.NetAddress { + var buckets []map[string]*knownAddress + switch bucketType { + case bucketTypeNew: + buckets = a.bucketsNew + case bucketTypeOld: + buckets = a.bucketsOld + default: + panic("unexpected bucketType") + } + total := 0 + for _, bucket := range buckets { + total = total + len(bucket) + } + addresses := make([]*knownAddress, 0, total) + for _, bucket := range buckets { + for _, ka := range bucket { + addresses = append(addresses, ka) + } + } + selection := make([]*p2p.NetAddress, 0, num) + chosenSet := make(map[string]bool, num) + rand.Shuffle(total, func(i, j int) { + addresses[i], addresses[j] = addresses[j], addresses[i] + }) + for _, addr := range addresses { + if chosenSet[addr.Addr.String()] { + continue + } + chosenSet[addr.Addr.String()] = true + selection = append(selection, addr.Addr) + if len(selection) >= num { + return selection + } + } + return selection +} + // Make space in the new buckets by expiring the really bad entries. // If no bad entries are available we remove the oldest. func (a *addrBook) expireNew(bucketIdx int) { diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index 9effa5d0e..fdcb0c8ad 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -435,12 +435,12 @@ func TestPrivatePeers(t *testing.T) { func testAddrBookAddressSelection(t *testing.T, bookSize int) { // generate all combinations of old (m) and new addresses - for nOld := 0; nOld <= bookSize; nOld++ { - nNew := bookSize - nOld - dbgStr := fmt.Sprintf("book of size %d (new %d, old %d)", bookSize, nNew, nOld) + for nBookOld := 0; nBookOld <= bookSize; nBookOld++ { + nBookNew := bookSize - nBookOld + dbgStr := fmt.Sprintf("book of size %d (new %d, old %d)", bookSize, nBookNew, nBookOld) // create book and get selection - book, fname := createAddrBookWithMOldAndNNewAddrs(t, nOld, nNew) + book, fname := createAddrBookWithMOldAndNNewAddrs(t, nBookOld, nBookNew) defer deleteTempFile(fname) addrs := book.GetSelectionWithBias(biasToSelectNewPeers) assert.NotNil(t, addrs, "%s - expected a non-nil selection", dbgStr) @@ -460,27 +460,25 @@ func testAddrBookAddressSelection(t *testing.T, bookSize int) { // Given: // n - num new addrs, m - num old addrs // k - num new addrs expected in the beginning (based on bias %) - // i=min(n, k), aka expFirstNew + // i=min(n, max(k,r-m)), aka expNew // j=min(m, r-i), aka expOld // // We expect this layout: - // indices: 0...i-1 i...i+j-1 i+j...r - // addresses: N0..Ni-1 O0..Oj-1 Ni... + // indices: 0...i-1 i...i+j-1 + // addresses: N0..Ni-1 O0..Oj-1 // // There is at least one partition and at most three. var ( - k = percentageOfNum(biasToSelectNewPeers, nAddrs) - expFirstNew = cmn.MinInt(nNew, k) - expOld = cmn.MinInt(nOld, nAddrs-expFirstNew) - expNew = nAddrs - expOld - expLastNew = expNew - expFirstNew + k = percentageOfNum(biasToSelectNewPeers, nAddrs) + expNew = cmn.MinInt(nNew, cmn.MaxInt(k, nAddrs-nBookOld)) + expOld = cmn.MinInt(nOld, nAddrs-expNew) ) // Verify that the number of old and new addresses are as expected - if nNew < expNew || nNew > expNew { + if nNew != expNew { t.Fatalf("%s - expected new addrs %d, got %d", dbgStr, expNew, nNew) } - if nOld < expOld || nOld > expOld { + if nOld != expOld { t.Fatalf("%s - expected old addrs %d, got %d", dbgStr, expOld, nOld) } @@ -499,15 +497,12 @@ func testAddrBookAddressSelection(t *testing.T, bookSize int) { case expOld == 0: // all new addresses expSeqLens = []int{nAddrs} expSeqTypes = []int{1} - case expFirstNew == 0: // all old addresses + case expNew == 0: // all old addresses expSeqLens = []int{nAddrs} expSeqTypes = []int{2} - case nAddrs-expFirstNew-expOld == 0: // new addresses, old addresses - expSeqLens = []int{expFirstNew, expOld} + case nAddrs-expNew-expOld == 0: // new addresses, old addresses + expSeqLens = []int{expNew, expOld} expSeqTypes = []int{1, 2} - default: // new addresses, old addresses, new addresses - expSeqLens = []int{expFirstNew, expOld, expLastNew} - expSeqTypes = []int{1, 2, 1} } assert.Equal(t, expSeqLens, seqLens, From 55b7118c981e93b920cead4616b91ec58130e316 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 15:35:32 +0100 Subject: [PATCH 128/259] Prep changelog: copy from pending & update version --- CHANGELOG.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f0ba675ae..4824c4f27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,35 @@ # Changelog +## v0.31.1 + +*March 27th, 2019* + +### BREAKING CHANGES: + +* CLI/RPC/Config + +* Apps + +* Go API +- [libs/common] Remove RepeatTimer (also TimerMaker and Ticker interface) +- [rpc/client] \#3458 Include NetworkClient interface into Client interface + +* Blockchain Protocol + +* P2P Protocol + +### FEATURES: +- [rpc] \#3419 Start HTTPS server if `rpc.tls_cert_file` and `rpc.tls_key_file` are provided in the config (@guagualvcha) + +### IMPROVEMENTS: + +- [mempool] \#2778 No longer send txs back to peers who sent it to you + +### BUG FIXES: + +- [blockchain] \#2699 update the maxHeight when a peer is removed + + ## v0.31.0 *March 16th, 2019* From ed63e1f378eaf465b714084fa1edd6ac4761782f Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 16:03:25 +0100 Subject: [PATCH 129/259] Add more entries to the Changelog, fix formatting, linkify --- CHANGELOG.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4824c4f27..7eb690e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ *March 27th, 2019* +Special thanks to external contributors on this release: + ### BREAKING CHANGES: * CLI/RPC/Config @@ -11,23 +13,30 @@ * Apps * Go API -- [libs/common] Remove RepeatTimer (also TimerMaker and Ticker interface) -- [rpc/client] \#3458 Include NetworkClient interface into Client interface + - [crypto] [\#3426](https://github.com/tendermint/tendermint/pull/3426) Remove `Ripemd160` helper method (@needkane) + - [libs/common] Remove `RepeatTimer` (also `TimerMaker` and `Ticker` interface) + - [rpc/client] [\#3458](https://github.com/tendermint/tendermint/issues/3458) Include `NetworkClient` interface into `Client` interface + - [types] [\#3448](https://github.com/tendermint/tendermint/issues/3448) Remove method `PB2TM.ConsensusParams` * Blockchain Protocol * P2P Protocol ### FEATURES: -- [rpc] \#3419 Start HTTPS server if `rpc.tls_cert_file` and `rpc.tls_key_file` are provided in the config (@guagualvcha) + + - [rpc] [\#3419](https://github.com/tendermint/tendermint/issues/3419) Start HTTPS server if `rpc.tls_cert_file` and `rpc.tls_key_file` are provided in the config (@guagualvcha) ### IMPROVEMENTS: -- [mempool] \#2778 No longer send txs back to peers who sent it to you +- [docs] [\#3140](https://github.com/tendermint/tendermint/issues/3140) Formalize proposer election algorithm properties +- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you +- [p2p] [\#3475](https://github.com/tendermint/tendermint/issues/3475) Simplify `GetSelectionWithBias` for addressbook +- [rpc/lib/client] [\#3430](https://github.com/tendermint/tendermint/issues/3430) Disable compression for HTTP client to prevent GZIP-bomb DoS attacks (@guagualvcha) ### BUG FIXES: -- [blockchain] \#2699 update the maxHeight when a peer is removed +- [blockchain] [\#2699](https://github.com/tendermint/tendermint/issues/2699) update the maxHeight when a peer is removed +- [mempool] [\#3478](https://github.com/tendermint/tendermint/issues/3478) Fix memory-leak related to `broadcastTxRoutine` (@HaoyangLiu) ## v0.31.0 From e3f840e6a6f81406c44e760e578e48fcd3fedf12 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 16:04:43 +0100 Subject: [PATCH 130/259] reset CHANGELOG_PENDING.md --- CHANGELOG_PENDING.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index eaf08928d..470282aa9 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,20 +9,14 @@ * Apps * Go API -- [libs/common] Remove RepeatTimer (also TimerMaker and Ticker interface) -- [rpc/client] \#3458 Include NetworkClient interface into Client interface * Blockchain Protocol * P2P Protocol ### FEATURES: -- [rpc] \#3419 Start HTTPS server if `rpc.tls_cert_file` and `rpc.tls_key_file` are provided in the config (@guagualvcha) ### IMPROVEMENTS: -- [mempool] \#2778 No longer send txs back to peers who sent it to you - ### BUG FIXES: -- [blockchain] \#2699 update the maxHeight when a peer is removed From 52727863e187e85901e7f2e7285920a701d2a8e6 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 16:07:03 +0100 Subject: [PATCH 131/259] add external contributors --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7eb690e80..6e87e2b76 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ *March 27th, 2019* Special thanks to external contributors on this release: +@guagualvcha, @HaoyangLiu, @needkane ### BREAKING CHANGES: From 5fa540bdc9eac01cc3df6de01119550d1122114b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 27 Mar 2019 16:45:34 +0100 Subject: [PATCH 132/259] mempool: add a safety check, write tests for mempoolIDs (#3487) * mempool: add a safety check, write tests for mempoolIDs and document 65536 limit in the mempool reactor spec follow-up to https://github.com/tendermint/tendermint/pull/2778 * rename the test * fixes after Ismail's review --- consensus/state_test.go | 6 +- docs/spec/reactors/mempool/reactor.md | 8 ++- mempool/mempool.go | 5 +- mempool/reactor.go | 24 +++++-- mempool/reactor_test.go | 35 +++++++++ p2p/dummy/peer.go | 100 -------------------------- p2p/mock/peer.go | 68 ++++++++++++++++++ p2p/pex/pex_reactor_test.go | 53 ++------------ 8 files changed, 140 insertions(+), 159 deletions(-) delete mode 100644 p2p/dummy/peer.go create mode 100644 p2p/mock/peer.go diff --git a/consensus/state_test.go b/consensus/state_test.go index a4d01e584..fc1e3e949 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -14,7 +14,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" - p2pdummy "github.com/tendermint/tendermint/p2p/dummy" + p2pmock "github.com/tendermint/tendermint/p2p/mock" "github.com/tendermint/tendermint/types" ) @@ -1548,7 +1548,7 @@ func TestStateHalt1(t *testing.T) { func TestStateOutputsBlockPartsStats(t *testing.T) { // create dummy peer cs, _ := randConsensusState(1) - peer := p2pdummy.NewPeer() + peer := p2pmock.NewPeer(nil) // 1) new block part parts := types.NewPartSetFromData(cmn.RandBytes(100), 10) @@ -1591,7 +1591,7 @@ func TestStateOutputsBlockPartsStats(t *testing.T) { func TestStateOutputVoteStats(t *testing.T) { cs, vss := randConsensusState(2) // create dummy peer - peer := p2pdummy.NewPeer() + peer := p2pmock.NewPeer(nil) vote := signVote(vss[1], types.PrecommitType, []byte("test"), types.PartSetHeader{}) diff --git a/docs/spec/reactors/mempool/reactor.md b/docs/spec/reactors/mempool/reactor.md index d0b19f7ca..d349fc7cc 100644 --- a/docs/spec/reactors/mempool/reactor.md +++ b/docs/spec/reactors/mempool/reactor.md @@ -13,4 +13,10 @@ for details. Sending incorrectly encoded data or data exceeding `maxMsgSize` will result in stopping the peer. -The mempool will not send a tx back to any peer which it received it from. \ No newline at end of file +The mempool will not send a tx back to any peer which it received it from. + +The reactor assigns an `uint16` number for each peer and maintains a map from +p2p.ID to `uint16`. Each mempool transaction carries a list of all the senders +(`[]uint16`). The list is updated every time mempool receives a transaction it +is already seen. `uint16` assumes that a node will never have over 65535 active +peers (0 is reserved for unknown source - e.g. RPC). diff --git a/mempool/mempool.go b/mempool/mempool.go index 2064b7bce..bd3cbf7d9 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -382,7 +382,10 @@ func (mem *Mempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo if !mem.cache.Push(tx) { // record the sender e, ok := mem.txsMap[sha256.Sum256(tx)] - if ok { // tx may be in cache, but not in the mempool + // The check is needed because tx may be in cache, but not in the mempool. + // E.g. after we've committed a block, txs are removed from the mempool, + // but not from the cache. + if ok { memTx := e.Value.(*mempoolTx) if _, loaded := memTx.senders.LoadOrStore(txInfo.PeerID, true); loaded { // TODO: consider punishing peer for dups, diff --git a/mempool/reactor.go b/mempool/reactor.go index 23fec2700..e1376b287 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -2,6 +2,7 @@ package mempool import ( "fmt" + "math" "reflect" "sync" "time" @@ -26,6 +27,8 @@ const ( // UnknownPeerID is the peer ID to use when running CheckTx when there is // no peer (e.g. RPC) UnknownPeerID uint16 = 0 + + maxActiveIDs = math.MaxUint16 ) // MempoolReactor handles mempool tx broadcasting amongst peers. @@ -45,7 +48,8 @@ type mempoolIDs struct { activeIDs map[uint16]struct{} // used to check if a given peerID key is used, the value doesn't matter } -// Reserve searches for the next unused ID and assignes it to the peer. +// Reserve searches for the next unused ID and assignes it to the +// peer. func (ids *mempoolIDs) ReserveForPeer(peer p2p.Peer) { ids.mtx.Lock() defer ids.mtx.Unlock() @@ -58,6 +62,10 @@ func (ids *mempoolIDs) ReserveForPeer(peer p2p.Peer) { // nextPeerID returns the next unused peer ID to use. // This assumes that ids's mutex is already locked. func (ids *mempoolIDs) nextPeerID() uint16 { + if len(ids.activeIDs) == maxActiveIDs { + panic(fmt.Sprintf("node has maximum %d active IDs and wanted to get one more", maxActiveIDs)) + } + _, idExists := ids.activeIDs[ids.nextID] for idExists { ids.nextID++ @@ -88,16 +96,20 @@ func (ids *mempoolIDs) GetForPeer(peer p2p.Peer) uint16 { return ids.peerMap[peer.ID()] } +func newMempoolIDs() *mempoolIDs { + return &mempoolIDs{ + peerMap: make(map[p2p.ID]uint16), + activeIDs: map[uint16]struct{}{0: {}}, + nextID: 1, // reserve unknownPeerID(0) for mempoolReactor.BroadcastTx + } +} + // NewMempoolReactor returns a new MempoolReactor with the given config and mempool. func NewMempoolReactor(config *cfg.MempoolConfig, mempool *Mempool) *MempoolReactor { memR := &MempoolReactor{ config: config, Mempool: mempool, - ids: &mempoolIDs{ - peerMap: make(map[p2p.ID]uint16), - activeIDs: map[uint16]struct{}{0: {}}, - nextID: 1, // reserve unknownPeerID(0) for mempoolReactor.BroadcastTx - }, + ids: newMempoolIDs(), } memR.BaseReactor = *p2p.NewBaseReactor("MempoolReactor", memR) return memR diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index f16f84479..c9cf49809 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -2,6 +2,7 @@ package mempool import ( "fmt" + "net" "sync" "testing" "time" @@ -15,6 +16,7 @@ import ( cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/mock" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" ) @@ -189,3 +191,36 @@ func TestBroadcastTxForPeerStopsWhenReactorStops(t *testing.T) { // i.e. broadcastTxRoutine finishes when reactor is stopped leaktest.CheckTimeout(t, 10*time.Second)() } + +func TestMempoolIDsBasic(t *testing.T) { + ids := newMempoolIDs() + + peer := mock.NewPeer(net.IP{127, 0, 0, 1}) + + ids.ReserveForPeer(peer) + assert.EqualValues(t, 1, ids.GetForPeer(peer)) + ids.Reclaim(peer) + + ids.ReserveForPeer(peer) + assert.EqualValues(t, 2, ids.GetForPeer(peer)) + ids.Reclaim(peer) +} + +func TestMempoolIDsPanicsIfNodeRequestsOvermaxActiveIDs(t *testing.T) { + if testing.Short() { + return + } + + // 0 is already reserved for UnknownPeerID + ids := newMempoolIDs() + + for i := 0; i < maxActiveIDs-1; i++ { + peer := mock.NewPeer(net.IP{127, 0, 0, 1}) + ids.ReserveForPeer(peer) + } + + assert.Panics(t, func() { + peer := mock.NewPeer(net.IP{127, 0, 0, 1}) + ids.ReserveForPeer(peer) + }) +} diff --git a/p2p/dummy/peer.go b/p2p/dummy/peer.go deleted file mode 100644 index 57edafc67..000000000 --- a/p2p/dummy/peer.go +++ /dev/null @@ -1,100 +0,0 @@ -package dummy - -import ( - "net" - - cmn "github.com/tendermint/tendermint/libs/common" - p2p "github.com/tendermint/tendermint/p2p" - tmconn "github.com/tendermint/tendermint/p2p/conn" -) - -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 -} - -// FlushStop just calls Stop. -func (p *peer) FlushStop() { - p.Stop() -} - -// 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.DefaultNodeInfo{} -} - -// RemoteIP always returns localhost. -func (p *peer) RemoteIP() net.IP { - return net.ParseIP("127.0.0.1") -} - -// Addr always returns tcp://localhost:8800. -func (p *peer) RemoteAddr() net.Addr { - return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8800} -} - -// CloseConn always returns nil. -func (p *peer) CloseConn() error { - return nil -} - -// 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 -} - -// OriginalAddr always returns nil. -func (p *peer) OriginalAddr() *p2p.NetAddress { - return nil -} diff --git a/p2p/mock/peer.go b/p2p/mock/peer.go new file mode 100644 index 000000000..5ee81f67e --- /dev/null +++ b/p2p/mock/peer.go @@ -0,0 +1,68 @@ +package mock + +import ( + "net" + + "github.com/tendermint/tendermint/crypto/ed25519" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/conn" +) + +type Peer struct { + *cmn.BaseService + ip net.IP + id p2p.ID + addr *p2p.NetAddress + kv map[string]interface{} + Outbound, Persistent bool +} + +// NewPeer creates and starts a new mock peer. If the ip +// is nil, random routable address is used. +func NewPeer(ip net.IP) *Peer { + var netAddr *p2p.NetAddress + if ip == nil { + _, netAddr = p2p.CreateRoutableAddr() + } else { + netAddr = p2p.NewNetAddressIPPort(ip, 26656) + } + nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()} + netAddr.ID = nodeKey.ID() + mp := &Peer{ + ip: ip, + id: nodeKey.ID(), + addr: netAddr, + kv: make(map[string]interface{}), + } + mp.BaseService = cmn.NewBaseService(nil, "MockPeer", mp) + mp.Start() + return mp +} + +func (mp *Peer) FlushStop() { mp.Stop() } +func (mp *Peer) TrySend(chID byte, msgBytes []byte) bool { return true } +func (mp *Peer) Send(chID byte, msgBytes []byte) bool { return true } +func (mp *Peer) NodeInfo() p2p.NodeInfo { + return p2p.DefaultNodeInfo{ + ID_: mp.addr.ID, + ListenAddr: mp.addr.DialString(), + } +} +func (mp *Peer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} } +func (mp *Peer) ID() p2p.ID { return mp.id } +func (mp *Peer) IsOutbound() bool { return mp.Outbound } +func (mp *Peer) IsPersistent() bool { return mp.Persistent } +func (mp *Peer) Get(key string) interface{} { + if value, ok := mp.kv[key]; ok { + return value + } + return nil +} +func (mp *Peer) Set(key string, value interface{}) { + mp.kv[key] = value +} +func (mp *Peer) RemoteIP() net.IP { return mp.ip } +func (mp *Peer) OriginalAddr() *p2p.NetAddress { return mp.addr } +func (mp *Peer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} } +func (mp *Peer) CloseConn() error { return nil } diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 4f4ccb039..9e23058a5 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -3,7 +3,6 @@ package pex import ( "fmt" "io/ioutil" - "net" "os" "path/filepath" "testing" @@ -12,14 +11,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/p2p" - "github.com/tendermint/tendermint/p2p/conn" + "github.com/tendermint/tendermint/p2p/mock" ) var ( @@ -148,7 +143,7 @@ func TestPEXReactorRequestMessageAbuse(t *testing.T) { sw := createSwitchAndAddReactors(r) sw.SetAddrBook(book) - peer := newMockPeer() + peer := mock.NewPeer(nil) p2p.AddPeerToSwitch(sw, peer) assert.True(t, sw.Peers().Has(peer.ID())) @@ -178,7 +173,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { sw := createSwitchAndAddReactors(r) sw.SetAddrBook(book) - peer := newMockPeer() + peer := mock.NewPeer(nil) p2p.AddPeerToSwitch(sw, peer) assert.True(t, sw.Peers().Has(peer.ID())) @@ -418,7 +413,7 @@ func TestPEXReactorDialPeer(t *testing.T) { sw := createSwitchAndAddReactors(pexR) sw.SetAddrBook(book) - peer := newMockPeer() + peer := mock.NewPeer(nil) addr := peer.NodeInfo().NetAddress() assert.Equal(t, 0, pexR.AttemptsToDial(addr)) @@ -444,44 +439,6 @@ func TestPEXReactorDialPeer(t *testing.T) { } } -type mockPeer struct { - *cmn.BaseService - pubKey crypto.PubKey - addr *p2p.NetAddress - outbound, persistent bool -} - -func newMockPeer() mockPeer { - _, netAddr := p2p.CreateRoutableAddr() - mp := mockPeer{ - addr: netAddr, - pubKey: ed25519.GenPrivKey().PubKey(), - } - mp.BaseService = cmn.NewBaseService(nil, "MockPeer", mp) - mp.Start() - return mp -} - -func (mp mockPeer) FlushStop() { mp.Stop() } -func (mp mockPeer) ID() p2p.ID { return mp.addr.ID } -func (mp mockPeer) IsOutbound() bool { return mp.outbound } -func (mp mockPeer) IsPersistent() bool { return mp.persistent } -func (mp mockPeer) NodeInfo() p2p.NodeInfo { - return p2p.DefaultNodeInfo{ - ID_: mp.addr.ID, - ListenAddr: mp.addr.DialString(), - } -} -func (mockPeer) RemoteIP() net.IP { return net.ParseIP("127.0.0.1") } -func (mockPeer) Status() conn.ConnectionStatus { return conn.ConnectionStatus{} } -func (mockPeer) Send(byte, []byte) bool { return false } -func (mockPeer) TrySend(byte, []byte) bool { return false } -func (mockPeer) Set(string, interface{}) {} -func (mockPeer) Get(string) interface{} { return nil } -func (mockPeer) OriginalAddr() *p2p.NetAddress { return nil } -func (mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 8800} } -func (mockPeer) CloseConn() error { return nil } - func assertPeersWithTimeout( t *testing.T, switches []*p2p.Switch, From 3c7bb6b571ac47e050a47013e22dbdfd68f02745 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 16:48:00 +0100 Subject: [PATCH 133/259] Add some numbers for #2778 --- CHANGELOG.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e87e2b76..4f2a9971f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,15 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: - [docs] [\#3140](https://github.com/tendermint/tendermint/issues/3140) Formalize proposer election algorithm properties -- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you +- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you and limit to 65536 active peers. +This vastly improves the the b bandwidth consumption of nodes. +E.g. 250bytes txs for 120 sec. at 500 txs/sec. rate, so total 15MB: + - total bytes received from 1st node in 4 node localnet + - before: 42793967 (43MB) + - after: 30003256 (30MB) + - total bytes sent to 1st node in 4 node localnet + - before: 30569339 (30MB) + - after: 19304964 (19MB) - [p2p] [\#3475](https://github.com/tendermint/tendermint/issues/3475) Simplify `GetSelectionWithBias` for addressbook - [rpc/lib/client] [\#3430](https://github.com/tendermint/tendermint/issues/3430) Disable compression for HTTP client to prevent GZIP-bomb DoS attacks (@guagualvcha) From 233813483642c2ac370110dd42c3291556c26039 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 16:52:19 +0100 Subject: [PATCH 134/259] bump versions --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index b2202206c..9090fc7e6 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.0" + TMCoreSemVer = "0.31.1" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From ae88965ff6ddf174607a02eee83cfc5ed11c6cbb Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 17:46:29 +0100 Subject: [PATCH 135/259] changelog: add summary & fix link & add external contributor (#3490) --- CHANGELOG.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f2a9971f..1b3ee16d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ *March 27th, 2019* +This release contains a major improvement for the mempool that reduce the amount of sent data by about 30% +(see some numbers below). +It also fixes a memory leak in the mempool and gives you the ability to run HTTPS RPC servers (over TLS) by providing a certificate and key in the config. + Special thanks to external contributors on this release: @guagualvcha, @HaoyangLiu, @needkane @@ -15,7 +19,7 @@ Special thanks to external contributors on this release: * Go API - [crypto] [\#3426](https://github.com/tendermint/tendermint/pull/3426) Remove `Ripemd160` helper method (@needkane) - - [libs/common] Remove `RepeatTimer` (also `TimerMaker` and `Ticker` interface) + - [libs/common] [\#3429](https://github.com/tendermint/tendermint/pull/3429) Remove `RepeatTimer` (also `TimerMaker` and `Ticker` interface) - [rpc/client] [\#3458](https://github.com/tendermint/tendermint/issues/3458) Include `NetworkClient` interface into `Client` interface - [types] [\#3448](https://github.com/tendermint/tendermint/issues/3448) Remove method `PB2TM.ConsensusParams` @@ -30,7 +34,8 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: - [docs] [\#3140](https://github.com/tendermint/tendermint/issues/3140) Formalize proposer election algorithm properties -- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you and limit to 65536 active peers. +- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you. +Also, limit to 65536 active peers. This vastly improves the the b bandwidth consumption of nodes. E.g. 250bytes txs for 120 sec. at 500 txs/sec. rate, so total 15MB: - total bytes received from 1st node in 4 node localnet @@ -39,7 +44,7 @@ E.g. 250bytes txs for 120 sec. at 500 txs/sec. rate, so total 15MB: - total bytes sent to 1st node in 4 node localnet - before: 30569339 (30MB) - after: 19304964 (19MB) -- [p2p] [\#3475](https://github.com/tendermint/tendermint/issues/3475) Simplify `GetSelectionWithBias` for addressbook +- [p2p] [\#3475](https://github.com/tendermint/tendermint/issues/3475) Simplify `GetSelectionWithBias` for addressbook (@guagualvcha) - [rpc/lib/client] [\#3430](https://github.com/tendermint/tendermint/issues/3430) Disable compression for HTTP client to prevent GZIP-bomb DoS attacks (@guagualvcha) ### BUG FIXES: From d586945d6981262759e0e681e7bc92a74637185d Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Wed, 27 Mar 2019 18:51:57 +0100 Subject: [PATCH 136/259] docs: Fix broken links (#3482) (#3488) * docs: fix broken links (#3482) A bunch of links were broken in the documentation s they included the `docs` prefix. * Update CHANGELOG_PENDING * docs: switch to relative links for github compatitibility (#3482) --- CHANGELOG_PENDING.md | 1 + docs/networks/docker-compose.md | 2 +- docs/spec/blockchain/blockchain.md | 8 ++++---- docs/spec/blockchain/encoding.md | 2 +- docs/spec/consensus/abci.md | 2 +- docs/spec/consensus/wal.md | 2 +- 6 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index eaf08928d..0030611cd 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -26,3 +26,4 @@ ### BUG FIXES: - [blockchain] \#2699 update the maxHeight when a peer is removed +- [docs] \#3482 fix broken links (@brapse) diff --git a/docs/networks/docker-compose.md b/docs/networks/docker-compose.md index 7e4adde8d..8db49af5e 100644 --- a/docs/networks/docker-compose.md +++ b/docs/networks/docker-compose.md @@ -4,7 +4,7 @@ With Docker Compose, you can spin up local testnets with a single command. ## Requirements -1. [Install tendermint](/docs/introduction/install.md) +1. [Install tendermint](../introduction/install.md) 2. [Install docker](https://docs.docker.com/engine/installation/) 3. [Install docker-compose](https://docs.docker.com/compose/install/) diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index 60a07d42a..9f88d6417 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -103,7 +103,7 @@ type PartSetHeader struct { } ``` -See [MerkleRoot](/docs/spec/blockchain/encoding.md#MerkleRoot) for details. +See [MerkleRoot](./encoding.md#MerkleRoot) for details. ## Time @@ -163,7 +163,7 @@ a _precommit_ has `vote.Type == 2`. Signatures in Tendermint are raw bytes representing the underlying signature. -See the [signature spec](/docs/spec/blockchain/encoding.md#key-types) for more. +See the [signature spec](./encoding.md#key-types) for more. ## EvidenceData @@ -190,7 +190,7 @@ type DuplicateVoteEvidence struct { } ``` -See the [pubkey spec](/docs/spec/blockchain/encoding.md#key-types) for more. +See the [pubkey spec](./encoding.md#key-types) for more. ## Validation @@ -209,7 +209,7 @@ the current version of the `state` corresponds to the state after executing transactions from the `prevBlock`. Elements of an object are accessed as expected, ie. `block.Header`. -See the [definition of `State`](/docs/spec/blockchain/state.md). +See the [definition of `State`](./state.md). ### Header diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index e8258e4a9..bde580a14 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -339,6 +339,6 @@ type CanonicalVote struct { The field ordering and the fixed sized encoding for the first three fields is optimized to ease parsing of SignBytes in HSMs. It creates fixed offsets for relevant fields that need to be read in this context. -For more details, see the [signing spec](/docs/spec/consensus/signing.md). +For more details, see the [signing spec](../consensus/signing.md). Also, see the motivating discussion in [#1622](https://github.com/tendermint/tendermint/issues/1622). diff --git a/docs/spec/consensus/abci.md b/docs/spec/consensus/abci.md index 82b88161e..226d22899 100644 --- a/docs/spec/consensus/abci.md +++ b/docs/spec/consensus/abci.md @@ -1 +1 @@ -[Moved](/docs/spec/software/abci.md) +[Moved](../software/abci.md) diff --git a/docs/spec/consensus/wal.md b/docs/spec/consensus/wal.md index 589680f99..6146ab9c0 100644 --- a/docs/spec/consensus/wal.md +++ b/docs/spec/consensus/wal.md @@ -1 +1 @@ -[Moved](/docs/spec/software/wal.md) +[Moved](../software/wal.md) From ccfe75ec4a701ad7dcaf1f26df772b0881ab1184 Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Wed, 27 Mar 2019 18:51:57 +0100 Subject: [PATCH 137/259] docs: Fix broken links (#3482) (#3488) * docs: fix broken links (#3482) A bunch of links were broken in the documentation s they included the `docs` prefix. * Update CHANGELOG_PENDING * docs: switch to relative links for github compatitibility (#3482) --- docs/networks/docker-compose.md | 2 +- docs/spec/blockchain/blockchain.md | 8 ++++---- docs/spec/blockchain/encoding.md | 2 +- docs/spec/consensus/abci.md | 2 +- docs/spec/consensus/wal.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/networks/docker-compose.md b/docs/networks/docker-compose.md index 7e4adde8d..8db49af5e 100644 --- a/docs/networks/docker-compose.md +++ b/docs/networks/docker-compose.md @@ -4,7 +4,7 @@ With Docker Compose, you can spin up local testnets with a single command. ## Requirements -1. [Install tendermint](/docs/introduction/install.md) +1. [Install tendermint](../introduction/install.md) 2. [Install docker](https://docs.docker.com/engine/installation/) 3. [Install docker-compose](https://docs.docker.com/compose/install/) diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index 60a07d42a..9f88d6417 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -103,7 +103,7 @@ type PartSetHeader struct { } ``` -See [MerkleRoot](/docs/spec/blockchain/encoding.md#MerkleRoot) for details. +See [MerkleRoot](./encoding.md#MerkleRoot) for details. ## Time @@ -163,7 +163,7 @@ a _precommit_ has `vote.Type == 2`. Signatures in Tendermint are raw bytes representing the underlying signature. -See the [signature spec](/docs/spec/blockchain/encoding.md#key-types) for more. +See the [signature spec](./encoding.md#key-types) for more. ## EvidenceData @@ -190,7 +190,7 @@ type DuplicateVoteEvidence struct { } ``` -See the [pubkey spec](/docs/spec/blockchain/encoding.md#key-types) for more. +See the [pubkey spec](./encoding.md#key-types) for more. ## Validation @@ -209,7 +209,7 @@ the current version of the `state` corresponds to the state after executing transactions from the `prevBlock`. Elements of an object are accessed as expected, ie. `block.Header`. -See the [definition of `State`](/docs/spec/blockchain/state.md). +See the [definition of `State`](./state.md). ### Header diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index e8258e4a9..bde580a14 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -339,6 +339,6 @@ type CanonicalVote struct { The field ordering and the fixed sized encoding for the first three fields is optimized to ease parsing of SignBytes in HSMs. It creates fixed offsets for relevant fields that need to be read in this context. -For more details, see the [signing spec](/docs/spec/consensus/signing.md). +For more details, see the [signing spec](../consensus/signing.md). Also, see the motivating discussion in [#1622](https://github.com/tendermint/tendermint/issues/1622). diff --git a/docs/spec/consensus/abci.md b/docs/spec/consensus/abci.md index 82b88161e..226d22899 100644 --- a/docs/spec/consensus/abci.md +++ b/docs/spec/consensus/abci.md @@ -1 +1 @@ -[Moved](/docs/spec/software/abci.md) +[Moved](../software/abci.md) diff --git a/docs/spec/consensus/wal.md b/docs/spec/consensus/wal.md index 589680f99..6146ab9c0 100644 --- a/docs/spec/consensus/wal.md +++ b/docs/spec/consensus/wal.md @@ -1 +1 @@ -[Moved](/docs/spec/software/wal.md) +[Moved](../software/wal.md) From a49d80b89c9df3cb2497adb8175915c654479708 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Wed, 27 Mar 2019 19:12:01 +0100 Subject: [PATCH 138/259] catch up with develop and rebase on current release to include #3482 --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b3ee16d5..69c4f6071 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ This release contains a major improvement for the mempool that reduce the amount It also fixes a memory leak in the mempool and gives you the ability to run HTTPS RPC servers (over TLS) by providing a certificate and key in the config. Special thanks to external contributors on this release: -@guagualvcha, @HaoyangLiu, @needkane +@brapse, @guagualvcha, @HaoyangLiu, @needkane, @TraceBundy ### BREAKING CHANGES: @@ -34,6 +34,7 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: - [docs] [\#3140](https://github.com/tendermint/tendermint/issues/3140) Formalize proposer election algorithm properties +- [docs] [\#3482](https://github.com/tendermint/tendermint/issues/3482) fix broken links (@brapse) - [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you. Also, limit to 65536 active peers. This vastly improves the the b bandwidth consumption of nodes. From 9390a810ebda0b1aebd90f6e8b8688562bdf7958 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Wed, 27 Mar 2019 21:03:28 -0400 Subject: [PATCH 139/259] minor changelog updates (#3499) --- CHANGELOG.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c4f6071..31ee14c51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,9 @@ *March 27th, 2019* -This release contains a major improvement for the mempool that reduce the amount of sent data by about 30% +This release contains a major improvement for the mempool that reduce the amount of sent data by about 30% (see some numbers below). -It also fixes a memory leak in the mempool and gives you the ability to run HTTPS RPC servers (over TLS) by providing a certificate and key in the config. +It also fixes a memory leak in the mempool and adds TLS support to the RPC server by providing a certificate and key in the config. Special thanks to external contributors on this release: @brapse, @guagualvcha, @HaoyangLiu, @needkane, @TraceBundy @@ -34,23 +34,23 @@ Special thanks to external contributors on this release: ### IMPROVEMENTS: - [docs] [\#3140](https://github.com/tendermint/tendermint/issues/3140) Formalize proposer election algorithm properties -- [docs] [\#3482](https://github.com/tendermint/tendermint/issues/3482) fix broken links (@brapse) -- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you. +- [docs] [\#3482](https://github.com/tendermint/tendermint/issues/3482) Fix broken links (@brapse) +- [mempool] [\#2778](https://github.com/tendermint/tendermint/issues/2778) No longer send txs back to peers who sent it to you. Also, limit to 65536 active peers. -This vastly improves the the b bandwidth consumption of nodes. -E.g. 250bytes txs for 120 sec. at 500 txs/sec. rate, so total 15MB: - - total bytes received from 1st node in 4 node localnet +This vastly improves the bandwidth consumption of nodes. +For instance, for a 4 node localnet, in a test sending 250byte txs for 120 sec. at 500 txs/sec (total of 15MB): + - total bytes received from 1st node: - before: 42793967 (43MB) - - after: 30003256 (30MB) - - total bytes sent to 1st node in 4 node localnet + - after: 30003256 (30MB) + - total bytes sent to 1st node: - before: 30569339 (30MB) - - after: 19304964 (19MB) + - after: 19304964 (19MB) - [p2p] [\#3475](https://github.com/tendermint/tendermint/issues/3475) Simplify `GetSelectionWithBias` for addressbook (@guagualvcha) - [rpc/lib/client] [\#3430](https://github.com/tendermint/tendermint/issues/3430) Disable compression for HTTP client to prevent GZIP-bomb DoS attacks (@guagualvcha) ### BUG FIXES: -- [blockchain] [\#2699](https://github.com/tendermint/tendermint/issues/2699) update the maxHeight when a peer is removed +- [blockchain] [\#2699](https://github.com/tendermint/tendermint/issues/2699) Update the maxHeight when a peer is removed - [mempool] [\#3478](https://github.com/tendermint/tendermint/issues/3478) Fix memory-leak related to `broadcastTxRoutine` (@HaoyangLiu) From 6c1a4b513747fed5be06edffd16db4a1c8749041 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Thu, 28 Mar 2019 17:39:09 +0100 Subject: [PATCH 140/259] blockchain: comment out logger in test code that causes a race condition (#3500) --- blockchain/pool_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/blockchain/pool_test.go b/blockchain/pool_test.go index e24f6131e..01d7dba20 100644 --- a/blockchain/pool_test.go +++ b/blockchain/pool_test.go @@ -42,7 +42,9 @@ func (p testPeer) runInputRoutine() { func (p testPeer) simulateInput(input inputData) { block := &types.Block{Header: types.Header{Height: input.request.Height}} input.pool.AddBlock(input.request.PeerID, block, 123) - input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height) + // TODO: uncommenting this creates a race which is detected by: https://github.com/golang/go/blob/2bd767b1022dd3254bcec469f0ee164024726486/src/testing/testing.go#L854-L856 + // see: https://github.com/tendermint/tendermint/issues/3390#issue-418379890 + // input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height) } type testPeers map[p2p.ID]testPeer From 9199f3f6132ff24500b538068450eae7d11bef64 Mon Sep 17 00:00:00 2001 From: Greg Szabo <16846635+greg-szabo@users.noreply.github.com> Date: Fri, 29 Mar 2019 07:57:16 -0400 Subject: [PATCH 141/259] Release management using CircleCI (#3498) * Release management using CircleCI * Changelog updated --- .circleci/config.yml | 128 +++++++++++++++++- CHANGELOG_PENDING.md | 2 + DOCKER/Dockerfile | 4 +- Makefile | 11 +- scripts/release_management/README.md | 65 +++++++++ scripts/release_management/bump-semver.py | 37 +++++ scripts/release_management/github-draft.py | 60 ++++++++ scripts/release_management/github-openpr.py | 52 +++++++ .../github-public-newbranch.bash | 28 ++++ scripts/release_management/github-publish.py | 53 ++++++++ scripts/release_management/github-upload.py | 68 ++++++++++ scripts/release_management/sha-files.py | 35 +++++ scripts/release_management/zip-file.py | 44 ++++++ 13 files changed, 578 insertions(+), 9 deletions(-) create mode 100644 scripts/release_management/README.md create mode 100755 scripts/release_management/bump-semver.py create mode 100755 scripts/release_management/github-draft.py create mode 100755 scripts/release_management/github-openpr.py create mode 100644 scripts/release_management/github-public-newbranch.bash create mode 100755 scripts/release_management/github-publish.py create mode 100755 scripts/release_management/github-upload.py create mode 100755 scripts/release_management/sha-files.py create mode 100755 scripts/release_management/zip-file.py diff --git a/.circleci/config.yml b/.circleci/config.yml index 9c51bc48f..7ad793549 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ version: 2 defaults: &defaults working_directory: /go/src/github.com/tendermint/tendermint docker: - - image: circleci/golang:1.12.0 + - image: circleci/golang environment: GOBIN: /tmp/workspace/bin @@ -14,6 +14,9 @@ docs_update_config: &docs_update_config environment: AWS_REGION: us-east-1 +release_management_docker: &release_management_docker + machine: true + jobs: setup_dependencies: <<: *defaults @@ -192,7 +195,7 @@ jobs: name: run localnet and exit on failure command: | set -x - docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.11.4 make build-linux + docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux make localnet-start & ./scripts/localnet-blocks-test.sh 40 5 10 localhost @@ -256,6 +259,105 @@ jobs: echo "Website build started" fi + prepare_build: + <<: *defaults + steps: + - checkout + - run: + name: Get next release number + command: | + export LAST_TAG="`git describe --tags --abbrev=0 --match "${CIRCLE_BRANCH}.*"`" + echo "Last tag: ${LAST_TAG}" + if [ -z "${LAST_TAG}" ]; then + export LAST_TAG="${CIRCLE_BRANCH}" + echo "Last tag not found. Possibly fresh branch or feature branch. Setting ${LAST_TAG} as tag." + fi + export NEXT_TAG="`python -u scripts/release_management/bump-semver.py --version "${LAST_TAG}"`" + echo "Next tag: ${NEXT_TAG}" + echo "export CIRCLE_TAG=\"${NEXT_TAG}\"" > release-version.source + - run: + name: Build dependencies + command: | + make get_tools get_vendor_deps + - persist_to_workspace: + root: . + paths: + - "release-version.source" + - save_cache: + key: v1-release-deps-{{ .Branch }}-{{ .Revision }} + paths: + - "vendor" + + build_artifacts: + <<: *defaults + parallelism: 4 + steps: + - checkout + - restore_cache: + keys: + - v1-release-deps-{{ .Branch }}-{{ .Revision }} + - attach_workspace: + at: /tmp/workspace + - run: + name: Build artifact + command: | + # Setting CIRCLE_TAG because we do not tag the release ourselves. + source /tmp/workspace/release-version.source + if test ${CIRCLE_NODE_INDEX:-0} == 0 ;then export GOOS=linux GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi + if test ${CIRCLE_NODE_INDEX:-0} == 1 ;then export GOOS=darwin GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi + if test ${CIRCLE_NODE_INDEX:-0} == 2 ;then export GOOS=windows GOARCH=amd64 && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi + if test ${CIRCLE_NODE_INDEX:-0} == 3 ;then export GOOS=linux GOARCH=arm && export OUTPUT=build/tendermint_${GOOS}_${GOARCH} && make build && python -u scripts/release_management/zip-file.py ;fi + - persist_to_workspace: + root: build + paths: + - "*.zip" + - "tendermint_linux_amd64" + + release_artifacts: + <<: *defaults + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: Deploy to GitHub + command: | + # Setting CIRCLE_TAG because we do not tag the release ourselves. + source /tmp/workspace/release-version.source + echo "---" + ls -la /tmp/workspace/*.zip + echo "---" + python -u scripts/release_management/sha-files.py + echo "---" + cat /tmp/workspace/SHA256SUMS + echo "---" + export RELEASE_ID="`python -u scripts/release_management/github-draft.py`" + echo "Release ID: ${RELEASE_ID}" + #Todo: Parallelize uploads + export GOOS=linux GOARCH=amd64 && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}" + export GOOS=darwin GOARCH=amd64 && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}" + export GOOS=windows GOARCH=amd64 && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}" + export GOOS=linux GOARCH=arm && python -u scripts/release_management/github-upload.py --id "${RELEASE_ID}" + python -u scripts/release_management/github-upload.py --file "/tmp/workspace/SHA256SUMS" --id "${RELEASE_ID}" + python -u scripts/release_management/github-publish.py --id "${RELEASE_ID}" + + release_docker: + <<: *release_management_docker + steps: + - checkout + - attach_workspace: + at: /tmp/workspace + - run: + name: Deploy to Docker Hub + command: | + # Setting CIRCLE_TAG because we do not tag the release ourselves. + source /tmp/workspace/release-version.source + cp /tmp/workspace/tendermint_linux_amd64 DOCKER/tendermint + docker build --label="tendermint" --tag="tendermint/tendermint:${CIRCLE_TAG}" --tag="tendermint/tendermint:latest" "DOCKER" + docker login -u "${DOCKERHUB_USER}" --password-stdin <<< "${DOCKERHUB_PASS}" + docker push "tendermint/tendermint" + docker logout + workflows: version: 2 test-suite: @@ -292,3 +394,25 @@ workflows: - upload_coverage: requires: - test_cover + release: + jobs: + - prepare_build + - build_artifacts: + requires: + - prepare_build + - release_artifacts: + requires: + - prepare_build + - build_artifacts + filters: + branches: + only: + - /v[0-9]+\.[0-9]+/ + - release_docker: + requires: + - prepare_build + - build_artifacts + filters: + branches: + only: + - /v[0-9]+\.[0-9]+/ diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 37ae3a510..a1745c193 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,4 +18,6 @@ ### IMPROVEMENTS: +- [CircleCI] \#3497 Move release management to CircleCI + ### BUG FIXES: diff --git a/DOCKER/Dockerfile b/DOCKER/Dockerfile index 4a855f425..6a7f289f5 100644 --- a/DOCKER/Dockerfile +++ b/DOCKER/Dockerfile @@ -1,5 +1,5 @@ -FROM alpine:3.7 -MAINTAINER Greg Szabo +FROM alpine:3.9 +LABEL maintainer="hello@tendermint.com" # Tendermint will be looking for the genesis file in /tendermint/config/genesis.json # (unless you change `genesis_file` in config.toml). You can put your config.toml and diff --git a/Makefile b/Makefile index 79ae6aaba..7c2ce1d9c 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ GOTOOLS = \ github.com/square/certstrap GOBIN?=${GOPATH}/bin PACKAGES=$(shell go list ./...) +OUTPUT?=build/tendermint INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf BUILD_TAGS?='tendermint' @@ -19,13 +20,13 @@ check: check_tools get_vendor_deps ### Build Tendermint build: - CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o build/tendermint ./cmd/tendermint/ + CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/ build_c: - CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" -o build/tendermint ./cmd/tendermint/ + CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" -o $(OUTPUT) ./cmd/tendermint/ build_race: - CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o build/tendermint ./cmd/tendermint + CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint install: CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint @@ -109,7 +110,7 @@ draw_deps: get_deps_bin_size: @# Copy of build recipe with additional flags to perform binary size analysis - $(eval $(shell go build -work -a $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o build/tendermint ./cmd/tendermint/ 2>&1)) + $(eval $(shell go build -work -a $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/ 2>&1)) @find $(WORK) -type f -name "*.a" | xargs -I{} du -hxs "{}" | sort -rh | sed -e s:${WORK}/::g > deps_bin_size.log @echo "Results can be found here: $(CURDIR)/deps_bin_size.log" @@ -261,7 +262,7 @@ check_dep: ### Docker image build-docker: - cp build/tendermint DOCKER/tendermint + cp $(OUTPUT) DOCKER/tendermint docker build --label=tendermint --tag="tendermint/tendermint" DOCKER rm -rf DOCKER/tendermint diff --git a/scripts/release_management/README.md b/scripts/release_management/README.md new file mode 100644 index 000000000..e92f1ccf6 --- /dev/null +++ b/scripts/release_management/README.md @@ -0,0 +1,65 @@ +# Release management scripts + +## Overview +The scripts in this folder are used for release management in CircleCI. Although the scripts are fully configurable using input parameters, +the default settings were modified to accommodate CircleCI execution. + +# Build scripts +These scripts help during the build process. They prepare the release files. + +## bump-semver.py +Bumps the semantic version of the input `--version`. Versions are expected in vMAJOR.MINOR.PATCH format or vMAJOR.MINOR format. + +In vMAJOR.MINOR format, the result will be patch version 0 of that version, for example `v1.2 -> v1.2.0`. + +In vMAJOR.MINOR.PATCH format, the result will be a bumped PATCH version, for example `v1.2.3 -> v1.2.4`. + +If the PATCH number contains letters, it is considered a development version, in which case, the result is the non-development version of that number. +The patch number will not be bumped, only the "-dev" or similar additional text will be removed. For example: `v1.2.6-rc1 -> v1.2.6`. + +## zip-file.py +Specialized ZIP command for release management. Special features: +1. Uses Python ZIP libaries, so the `zip` command does not need to be installed. +1. Can only zip one file. +1. Optionally gets file version, Go OS and architecture. +1. By default all inputs and output is formatted exactly how CircleCI needs it. + +By default, the command will try to ZIP the file at `build/tendermint_${GOOS}_${GOARCH}`. +This can be changed with the `--file` input parameter. + +By default, the command will output the ZIP file to `build/tendermint_${CIRCLE_TAG}_${GOOS}_${GOARCH}.zip`. +This can be changed with the `--destination` (folder), `--version`, `--goos` and `--goarch` input parameters respectively. + +## sha-files.py +Specialized `shasum` command for release management. Special features: +1. Reads all ZIP files in the given folder. +1. By default all inputs and output is formatted exactly how CircleCI needs it. + +By default, the command will look up all ZIP files in the `build/` folder. + +By default, the command will output results into the `build/SHA256SUMS` file. + +# GitHub management +Uploading build results to GitHub requires at least these steps: +1. Create a new release on GitHub with content +2. Upload all binaries to the release +3. Publish the release +The below scripts help with these steps. + +## github-draft.py +Creates a GitHub release and fills the content with the CHANGELOG.md link. The version number can be changed by the `--version` parameter. + +By default, the command will use the tendermint/tendermint organization/repo, which can be changed using the `--org` and `--repo` parameters. + +By default, the command will get the version number from the `${CIRCLE_TAG}` variable. + +Returns the GitHub release ID. + +## github-upload.py +Upload a file to a GitHub release. The release is defined by the mandatory `--id` (release ID) input parameter. + +By default, the command will upload the file `/tmp/workspace/tendermint_${CIRCLE_TAG}_${GOOS}_${GOARCH}.zip`. This can be changed by the `--file` input parameter. + +## github-publish.py +Publish a GitHub release. The release is defined by the mandatory `--id` (release ID) input parameter. + diff --git a/scripts/release_management/bump-semver.py b/scripts/release_management/bump-semver.py new file mode 100755 index 000000000..b13a10342 --- /dev/null +++ b/scripts/release_management/bump-semver.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +# Bump the release number of a semantic version number and print it. --version is required. +# Version is +# - vA.B.C, in which case vA.B.C+1 will be returned +# - vA.B.C-devorwhatnot in which case vA.B.C will be returned +# - vA.B in which case vA.B.0 will be returned + +import re +import argparse + + +def semver(ver): + if re.match('v[0-9]+\.[0-9]+',ver) is None: + ver="v0.0" + #raise argparse.ArgumentTypeError('--version must be a semantic version number with major, minor and patch numbers') + return ver + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--version", help="Version number to bump, e.g.: v1.0.0", required=True, type=semver) + args = parser.parse_args() + + found = re.match('(v[0-9]+\.[0-9]+)(\.(.+))?', args.version) + majorminorprefix = found.group(1) + patch = found.group(3) + if patch is None: + patch = "0-new" + + if re.match('[0-9]+$',patch) is None: + patchfound = re.match('([0-9]+)',patch) + patch = int(patchfound.group(1)) + else: + patch = int(patch) + 1 + + print("{0}.{1}".format(majorminorprefix, patch)) diff --git a/scripts/release_management/github-draft.py b/scripts/release_management/github-draft.py new file mode 100755 index 000000000..8c6e0eeb5 --- /dev/null +++ b/scripts/release_management/github-draft.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +# Create a draft release on GitHub. By default in the tendermint/tendermint repo. +# Optimized for CircleCI + +import argparse +import httplib +import json +import os +from base64 import b64encode + +def request(org, repo, data): + user_and_pass = b64encode(b"{0}:{1}".format(os.environ['GITHUB_USERNAME'], os.environ['GITHUB_TOKEN'])).decode("ascii") + headers = { + 'User-Agent': 'tenderbot', + 'Accept': 'application/vnd.github.v3+json', + 'Authorization': 'Basic %s' % user_and_pass + } + + conn = httplib.HTTPSConnection('api.github.com', timeout=5) + conn.request('POST', '/repos/{0}/{1}/releases'.format(org,repo), data, headers) + response = conn.getresponse() + if response.status < 200 or response.status > 299: + print("{0}: {1}".format(response.status, response.reason)) + conn.close() + raise IOError(response.reason) + responsedata = response.read() + conn.close() + return json.loads(responsedata) + + +def create_draft(org,repo,version): + draft = { + 'tag_name': version, + 'target_commitish': 'master', + 'name': '{0} (WARNING: ALPHA SOFTWARE)'.format(version), + 'body': 'https://github.com/{0}/{1}/blob/master/CHANGELOG.md#{2}'.format(org,repo,version.replace('v','').replace('.','')), + 'draft': True, + 'prerelease': False + } + data=json.dumps(draft) + return request(org, repo, data) + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--org", default="tendermint", help="GitHub organization") + parser.add_argument("--repo", default="tendermint", help="GitHub repository") + parser.add_argument("--version", default=os.environ.get('CIRCLE_TAG'), help="Version number for binary, e.g.: v1.0.0") + args = parser.parse_args() + + if not os.environ.has_key('GITHUB_USERNAME'): + raise parser.error('environment variable GITHUB_USERNAME is required') + + if not os.environ.has_key('GITHUB_TOKEN'): + raise parser.error('environment variable GITHUB_TOKEN is required') + + release = create_draft(args.org,args.repo,args.version) + + print(release["id"]) + diff --git a/scripts/release_management/github-openpr.py b/scripts/release_management/github-openpr.py new file mode 100755 index 000000000..af0434f02 --- /dev/null +++ b/scripts/release_management/github-openpr.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python + +# Open a PR against the develop branch. --branch required. +# Optimized for CircleCI + +import json +import os +import argparse +import httplib +from base64 import b64encode + + +def request(org, repo, data): + user_and_pass = b64encode(b"{0}:{1}".format(os.environ['GITHUB_USERNAME'], os.environ['GITHUB_TOKEN'])).decode("ascii") + headers = { + 'User-Agent': 'tenderbot', + 'Accept': 'application/vnd.github.v3+json', + 'Authorization': 'Basic %s' % user_and_pass + } + + conn = httplib.HTTPSConnection('api.github.com', timeout=5) + conn.request('POST', '/repos/{0}/{1}/pulls'.format(org,repo), data, headers) + response = conn.getresponse() + if response.status < 200 or response.status > 299: + print(response) + conn.close() + raise IOError(response.reason) + responsedata = response.read() + conn.close() + return json.loads(responsedata) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--org", default="tendermint", help="GitHub organization. Defaults to tendermint.") + parser.add_argument("--repo", default="tendermint", help="GitHub repository. Defaults to tendermint.") + parser.add_argument("--head", help="The name of the branch where your changes are implemented.", required=True) + parser.add_argument("--base", help="The name of the branch you want the changes pulled into.", required=True) + parser.add_argument("--title", default="Security release {0}".format(os.environ.get('CIRCLE_TAG')), help="The title of the pull request.") + args = parser.parse_args() + + if not os.environ.has_key('GITHUB_USERNAME'): + raise parser.error('GITHUB_USERNAME not set.') + + if not os.environ.has_key('GITHUB_TOKEN'): + raise parser.error('GITHUB_TOKEN not set.') + + if os.environ.get('CIRCLE_TAG') is None: + raise parser.error('CIRCLE_TAG not set.') + + result = request(args.org, args.repo, data=json.dumps({'title':"{0}".format(args.title),'head':"{0}".format(args.head),'base':"{0}".format(args.base),'body':""})) + print(result['html_url']) diff --git a/scripts/release_management/github-public-newbranch.bash b/scripts/release_management/github-public-newbranch.bash new file mode 100644 index 000000000..ca2fa1314 --- /dev/null +++ b/scripts/release_management/github-public-newbranch.bash @@ -0,0 +1,28 @@ +#!/bin/sh + +# github-public-newbranch.bash - create public branch from the security repository + +set -euo pipefail + +# Create new branch +BRANCH="${CIRCLE_TAG:-v0.0.0}-security-`date -u +%Y%m%d%H%M%S`" +# Check if the patch release exist already as a branch +if [ -n "`git branch | grep '${BRANCH}'`" ]; then + echo "WARNING: Branch ${BRANCH} already exists." +else + echo "Creating branch ${BRANCH}." + git branch "${BRANCH}" +fi + +# ... and check it out +git checkout "${BRANCH}" + +# Add entry to public repository +git remote add tendermint-origin git@github.com:tendermint/tendermint.git + +# Push branch and tag to public repository +git push tendermint-origin +git push tendermint-origin --tags + +# Create a PR from the public branch to the assumed release branch in public (release branch has to exist) +python -u scripts/release_management/github-openpr.py --head "${BRANCH}" --base "${BRANCH:%.*}" diff --git a/scripts/release_management/github-publish.py b/scripts/release_management/github-publish.py new file mode 100755 index 000000000..31071aecd --- /dev/null +++ b/scripts/release_management/github-publish.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python + +# Publish an existing GitHub draft release. --id required. +# Optimized for CircleCI + +import json +import os +import argparse +import httplib +from base64 import b64encode + + +def request(org, repo, id, data): + user_and_pass = b64encode(b"{0}:{1}".format(os.environ['GITHUB_USERNAME'], os.environ['GITHUB_TOKEN'])).decode("ascii") + headers = { + 'User-Agent': 'tenderbot', + 'Accept': 'application/vnd.github.v3+json', + 'Authorization': 'Basic %s' % user_and_pass + } + + conn = httplib.HTTPSConnection('api.github.com', timeout=5) + conn.request('POST', '/repos/{0}/{1}/releases/{2}'.format(org,repo,id), data, headers) + response = conn.getresponse() + if response.status < 200 or response.status > 299: + print(response) + conn.close() + raise IOError(response.reason) + responsedata = response.read() + conn.close() + return json.loads(responsedata) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--org", default="tendermint", help="GitHub organization") + parser.add_argument("--repo", default="tendermint", help="GitHub repository") + parser.add_argument("--id", help="GitHub release ID", required=True, type=int) + parser.add_argument("--version", default=os.environ.get('CIRCLE_TAG'), help="Version number for the release, e.g.: v1.0.0") + args = parser.parse_args() + + if not os.environ.has_key('GITHUB_USERNAME'): + raise parser.error('GITHUB_USERNAME not set.') + + if not os.environ.has_key('GITHUB_TOKEN'): + raise parser.error('GITHUB_TOKEN not set.') + + try: + result = request(args.org, args.repo, args.id, data=json.dumps({'draft':False,'tag_name':"{0}".format(args.version)})) + except IOError as e: + print(e) + result = request(args.org, args.repo, args.id, data=json.dumps({'draft':False,'tag_name':"{0}-autorelease".format(args.version)})) + + print(result['name']) diff --git a/scripts/release_management/github-upload.py b/scripts/release_management/github-upload.py new file mode 100755 index 000000000..77c76a755 --- /dev/null +++ b/scripts/release_management/github-upload.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python + +# Upload a file to a GitHub draft release. --id and --file are required. +# Optimized for CircleCI + +import json +import os +import re +import argparse +import mimetypes +import httplib +from base64 import b64encode + + +def request(baseurl, path, mimetype, mimeencoding, data): + user_and_pass = b64encode(b"{0}:{1}".format(os.environ['GITHUB_USERNAME'], os.environ['GITHUB_TOKEN'])).decode("ascii") + + headers = { + 'User-Agent': 'tenderbot', + 'Accept': 'application/vnd.github.v3.raw+json', + 'Authorization': 'Basic %s' % user_and_pass, + 'Content-Type': mimetype, + 'Content-Encoding': mimeencoding + } + + conn = httplib.HTTPSConnection(baseurl, timeout=5) + conn.request('POST', path, data, headers) + response = conn.getresponse() + if response.status < 200 or response.status > 299: + print(response) + conn.close() + raise IOError(response.reason) + responsedata = response.read() + conn.close() + return json.loads(responsedata) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--id", help="GitHub release ID", required=True, type=int) + parser.add_argument("--file", default="/tmp/workspace/tendermint_{0}_{1}_{2}.zip".format(os.environ.get('CIRCLE_TAG'),os.environ.get('GOOS'),os.environ.get('GOARCH')), help="File to upload") + parser.add_argument("--return-id-only", help="Return only the release ID after upload to GitHub.", action='store_true') + args = parser.parse_args() + + if not os.environ.has_key('GITHUB_USERNAME'): + raise parser.error('GITHUB_USERNAME not set.') + + if not os.environ.has_key('GITHUB_TOKEN'): + raise parser.error('GITHUB_TOKEN not set.') + + mimetypes.init() + filename = os.path.basename(args.file) + mimetype,mimeencoding = mimetypes.guess_type(filename, strict=False) + if mimetype is None: + mimetype = 'application/zip' + if mimeencoding is None: + mimeencoding = 'utf8' + + with open(args.file,'rb') as f: + asset = f.read() + + result = request('uploads.github.com', '/repos/tendermint/tendermint/releases/{0}/assets?name={1}'.format(args.id, filename), mimetype, mimeencoding, asset) + + if args.return_id_only: + print(result['id']) + else: + print(result['browser_download_url']) + diff --git a/scripts/release_management/sha-files.py b/scripts/release_management/sha-files.py new file mode 100755 index 000000000..2a9ee0d59 --- /dev/null +++ b/scripts/release_management/sha-files.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +# Create SHA256 summaries from all ZIP files in a folder +# Optimized for CircleCI + +import re +import os +import argparse +import zipfile +import hashlib + + +BLOCKSIZE = 65536 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--folder", default="/tmp/workspace", help="Folder to look for, for ZIP files") + parser.add_argument("--shafile", default="/tmp/workspace/SHA256SUMS", help="SHA256 summaries File") + args = parser.parse_args() + + for filename in os.listdir(args.folder): + if re.search('\.zip$',filename) is None: + continue + if not os.path.isfile(os.path.join(args.folder, filename)): + continue + with open(args.shafile,'a+') as shafile: + hasher = hashlib.sha256() + with open(os.path.join(args.folder, filename),'r') as f: + buf = f.read(BLOCKSIZE) + while len(buf) > 0: + hasher.update(buf) + buf = f.read(BLOCKSIZE) + shafile.write("{0} {1}\n".format(hasher.hexdigest(),filename)) + diff --git a/scripts/release_management/zip-file.py b/scripts/release_management/zip-file.py new file mode 100755 index 000000000..5d2f5b2c8 --- /dev/null +++ b/scripts/release_management/zip-file.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# ZIP one file as "tendermint" into a ZIP like tendermint_VERSION_OS_ARCH.zip +# Use environment variables CIRCLE_TAG, GOOS and GOARCH for easy input parameters. +# Optimized for CircleCI + +import os +import argparse +import zipfile +import hashlib + + +BLOCKSIZE = 65536 + + +def zip_asset(file,destination,arcname,version,goos,goarch): + filename = os.path.basename(file) + output = "{0}/{1}_{2}_{3}_{4}.zip".format(destination,arcname,version,goos,goarch) + + with zipfile.ZipFile(output,'w') as f: + f.write(filename=file,arcname=arcname) + f.comment=filename + return output + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--file", default="build/tendermint_{0}_{1}".format(os.environ.get('GOOS'),os.environ.get('GOARCH')), help="File to zip") + parser.add_argument("--destination", default="build", help="Destination folder for files") + parser.add_argument("--version", default=os.environ.get('CIRCLE_TAG'), help="Version number for binary, e.g.: v1.0.0") + parser.add_argument("--goos", default=os.environ.get('GOOS'), help="GOOS parameter") + parser.add_argument("--goarch", default=os.environ.get('GOARCH'), help="GOARCH parameter") + args = parser.parse_args() + + if args.version is None: + raise parser.error("argument --version is required") + if args.goos is None: + raise parser.error("argument --goos is required") + if args.goarch is None: + raise parser.error("argument --goarch is required") + + file = zip_asset(args.file,args.destination,"tendermint",args.version,args.goos,args.goarch) + print(file) + From 2233dd45bd774905b1ba545eeadea5ebe49d1e31 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Sat, 30 Mar 2019 01:47:53 +0800 Subject: [PATCH 142/259] libs: remove useless code in group (#3504) * lib: remove useless code in group * update change log * Update CHANGELOG_PENDING.md Co-Authored-By: guagualvcha --- CHANGELOG_PENDING.md | 1 + libs/autofile/group.go | 251 ------------------------------------ libs/autofile/group_test.go | 199 ---------------------------- 3 files changed, 1 insertion(+), 450 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index a1745c193..19954dd5e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,6 +9,7 @@ * Apps * Go API +- [libs/autofile] \#3504 Remove unused code in autofile package. Deleted functions: `Group.Search`, `Group.FindLast`, `GroupReader.ReadLine`, `GroupReader.PushLine`, `MakeSimpleSearchFunc` (@guagualvcha) * Blockchain Protocol diff --git a/libs/autofile/group.go b/libs/autofile/group.go index d1ea0de75..ce73466e4 100644 --- a/libs/autofile/group.go +++ b/libs/autofile/group.go @@ -331,172 +331,6 @@ func (g *Group) NewReader(index int) (*GroupReader, error) { return r, nil } -// Returns -1 if line comes after, 0 if found, 1 if line comes before. -type SearchFunc func(line string) (int, error) - -// Searches for the right file in Group, then returns a GroupReader to start -// streaming lines. -// Returns true if an exact match was found, otherwise returns the next greater -// line that starts with prefix. -// CONTRACT: Caller must close the returned GroupReader -func (g *Group) Search(prefix string, cmp SearchFunc) (*GroupReader, bool, error) { - g.mtx.Lock() - minIndex, maxIndex := g.minIndex, g.maxIndex - g.mtx.Unlock() - // Now minIndex/maxIndex may change meanwhile, - // but it shouldn't be a big deal - // (maybe we'll want to limit scanUntil though) - - for { - curIndex := (minIndex + maxIndex + 1) / 2 - - // Base case, when there's only 1 choice left. - if minIndex == maxIndex { - r, err := g.NewReader(maxIndex) - if err != nil { - return nil, false, err - } - match, err := scanUntil(r, prefix, cmp) - if err != nil { - r.Close() - return nil, false, err - } - return r, match, err - } - - // Read starting roughly at the middle file, - // until we find line that has prefix. - r, err := g.NewReader(curIndex) - if err != nil { - return nil, false, err - } - foundIndex, line, err := scanNext(r, prefix) - r.Close() - if err != nil { - return nil, false, err - } - - // Compare this line to our search query. - val, err := cmp(line) - if err != nil { - return nil, false, err - } - if val < 0 { - // Line will come later - minIndex = foundIndex - } else if val == 0 { - // Stroke of luck, found the line - r, err := g.NewReader(foundIndex) - if err != nil { - return nil, false, err - } - match, err := scanUntil(r, prefix, cmp) - if !match { - panic("Expected match to be true") - } - if err != nil { - r.Close() - return nil, false, err - } - return r, true, err - } else { - // We passed it - maxIndex = curIndex - 1 - } - } - -} - -// Scans and returns the first line that starts with 'prefix' -// Consumes line and returns it. -func scanNext(r *GroupReader, prefix string) (int, string, error) { - for { - line, err := r.ReadLine() - if err != nil { - return 0, "", err - } - if !strings.HasPrefix(line, prefix) { - continue - } - index := r.CurIndex() - return index, line, nil - } -} - -// Returns true iff an exact match was found. -// Pushes line, does not consume it. -func scanUntil(r *GroupReader, prefix string, cmp SearchFunc) (bool, error) { - for { - line, err := r.ReadLine() - if err != nil { - return false, err - } - if !strings.HasPrefix(line, prefix) { - continue - } - val, err := cmp(line) - if err != nil { - return false, err - } - if val < 0 { - continue - } else if val == 0 { - r.PushLine(line) - return true, nil - } else { - r.PushLine(line) - return false, nil - } - } -} - -// Searches backwards for the last line in Group with prefix. -// Scans each file forward until the end to find the last match. -func (g *Group) FindLast(prefix string) (match string, found bool, err error) { - g.mtx.Lock() - minIndex, maxIndex := g.minIndex, g.maxIndex - g.mtx.Unlock() - - r, err := g.NewReader(maxIndex) - if err != nil { - return "", false, err - } - defer r.Close() - - // Open files from the back and read -GROUP_LOOP: - for i := maxIndex; i >= minIndex; i-- { - err := r.SetIndex(i) - if err != nil { - return "", false, err - } - // Scan each line and test whether line matches - for { - line, err := r.ReadLine() - if err == io.EOF { - if found { - return match, found, nil - } - continue GROUP_LOOP - } else if err != nil { - return "", false, err - } - if strings.HasPrefix(line, prefix) { - match = line - found = true - } - if r.CurIndex() > i { - if found { - return match, found, nil - } - continue GROUP_LOOP - } - } - } - - return -} - // GroupInfo holds information about the group. type GroupInfo struct { MinIndex int // index of the first file in the group, including head @@ -654,48 +488,6 @@ func (gr *GroupReader) Read(p []byte) (n int, err error) { } } -// ReadLine reads a line (without delimiter). -// just return io.EOF if no new lines found. -func (gr *GroupReader) ReadLine() (string, error) { - gr.mtx.Lock() - defer gr.mtx.Unlock() - - // From PushLine - if gr.curLine != nil { - line := string(gr.curLine) - gr.curLine = nil - return line, nil - } - - // Open file if not open yet - if gr.curReader == nil { - err := gr.openFile(gr.curIndex) - if err != nil { - return "", err - } - } - - // Iterate over files until line is found - var linePrefix string - for { - bytesRead, err := gr.curReader.ReadBytes('\n') - if err == io.EOF { - // Open the next file - if err1 := gr.openFile(gr.curIndex + 1); err1 != nil { - return "", err1 - } - if len(bytesRead) > 0 && bytesRead[len(bytesRead)-1] == byte('\n') { - return linePrefix + string(bytesRead[:len(bytesRead)-1]), nil - } - linePrefix += string(bytesRead) - continue - } else if err != nil { - return "", err - } - return linePrefix + string(bytesRead[:len(bytesRead)-1]), nil - } -} - // IF index > gr.Group.maxIndex, returns io.EOF // CONTRACT: caller should hold gr.mtx func (gr *GroupReader) openFile(index int) error { @@ -725,20 +517,6 @@ func (gr *GroupReader) openFile(index int) error { return nil } -// PushLine makes the given line the current one, so the next time somebody -// calls ReadLine, this line will be returned. -// panics if called twice without calling ReadLine. -func (gr *GroupReader) PushLine(line string) { - gr.mtx.Lock() - defer gr.mtx.Unlock() - - if gr.curLine == nil { - gr.curLine = []byte(line) - } else { - panic("PushLine failed, already have line") - } -} - // CurIndex returns cursor's file index. func (gr *GroupReader) CurIndex() int { gr.mtx.Lock() @@ -753,32 +531,3 @@ func (gr *GroupReader) SetIndex(index int) error { defer gr.mtx.Unlock() return gr.openFile(index) } - -//-------------------------------------------------------------------------------- - -// A simple SearchFunc that assumes that the marker is of form -// . -// For example, if prefix is '#HEIGHT:', the markers of expected to be of the form: -// -// #HEIGHT:1 -// ... -// #HEIGHT:2 -// ... -func MakeSimpleSearchFunc(prefix string, target int) SearchFunc { - return func(line string) (int, error) { - if !strings.HasPrefix(line, prefix) { - return -1, fmt.Errorf("Marker line did not have prefix: %v", prefix) - } - i, err := strconv.Atoi(line[len(prefix):]) - if err != nil { - return -1, fmt.Errorf("Failed to parse marker line: %v", err.Error()) - } - if target < i { - return 1, nil - } else if target == i { - return 0, nil - } else { - return -1, nil - } - } -} diff --git a/libs/autofile/group_test.go b/libs/autofile/group_test.go index 68870df87..c300aba71 100644 --- a/libs/autofile/group_test.go +++ b/libs/autofile/group_test.go @@ -1,13 +1,9 @@ package autofile import ( - "errors" - "fmt" "io" "io/ioutil" "os" - "strconv" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -106,107 +102,6 @@ func TestCheckHeadSizeLimit(t *testing.T) { destroyTestGroup(t, g) } -func TestSearch(t *testing.T) { - g := createTestGroupWithHeadSizeLimit(t, 10*1000) - - // Create some files in the group that have several INFO lines in them. - // Try to put the INFO lines in various spots. - for i := 0; i < 100; i++ { - // The random junk at the end ensures that this INFO linen - // is equally likely to show up at the end. - _, err := g.Head.Write([]byte(fmt.Sprintf("INFO %v %v\n", i, cmn.RandStr(123)))) - require.NoError(t, err, "Failed to write to head") - g.checkHeadSizeLimit() - for j := 0; j < 10; j++ { - _, err1 := g.Head.Write([]byte(cmn.RandStr(123) + "\n")) - require.NoError(t, err1, "Failed to write to head") - g.checkHeadSizeLimit() - } - } - - // Create a search func that searches for line - makeSearchFunc := func(target int) SearchFunc { - return func(line string) (int, error) { - parts := strings.Split(line, " ") - if len(parts) != 3 { - return -1, errors.New("Line did not have 3 parts") - } - i, err := strconv.Atoi(parts[1]) - if err != nil { - return -1, errors.New("Failed to parse INFO: " + err.Error()) - } - if target < i { - return 1, nil - } else if target == i { - return 0, nil - } else { - return -1, nil - } - } - } - - // Now search for each number - for i := 0; i < 100; i++ { - gr, match, err := g.Search("INFO", makeSearchFunc(i)) - require.NoError(t, err, "Failed to search for line, tc #%d", i) - assert.True(t, match, "Expected Search to return exact match, tc #%d", i) - line, err := gr.ReadLine() - require.NoError(t, err, "Failed to read line after search, tc #%d", i) - if !strings.HasPrefix(line, fmt.Sprintf("INFO %v ", i)) { - t.Fatalf("Failed to get correct line, tc #%d", i) - } - // Make sure we can continue to read from there. - cur := i + 1 - for { - line, err := gr.ReadLine() - if err == io.EOF { - if cur == 99+1 { - // OK! - break - } else { - t.Fatalf("Got EOF after the wrong INFO #, tc #%d", i) - } - } else if err != nil { - t.Fatalf("Error reading line, tc #%d, err:\n%s", i, err) - } - if !strings.HasPrefix(line, "INFO ") { - continue - } - if !strings.HasPrefix(line, fmt.Sprintf("INFO %v ", cur)) { - t.Fatalf("Unexpected INFO #. Expected %v got:\n%v, tc #%d", cur, line, i) - } - cur++ - } - gr.Close() - } - - // Now search for something that is too small. - // We should get the first available line. - { - gr, match, err := g.Search("INFO", makeSearchFunc(-999)) - require.NoError(t, err, "Failed to search for line") - assert.False(t, match, "Expected Search to not return exact match") - line, err := gr.ReadLine() - require.NoError(t, err, "Failed to read line after search") - if !strings.HasPrefix(line, "INFO 0 ") { - t.Error("Failed to fetch correct line, which is the earliest INFO") - } - err = gr.Close() - require.NoError(t, err, "Failed to close GroupReader") - } - - // Now search for something that is too large. - // We should get an EOF error. - { - gr, _, err := g.Search("INFO", makeSearchFunc(999)) - assert.Equal(t, io.EOF, err) - assert.Nil(t, gr) - } - - // Cleanup - destroyTestGroup(t, g) -} - func TestRotateFile(t *testing.T) { g := createTestGroupWithHeadSizeLimit(t, 0) g.WriteLine("Line 1") @@ -237,100 +132,6 @@ func TestRotateFile(t *testing.T) { destroyTestGroup(t, g) } -func TestFindLast1(t *testing.T) { - g := createTestGroupWithHeadSizeLimit(t, 0) - - g.WriteLine("Line 1") - g.WriteLine("Line 2") - g.WriteLine("# a") - g.WriteLine("Line 3") - g.FlushAndSync() - g.RotateFile() - g.WriteLine("Line 4") - g.WriteLine("Line 5") - g.WriteLine("Line 6") - g.WriteLine("# b") - g.FlushAndSync() - - match, found, err := g.FindLast("#") - assert.NoError(t, err) - assert.True(t, found) - assert.Equal(t, "# b", match) - - // Cleanup - destroyTestGroup(t, g) -} - -func TestFindLast2(t *testing.T) { - g := createTestGroupWithHeadSizeLimit(t, 0) - - g.WriteLine("Line 1") - g.WriteLine("Line 2") - g.WriteLine("Line 3") - g.FlushAndSync() - g.RotateFile() - g.WriteLine("# a") - g.WriteLine("Line 4") - g.WriteLine("Line 5") - g.WriteLine("# b") - g.WriteLine("Line 6") - g.FlushAndSync() - - match, found, err := g.FindLast("#") - assert.NoError(t, err) - assert.True(t, found) - assert.Equal(t, "# b", match) - - // Cleanup - destroyTestGroup(t, g) -} - -func TestFindLast3(t *testing.T) { - g := createTestGroupWithHeadSizeLimit(t, 0) - - g.WriteLine("Line 1") - g.WriteLine("# a") - g.WriteLine("Line 2") - g.WriteLine("# b") - g.WriteLine("Line 3") - g.FlushAndSync() - g.RotateFile() - g.WriteLine("Line 4") - g.WriteLine("Line 5") - g.WriteLine("Line 6") - g.FlushAndSync() - - match, found, err := g.FindLast("#") - assert.NoError(t, err) - assert.True(t, found) - assert.Equal(t, "# b", match) - - // Cleanup - destroyTestGroup(t, g) -} - -func TestFindLast4(t *testing.T) { - g := createTestGroupWithHeadSizeLimit(t, 0) - - g.WriteLine("Line 1") - g.WriteLine("Line 2") - g.WriteLine("Line 3") - g.FlushAndSync() - g.RotateFile() - g.WriteLine("Line 4") - g.WriteLine("Line 5") - g.WriteLine("Line 6") - g.FlushAndSync() - - match, found, err := g.FindLast("#") - assert.NoError(t, err) - assert.False(t, found) - assert.Empty(t, match) - - // Cleanup - destroyTestGroup(t, g) -} - func TestWrite(t *testing.T) { g := createTestGroupWithHeadSizeLimit(t, 0) From 422d04c8bae80e103474d37bd5a5afd0061e8d72 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 31 Mar 2019 07:14:18 -0400 Subject: [PATCH 143/259] Bucky/mempool txsmap (#3512) * mempool: resCb -> globalCb * reqResCb takes an externalCb * failing test for #3509 * txsMap is sync.Map * update changelog --- CHANGELOG_PENDING.md | 11 +++- abci/client/socket_client.go | 21 ++++--- mempool/cache_test.go | 2 +- mempool/mempool.go | 117 +++++++++++++++++++++-------------- mempool/mempool_test.go | 50 +++++++++++++++ 5 files changed, 141 insertions(+), 60 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 19954dd5e..e0a294ac3 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,6 +1,9 @@ -## v0.32.0 +## v0.31.2 -** +*March 30th, 2019* + +This release fixes a regression from v0.31.1 where Tendermint panics under +mempool load for external ABCI apps. ### BREAKING CHANGES: @@ -19,6 +22,8 @@ ### IMPROVEMENTS: -- [CircleCI] \#3497 Move release management to CircleCI +- [circle] \#3497 Move release management to CircleCI ### BUG FIXES: + +- [mempool] \#3512 Fix panic from concurrent access to txsMap, a regression for external ABCI apps introduced in v0.31.1 diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index 562676605..3b401bd3c 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -26,16 +26,17 @@ var _ Client = (*socketClient)(nil) type socketClient struct { cmn.BaseService - reqQueue chan *ReqRes - flushTimer *cmn.ThrottleTimer + addr string mustConnect bool + conn net.Conn + + reqQueue chan *ReqRes + flushTimer *cmn.ThrottleTimer mtx sync.Mutex - addr string - conn net.Conn err error - reqSent *list.List - resCb func(*types.Request, *types.Response) // listens to all callbacks + reqSent *list.List // list of requests sent, waiting for response + resCb func(*types.Request, *types.Response) // called on all requests, if set. } @@ -86,6 +87,7 @@ func (cli *socketClient) OnStop() { cli.mtx.Lock() defer cli.mtx.Unlock() if cli.conn != nil { + // does this really need a mutex? cli.conn.Close() } @@ -207,12 +209,15 @@ func (cli *socketClient) didRecvResponse(res *types.Response) error { reqres.Done() // Release waiters cli.reqSent.Remove(next) // Pop first item from linked list - // Notify reqRes listener if set + // Notify reqRes listener if set (request specific callback). + // NOTE: it is possible this callback isn't set on the reqres object. + // at this point, in which case it will be called after, when it is set. + // TODO: should we move this after the resCb call so the order is always consistent? if cb := reqres.GetCallback(); cb != nil { cb(res) } - // Notify client listener if set + // Notify client listener if set (global callback). if cli.resCb != nil { cli.resCb(reqres.Request, res) } diff --git a/mempool/cache_test.go b/mempool/cache_test.go index 26e560b6e..ea9f63fd6 100644 --- a/mempool/cache_test.go +++ b/mempool/cache_test.go @@ -19,7 +19,7 @@ func TestCacheRemove(t *testing.T) { for i := 0; i < numTxs; i++ { // probability of collision is 2**-256 txBytes := make([]byte, 32) - rand.Read(txBytes) + rand.Read(txBytes) // nolint: gosec txs[i] = txBytes cache.Push(txBytes) // make sure its added to both the linked list and the map diff --git a/mempool/mempool.go b/mempool/mempool.go index bd3cbf7d9..a5b14466a 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -149,6 +149,11 @@ func TxID(tx []byte) string { return fmt.Sprintf("%X", types.Tx(tx).Hash()) } +// txKey is the fixed length array sha256 hash used as the key in maps. +func txKey(tx types.Tx) [sha256.Size]byte { + return sha256.Sum256(tx) +} + // Mempool is an ordered in-memory pool for transactions before they are proposed in a consensus // round. Transaction validity is checked using the CheckTx abci message before the transaction is // added to the pool. The Mempool uses a concurrent list structure for storing transactions that @@ -159,23 +164,27 @@ type Mempool struct { proxyMtx sync.Mutex proxyAppConn proxy.AppConnMempool txs *clist.CList // concurrent linked-list of good txs - // map for quick access to txs - // Used in CheckTx to record the tx sender. - txsMap map[[sha256.Size]byte]*clist.CElement - height int64 // the last block Update()'d to - rechecking int32 // for re-checking filtered txs on Update() - recheckCursor *clist.CElement // next expected response - recheckEnd *clist.CElement // re-checking stops here + preCheck PreCheckFunc + postCheck PostCheckFunc + + // Track whether we're rechecking txs. + // These are not protected by a mutex and are expected to be mutated + // in serial (ie. by abci responses which are called in serial). + recheckCursor *clist.CElement // next expected response + recheckEnd *clist.CElement // re-checking stops here + + // notify listeners (ie. consensus) when txs are available notifiedTxsAvailable bool txsAvailable chan struct{} // fires once for each height, when the mempool is not empty - preCheck PreCheckFunc - postCheck PostCheckFunc + + // Map for quick access to txs to record sender in CheckTx. + // txsMap: txKey -> CElement + txsMap sync.Map // Atomic integers - - // Used to check if the mempool size is bigger than the allowed limit. - // See TxsBytes - txsBytes int64 + height int64 // the last block Update()'d to + rechecking int32 // for re-checking filtered txs on Update() + txsBytes int64 // total size of mempool, in bytes // Keep a cache of already-seen txs. // This reduces the pressure on the proxyApp. @@ -203,7 +212,6 @@ func NewMempool( config: config, proxyAppConn: proxyAppConn, txs: clist.New(), - txsMap: make(map[[sha256.Size]byte]*clist.CElement), height: height, rechecking: 0, recheckCursor: nil, @@ -216,7 +224,7 @@ func NewMempool( } else { mempool.cache = nopTxCache{} } - proxyAppConn.SetResponseCallback(mempool.resCb) + proxyAppConn.SetResponseCallback(mempool.globalCb) for _, option := range options { option(mempool) } @@ -319,7 +327,7 @@ func (mem *Mempool) Flush() { e.DetachPrev() } - mem.txsMap = make(map[[sha256.Size]byte]*clist.CElement) + mem.txsMap = sync.Map{} _ = atomic.SwapInt64(&mem.txsBytes, 0) } @@ -380,13 +388,12 @@ func (mem *Mempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo // CACHE if !mem.cache.Push(tx) { - // record the sender - e, ok := mem.txsMap[sha256.Sum256(tx)] - // The check is needed because tx may be in cache, but not in the mempool. - // E.g. after we've committed a block, txs are removed from the mempool, - // but not from the cache. - if ok { - memTx := e.Value.(*mempoolTx) + // Record a new sender for a tx we've already seen. + // Note it's possible a tx is still in the cache but no longer in the mempool + // (eg. after committing a block, txs are removed from mempool but not cache), + // so we only record the sender for txs still in the mempool. + if e, ok := mem.txsMap.Load(txKey(tx)); ok { + memTx := e.(*clist.CElement).Value.(*mempoolTx) if _, loaded := memTx.senders.LoadOrStore(txInfo.PeerID, true); loaded { // TODO: consider punishing peer for dups, // its non-trivial since invalid txs can become valid, @@ -416,25 +423,21 @@ func (mem *Mempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo if err = mem.proxyAppConn.Error(); err != nil { return err } + reqRes := mem.proxyAppConn.CheckTxAsync(tx) - if cb != nil { - composedCallback := func(res *abci.Response) { - mem.reqResCb(tx, txInfo.PeerID)(res) - cb(res) - } - reqRes.SetCallback(composedCallback) - } else { - reqRes.SetCallback(mem.reqResCb(tx, txInfo.PeerID)) - } + reqRes.SetCallback(mem.reqResCb(tx, txInfo.PeerID, cb)) return nil } -// Global callback, which is called in the absence of the specific callback. -// -// In recheckTxs because no reqResCb (specific) callback is set, this callback -// will be called. -func (mem *Mempool) resCb(req *abci.Request, res *abci.Response) { +// Global callback that will be called after every ABCI response. +// Having a single global callback avoids needing to set a callback for each request. +// However, processing the checkTx response requires the peerID (so we can track which txs we heard from who), +// and peerID is not included in the ABCI request, so we have to set request-specific callbacks that +// include this information. If we're not in the midst of a recheck, this function will just return, +// so the request specific callback can do the work. +// When rechecking, we don't need the peerID, so the recheck callback happens here. +func (mem *Mempool) globalCb(req *abci.Request, res *abci.Response) { if mem.recheckCursor == nil { return } @@ -446,35 +449,50 @@ func (mem *Mempool) resCb(req *abci.Request, res *abci.Response) { mem.metrics.Size.Set(float64(mem.Size())) } -// Specific callback, which allows us to incorporate local information, like -// the peer that sent us this tx, so we can avoid sending it back to the same -// peer. +// Request specific callback that should be set on individual reqRes objects +// to incorporate local information when processing the response. +// This allows us to track the peer that sent us this tx, so we can avoid sending it back to them. +// NOTE: alternatively, we could include this information in the ABCI request itself. +// +// External callers of CheckTx, like the RPC, can also pass an externalCb through here that is called +// when all other response processing is complete. // // Used in CheckTxWithInfo to record PeerID who sent us the tx. -func (mem *Mempool) reqResCb(tx []byte, peerID uint16) func(res *abci.Response) { +func (mem *Mempool) reqResCb(tx []byte, peerID uint16, externalCb func(*abci.Response)) func(res *abci.Response) { return func(res *abci.Response) { if mem.recheckCursor != nil { - return + // this should never happen + panic("recheck cursor is not nil in reqResCb") } mem.resCbFirstTime(tx, peerID, res) // update metrics mem.metrics.Size.Set(float64(mem.Size())) + + // passed in by the caller of CheckTx, eg. the RPC + if externalCb != nil { + externalCb(res) + } } } +// Called from: +// - resCbFirstTime (lock not held) if tx is valid func (mem *Mempool) addTx(memTx *mempoolTx) { e := mem.txs.PushBack(memTx) - mem.txsMap[sha256.Sum256(memTx.tx)] = e + mem.txsMap.Store(txKey(memTx.tx), e) atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx))) mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx))) } +// Called from: +// - Update (lock held) if tx was committed +// - resCbRecheck (lock not held) if tx was invalidated func (mem *Mempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) { mem.txs.Remove(elem) elem.DetachPrev() - delete(mem.txsMap, sha256.Sum256(tx)) + mem.txsMap.Delete(txKey(tx)) atomic.AddInt64(&mem.txsBytes, int64(-len(tx))) if removeFromCache { @@ -733,7 +751,7 @@ func (mem *Mempool) recheckTxs(txs []types.Tx) { mem.recheckEnd = mem.txs.Back() // Push txs to proxyAppConn - // NOTE: reqResCb may be called concurrently. + // NOTE: globalCb may be called concurrently. for _, tx := range txs { mem.proxyAppConn.CheckTxAsync(tx) } @@ -746,8 +764,11 @@ func (mem *Mempool) recheckTxs(txs []types.Tx) { type mempoolTx struct { height int64 // height that this tx had been validated in gasWanted int64 // amount of gas this tx states it will require - senders sync.Map // ids of peers who've sent us this tx (as a map for quick lookups) tx types.Tx // + + // ids of peers who've sent us this tx (as a map for quick lookups). + // senders: PeerID -> bool + senders sync.Map } // Height returns the height for this transaction @@ -798,7 +819,7 @@ func (cache *mapTxCache) Push(tx types.Tx) bool { defer cache.mtx.Unlock() // Use the tx hash in the cache - txHash := sha256.Sum256(tx) + txHash := txKey(tx) if moved, exists := cache.map_[txHash]; exists { cache.list.MoveToBack(moved) return false @@ -820,7 +841,7 @@ func (cache *mapTxCache) Push(tx types.Tx) bool { // Remove removes the given tx from the cache. func (cache *mapTxCache) Remove(tx types.Tx) { cache.mtx.Lock() - txHash := sha256.Sum256(tx) + txHash := txKey(tx) popped := cache.map_[txHash] delete(cache.map_, txHash) if popped != nil { diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index dc7d595af..d5f25396d 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "fmt" "io/ioutil" + mrand "math/rand" "os" "path/filepath" "testing" @@ -18,6 +19,7 @@ import ( "github.com/tendermint/tendermint/abci/example/counter" "github.com/tendermint/tendermint/abci/example/kvstore" + abciserver "github.com/tendermint/tendermint/abci/server" abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" @@ -510,6 +512,54 @@ func TestMempoolTxsBytes(t *testing.T) { assert.EqualValues(t, 0, mempool.TxsBytes()) } +// This will non-deterministically catch some concurrency failures like +// https://github.com/tendermint/tendermint/issues/3509 +// TODO: all of the tests should probably also run using the remote proxy app +// since otherwise we're not actually testing the concurrency of the mempool here! +func TestMempoolRemoteAppConcurrency(t *testing.T) { + sockPath := fmt.Sprintf("unix:///tmp/echo_%v.sock", cmn.RandStr(6)) + app := kvstore.NewKVStoreApplication() + cc, server := newRemoteApp(t, sockPath, app) + defer server.Stop() + config := cfg.ResetTestRoot("mempool_test") + mempool, cleanup := newMempoolWithAppAndConfig(cc, config) + defer cleanup() + + // generate small number of txs + nTxs := 10 + txLen := 200 + txs := make([]types.Tx, nTxs) + for i := 0; i < nTxs; i++ { + txs[i] = cmn.RandBytes(txLen) + } + + // simulate a group of peers sending them over and over + N := config.Mempool.Size + maxPeers := 5 + for i := 0; i < N; i++ { + peerID := mrand.Intn(maxPeers) + txNum := mrand.Intn(nTxs) + tx := txs[int(txNum)] + + // this will err with ErrTxInCache many times ... + mempool.CheckTxWithInfo(tx, nil, TxInfo{PeerID: uint16(peerID)}) + } + err := mempool.FlushAppConn() + require.NoError(t, err) +} + +// caller must close server +func newRemoteApp(t *testing.T, addr string, app abci.Application) (clientCreator proxy.ClientCreator, server cmn.Service) { + clientCreator = proxy.NewRemoteClientCreator(addr, "socket", true) + + // Start server + server = abciserver.NewSocketServer(addr, app) + server.SetLogger(log.TestingLogger().With("module", "abci-server")) + if err := server.Start(); err != nil { + t.Fatalf("Error starting socket server: %v", err.Error()) + } + return clientCreator, server +} func checksumIt(data []byte) string { h := sha256.New() h.Write(data) From 0ae41cc663014454184c2d31328b53dd45dd34d1 Mon Sep 17 00:00:00 2001 From: Greg Szabo <16846635+greg-szabo@users.noreply.github.com> Date: Mon, 1 Apr 2019 11:47:00 -0400 Subject: [PATCH 144/259] Fix for wrong version tag (#3517) * Fix for wrong version tag (tag on the release branch instead of master) --- scripts/release_management/github-draft.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/release_management/github-draft.py b/scripts/release_management/github-draft.py index 8c6e0eeb5..1fccd38b9 100755 --- a/scripts/release_management/github-draft.py +++ b/scripts/release_management/github-draft.py @@ -29,10 +29,10 @@ def request(org, repo, data): return json.loads(responsedata) -def create_draft(org,repo,version): +def create_draft(org,repo,branch,version): draft = { 'tag_name': version, - 'target_commitish': 'master', + 'target_commitish': '{0}'.format(branch), 'name': '{0} (WARNING: ALPHA SOFTWARE)'.format(version), 'body': 'https://github.com/{0}/{1}/blob/master/CHANGELOG.md#{2}'.format(org,repo,version.replace('v','').replace('.','')), 'draft': True, @@ -45,6 +45,7 @@ if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--org", default="tendermint", help="GitHub organization") parser.add_argument("--repo", default="tendermint", help="GitHub repository") + parser.add_argument("--branch", default=os.environ.get('CIRCLE_BRANCH'), help="Branch to build from, e.g.: v1.0") parser.add_argument("--version", default=os.environ.get('CIRCLE_TAG'), help="Version number for binary, e.g.: v1.0.0") args = parser.parse_args() @@ -54,7 +55,7 @@ if __name__ == "__main__": if not os.environ.has_key('GITHUB_TOKEN'): raise parser.error('environment variable GITHUB_TOKEN is required') - release = create_draft(args.org,args.repo,args.version) + release = create_draft(args.org,args.repo,args.branch,args.version) print(release["id"]) From ab24925c9432e9eff06b34445b1760217436f87d Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Mon, 1 Apr 2019 12:32:37 +0200 Subject: [PATCH 145/259] prepare changelog and bump versions to v0.31.2 --- CHANGELOG.md | 33 +++++++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 9 +-------- version/version.go | 2 +- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ee14c51..e5ac75e9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,38 @@ # Changelog +## v0.31.2 + +*March 30th, 2019* + +This release fixes a regression from v0.31.1 where Tendermint panics under +mempool load for external ABCI apps. + +Special thanks to external contributors on this release: +@guagualvcha + +### BREAKING CHANGES: + +* CLI/RPC/Config + +* Apps + +* Go API +- [libs/autofile] [\#3504](https://github.com/tendermint/tendermint/issues/3504) Remove unused code in autofile package. Deleted functions: `Group.Search`, `Group.FindLast`, `GroupReader.ReadLine`, `GroupReader.PushLine`, `MakeSimpleSearchFunc` (@guagualvcha) + +* Blockchain Protocol + +* P2P Protocol + +### FEATURES: + +### IMPROVEMENTS: + +- [circle] [\#3497](https://github.com/tendermint/tendermint/issues/3497) Move release management to CircleCI + +### BUG FIXES: + +- [mempool] [\#3512](https://github.com/tendermint/tendermint/issues/3512) Fix panic from concurrent access to txsMap, a regression for external ABCI apps introduced in v0.31.1 + ## v0.31.1 *March 27th, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index e0a294ac3..045247937 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,9 +1,6 @@ ## v0.31.2 -*March 30th, 2019* - -This release fixes a regression from v0.31.1 where Tendermint panics under -mempool load for external ABCI apps. +** ### BREAKING CHANGES: @@ -12,7 +9,6 @@ mempool load for external ABCI apps. * Apps * Go API -- [libs/autofile] \#3504 Remove unused code in autofile package. Deleted functions: `Group.Search`, `Group.FindLast`, `GroupReader.ReadLine`, `GroupReader.PushLine`, `MakeSimpleSearchFunc` (@guagualvcha) * Blockchain Protocol @@ -22,8 +18,5 @@ mempool load for external ABCI apps. ### IMPROVEMENTS: -- [circle] \#3497 Move release management to CircleCI - ### BUG FIXES: -- [mempool] \#3512 Fix panic from concurrent access to txsMap, a regression for external ABCI apps introduced in v0.31.1 diff --git a/version/version.go b/version/version.go index 9090fc7e6..ac52c4ec1 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.1" + TMCoreSemVer = "0.31.2" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From e4a03f249dcb7d2e833fc20add382b2a5e7f167e Mon Sep 17 00:00:00 2001 From: Greg Szabo <16846635+greg-szabo@users.noreply.github.com> Date: Mon, 1 Apr 2019 14:18:18 -0400 Subject: [PATCH 146/259] Release message changelog link fix (#3519) --- scripts/release_management/github-draft.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/release_management/github-draft.py b/scripts/release_management/github-draft.py index 1fccd38b9..8a189d53e 100755 --- a/scripts/release_management/github-draft.py +++ b/scripts/release_management/github-draft.py @@ -34,7 +34,7 @@ def create_draft(org,repo,branch,version): 'tag_name': version, 'target_commitish': '{0}'.format(branch), 'name': '{0} (WARNING: ALPHA SOFTWARE)'.format(version), - 'body': 'https://github.com/{0}/{1}/blob/master/CHANGELOG.md#{2}'.format(org,repo,version.replace('v','').replace('.','')), + 'body': 'https://github.com/{0}/{1}/blob/{2}/CHANGELOG.md#{3}'.format(org,repo,branch,version.replace('.','')), 'draft': True, 'prerelease': False } From 1ecf8148385692da971a46e14091270b23c6dff3 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 1 Apr 2019 19:45:57 -0400 Subject: [PATCH 147/259] Fixes tendermint/tendermint#3439 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * make sure we create valid private keys: - genPrivKey samples and rejects invalid fieldelems (like libsecp256k1) - GenPrivKeySecp256k1 uses `(sha(secret) mod (n − 1)) + 1` - fix typo, rename test file: s/secpk256k1/secp256k1/ * Update crypto/secp256k1/secp256k1.go --- crypto/secp256k1/secp256k1.go | 52 +++++++++++++++---- crypto/secp256k1/secp256k1_cgo_test.go | 39 ++++++++++++++ crypto/secp256k1/secp256k1_internal_test.go | 45 ++++++++++++++++ crypto/secp256k1/secp256k1_nocgo_test.go | 1 - .../{secpk256k1_test.go => secp256k1_test.go} | 26 ++++++++++ 5 files changed, 151 insertions(+), 12 deletions(-) create mode 100644 crypto/secp256k1/secp256k1_cgo_test.go create mode 100644 crypto/secp256k1/secp256k1_internal_test.go rename crypto/secp256k1/{secpk256k1_test.go => secp256k1_test.go} (74%) diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index 78857c45c..fc64a0b0f 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -6,6 +6,7 @@ import ( "crypto/subtle" "fmt" "io" + "math/big" "golang.org/x/crypto/ripemd160" @@ -65,32 +66,61 @@ func (privKey PrivKeySecp256k1) Equals(other crypto.PrivKey) bool { } // GenPrivKey generates a new ECDSA private key on curve secp256k1 private key. -// It uses OS randomness in conjunction with the current global random seed -// in tendermint/libs/common to generate the private key. +// It uses OS randomness to generate the private key. func GenPrivKey() PrivKeySecp256k1 { return genPrivKey(crypto.CReader()) } // genPrivKey generates a new secp256k1 private key using the provided reader. func genPrivKey(rand io.Reader) PrivKeySecp256k1 { - privKeyBytes := [32]byte{} - _, err := io.ReadFull(rand, privKeyBytes[:]) - if err != nil { - panic(err) + var privKeyBytes [32]byte + d := new(big.Int) + for { + privKeyBytes = [32]byte{} + _, err := io.ReadFull(rand, privKeyBytes[:]) + if err != nil { + panic(err) + } + + d.SetBytes(privKeyBytes[:]) + // break if we found a valid point (i.e. > 0 and < N == curverOrder) + isValidFieldElement := 0 < d.Sign() && d.Cmp(secp256k1.S256().N) < 0 + if isValidFieldElement { + break + } } - // crypto.CRandBytes is guaranteed to be 32 bytes long, so it can be - // casted to PrivKeySecp256k1. + return PrivKeySecp256k1(privKeyBytes) } +var one = new(big.Int).SetInt64(1) + // GenPrivKeySecp256k1 hashes the secret with SHA2, and uses // that 32 byte output to create the private key. +// +// It makes sure the private key is a valid field element by setting: +// +// c = sha256(secret) +// k = (c mod (n − 1)) + 1, where n = curve order. +// // NOTE: secret should be the output of a KDF like bcrypt, // if it's derived from user input. func GenPrivKeySecp256k1(secret []byte) PrivKeySecp256k1 { - privKey32 := sha256.Sum256(secret) - // sha256.Sum256() is guaranteed to be 32 bytes long, so it can be - // casted to PrivKeySecp256k1. + secHash := sha256.Sum256(secret) + // to guarantee that we have a valid field element, we use the approach of: + // "Suite B Implementer’s Guide to FIPS 186-3", A.2.1 + // https://apps.nsa.gov/iaarchive/library/ia-guidance/ia-solutions-for-classified/algorithm-guidance/suite-b-implementers-guide-to-fips-186-3-ecdsa.cfm + // see also https://github.com/golang/go/blob/0380c9ad38843d523d9c9804fe300cb7edd7cd3c/src/crypto/ecdsa/ecdsa.go#L89-L101 + fe := new(big.Int).SetBytes(secHash[:]) + n := new(big.Int).Sub(secp256k1.S256().N, one) + fe.Mod(fe, n) + fe.Add(fe, one) + + feB := fe.Bytes() + var privKey32 [32]byte + // copy feB over to fixed 32 byte privKey32 and pad (if necessary) + copy(privKey32[32-len(feB):32], feB) + return PrivKeySecp256k1(privKey32) } diff --git a/crypto/secp256k1/secp256k1_cgo_test.go b/crypto/secp256k1/secp256k1_cgo_test.go new file mode 100644 index 000000000..edb207b53 --- /dev/null +++ b/crypto/secp256k1/secp256k1_cgo_test.go @@ -0,0 +1,39 @@ +// +build libsecp256k1 + +package secp256k1 + +import ( + "github.com/magiconair/properties/assert" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPrivKeySecp256k1SignVerify(t *testing.T) { + msg := []byte("A.1.2 ECC Key Pair Generation by Testing Candidates") + priv := GenPrivKey() + tests := []struct { + name string + privKey PrivKeySecp256k1 + wantSignErr bool + wantVerifyPasses bool + }{ + {name: "valid sign-verify round", privKey: priv, wantSignErr: false, wantVerifyPasses: true}, + {name: "invalid private key", privKey: [32]byte{}, wantSignErr: true, wantVerifyPasses: false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.privKey.Sign(msg) + if tt.wantSignErr { + require.Error(t, err) + t.Logf("Got error: %s", err) + return + } + require.NoError(t, err) + require.NotNil(t, got) + + pub := tt.privKey.PubKey() + assert.Equal(t, tt.wantVerifyPasses, pub.VerifyBytes(msg, got)) + }) + } +} diff --git a/crypto/secp256k1/secp256k1_internal_test.go b/crypto/secp256k1/secp256k1_internal_test.go new file mode 100644 index 000000000..305f12020 --- /dev/null +++ b/crypto/secp256k1/secp256k1_internal_test.go @@ -0,0 +1,45 @@ +package secp256k1 + +import ( + "bytes" + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + underlyingSecp256k1 "github.com/btcsuite/btcd/btcec" +) + +func Test_genPrivKey(t *testing.T) { + + empty := make([]byte, 32) + oneB := big.NewInt(1).Bytes() + onePadded := make([]byte, 32) + copy(onePadded[32-len(oneB):32], oneB) + t.Logf("one padded: %v, len=%v", onePadded, len(onePadded)) + + validOne := append(empty, onePadded...) + tests := []struct { + name string + notSoRand []byte + shouldPanic bool + }{ + {"empty bytes (panics because 1st 32 bytes are zero and 0 is not a valid field element)", empty, true}, + {"curve order: N", underlyingSecp256k1.S256().N.Bytes(), true}, + {"valid because 0 < 1 < N", validOne, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.shouldPanic { + require.Panics(t, func() { + genPrivKey(bytes.NewReader(tt.notSoRand)) + }) + return + } + got := genPrivKey(bytes.NewReader(tt.notSoRand)) + fe := new(big.Int).SetBytes(got[:]) + require.True(t, fe.Cmp(underlyingSecp256k1.S256().N) < 0) + require.True(t, fe.Sign() > 0) + }) + } +} diff --git a/crypto/secp256k1/secp256k1_nocgo_test.go b/crypto/secp256k1/secp256k1_nocgo_test.go index a06a0e3d1..17cb75815 100644 --- a/crypto/secp256k1/secp256k1_nocgo_test.go +++ b/crypto/secp256k1/secp256k1_nocgo_test.go @@ -6,7 +6,6 @@ import ( "testing" secp256k1 "github.com/btcsuite/btcd/btcec" - "github.com/stretchr/testify/require" ) diff --git a/crypto/secp256k1/secpk256k1_test.go b/crypto/secp256k1/secp256k1_test.go similarity index 74% rename from crypto/secp256k1/secpk256k1_test.go rename to crypto/secp256k1/secp256k1_test.go index 0f0b5adce..2488b5399 100644 --- a/crypto/secp256k1/secpk256k1_test.go +++ b/crypto/secp256k1/secp256k1_test.go @@ -2,6 +2,7 @@ package secp256k1_test import ( "encoding/hex" + "math/big" "testing" "github.com/btcsuite/btcutil/base58" @@ -84,3 +85,28 @@ func TestSecp256k1LoadPrivkeyAndSerializeIsIdentity(t *testing.T) { require.Equal(t, privKeyBytes[:], serializedBytes) } } + +func TestGenPrivKeySecp256k1(t *testing.T) { + // curve oder N + N := underlyingSecp256k1.S256().N + tests := []struct { + name string + secret []byte + }{ + {"empty secret", []byte{}}, + {"some long secret", []byte("We live in a society exquisitely dependent on science and technology, in which hardly anyone knows anything about science and technology.")}, + {"another seed used in cosmos tests #1", []byte{0}}, + {"another seed used in cosmos tests #2", []byte("mySecret")}, + {"another seed used in cosmos tests #3", []byte("")}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotPrivKey := secp256k1.GenPrivKeySecp256k1(tt.secret) + require.NotNil(t, gotPrivKey) + // interpret as a big.Int and make sure it is a valid field element: + fe := new(big.Int).SetBytes(gotPrivKey[:]) + require.True(t, fe.Cmp(N) < 0) + require.True(t, fe.Sign() > 0) + }) + } +} From 882622ec10ba3fdc9f360da65bfb2bbbd13193ed Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 1 Apr 2019 19:59:57 -0400 Subject: [PATCH 148/259] Fixes tendermint/tendermint#3522 * OriginalAddr -> SocketAddr OriginalAddr records the originally dialed address for outbound peers, rather than the peer's self reported address. For inbound peers, it was nil. Here, we rename it to SocketAddr and for inbound peers, set it to the RemoteAddr of the connection. * use SocketAddr Numerous places in the code call peer.NodeInfo().NetAddress(). However, this call to NetAddress() may perform a DNS lookup if the reported NodeInfo.ListenAddr includes a name. Failure of this lookup returns a nil address, which can lead to panics in the code. Instead, call peer.SocketAddr() to return the static address of the connection. * remove nodeInfo.NetAddress() Expose `transport.NetAddress()`, a static result determined when the transport is created. Removing NetAddress() from the nodeInfo prevents accidental DNS lookups. * fixes from review * linter * fixes from review --- node/node.go | 2 +- p2p/mock/peer.go | 8 ++--- p2p/node_info.go | 10 ++---- p2p/peer.go | 27 ++++++++-------- p2p/peer_set_test.go | 2 +- p2p/peer_test.go | 14 +++++---- p2p/pex/pex_reactor.go | 4 +-- p2p/pex/pex_reactor_test.go | 27 ++++++++-------- p2p/switch.go | 22 ++++++------- p2p/switch_test.go | 12 +++---- p2p/test_util.go | 19 ++++++------ p2p/transport.go | 25 ++++++++++++--- p2p/transport_test.go | 62 ++++++++++++++++++++----------------- rpc/core/consensus.go | 2 +- 14 files changed, 127 insertions(+), 109 deletions(-) diff --git a/node/node.go b/node/node.go index 3501b6a7a..e91d36357 100644 --- a/node/node.go +++ b/node/node.go @@ -489,7 +489,7 @@ func NewNode(config *cfg.Config, addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) // Add ourselves to addrbook to prevent dialing ourselves - addrBook.AddOurAddress(nodeInfo.NetAddress()) + addrBook.AddOurAddress(sw.NetAddress()) addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) if config.P2P.PexReactor { diff --git a/p2p/mock/peer.go b/p2p/mock/peer.go index 5ee81f67e..bdcf012de 100644 --- a/p2p/mock/peer.go +++ b/p2p/mock/peer.go @@ -62,7 +62,7 @@ func (mp *Peer) Get(key string) interface{} { func (mp *Peer) Set(key string, value interface{}) { mp.kv[key] = value } -func (mp *Peer) RemoteIP() net.IP { return mp.ip } -func (mp *Peer) OriginalAddr() *p2p.NetAddress { return mp.addr } -func (mp *Peer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} } -func (mp *Peer) CloseConn() error { return nil } +func (mp *Peer) RemoteIP() net.IP { return mp.ip } +func (mp *Peer) SocketAddr() *p2p.NetAddress { return mp.addr } +func (mp *Peer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} } +func (mp *Peer) CloseConn() error { return nil } diff --git a/p2p/node_info.go b/p2p/node_info.go index 699fd7f1e..e80f1e1b7 100644 --- a/p2p/node_info.go +++ b/p2p/node_info.go @@ -23,14 +23,8 @@ func MaxNodeInfoSize() int { // NodeInfo exposes basic info of a node // and determines if we're compatible. type NodeInfo interface { - nodeInfoAddress - nodeInfoTransport -} - -// nodeInfoAddress exposes just the core info of a node. -type nodeInfoAddress interface { ID() ID - NetAddress() *NetAddress + nodeInfoTransport } // nodeInfoTransport validates a nodeInfo and checks @@ -221,7 +215,7 @@ func (info DefaultNodeInfo) NetAddress() *NetAddress { if err != nil { switch err.(type) { case ErrNetAddressLookup: - // XXX If the peer provided a host name and the lookup fails here + // XXX If the peer provided a host name and the lookup fails here // we're out of luck. // TODO: use a NetAddress in DefaultNodeInfo default: diff --git a/p2p/peer.go b/p2p/peer.go index 73332a2aa..fab3b42d4 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -29,7 +29,7 @@ type Peer interface { NodeInfo() NodeInfo // peer's info Status() tmconn.ConnectionStatus - OriginalAddr() *NetAddress // original address for outbound peers + SocketAddr() *NetAddress // actual address of the socket Send(byte, []byte) bool TrySend(byte, []byte) bool @@ -46,7 +46,7 @@ type peerConn struct { persistent bool conn net.Conn // source connection - originalAddr *NetAddress // nil for inbound connections + socketAddr *NetAddress // cached RemoteIP() ip net.IP @@ -55,14 +55,14 @@ type peerConn struct { func newPeerConn( outbound, persistent bool, conn net.Conn, - originalAddr *NetAddress, + socketAddr *NetAddress, ) peerConn { return peerConn{ - outbound: outbound, - persistent: persistent, - conn: conn, - originalAddr: originalAddr, + outbound: outbound, + persistent: persistent, + conn: conn, + socketAddr: socketAddr, } } @@ -223,13 +223,12 @@ func (p *peer) NodeInfo() NodeInfo { return p.nodeInfo } -// OriginalAddr returns the original address, which was used to connect with -// the peer. Returns nil for inbound peers. -func (p *peer) OriginalAddr() *NetAddress { - if p.peerConn.outbound { - return p.peerConn.originalAddr - } - return nil +// SocketAddr returns the address of the socket. +// For outbound peers, it's the address dialed (after DNS resolution). +// For inbound peers, it's the address returned by the underlying connection +// (not what's reported in the peer's NodeInfo). +func (p *peer) SocketAddr() *NetAddress { + return p.peerConn.socketAddr } // Status returns the peer's ConnectionStatus. diff --git a/p2p/peer_set_test.go b/p2p/peer_set_test.go index 1d2372fb0..4bacb07d0 100644 --- a/p2p/peer_set_test.go +++ b/p2p/peer_set_test.go @@ -29,7 +29,7 @@ func (mp *mockPeer) IsPersistent() bool { return true } func (mp *mockPeer) Get(s string) interface{} { return s } func (mp *mockPeer) Set(string, interface{}) {} func (mp *mockPeer) RemoteIP() net.IP { return mp.ip } -func (mp *mockPeer) OriginalAddr() *NetAddress { return nil } +func (mp *mockPeer) SocketAddr() *NetAddress { return nil } func (mp *mockPeer) RemoteAddr() net.Addr { return &net.TCPAddr{IP: mp.ip, Port: 8800} } func (mp *mockPeer) CloseConn() error { return nil } diff --git a/p2p/peer_test.go b/p2p/peer_test.go index 90be31131..bf61beb4f 100644 --- a/p2p/peer_test.go +++ b/p2p/peer_test.go @@ -109,25 +109,27 @@ func testOutboundPeerConn( persistent bool, ourNodePrivKey crypto.PrivKey, ) (peerConn, error) { + + var pc peerConn conn, err := testDial(addr, config) if err != nil { - return peerConn{}, cmn.ErrorWrap(err, "Error creating peer") + return pc, cmn.ErrorWrap(err, "Error creating peer") } - pc, err := testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr) + pc, err = testPeerConn(conn, config, true, persistent, ourNodePrivKey, addr) if err != nil { if cerr := conn.Close(); cerr != nil { - return peerConn{}, cmn.ErrorWrap(err, cerr.Error()) + return pc, cmn.ErrorWrap(err, cerr.Error()) } - return peerConn{}, err + return pc, err } // ensure dialed ID matches connection ID if addr.ID != pc.ID() { if cerr := conn.Close(); cerr != nil { - return peerConn{}, cmn.ErrorWrap(err, cerr.Error()) + return pc, cmn.ErrorWrap(err, cerr.Error()) } - return peerConn{}, ErrSwitchAuthenticationFailure{addr, pc.ID()} + return pc, ErrSwitchAuthenticationFailure{addr, pc.ID()} } return pc, nil diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 01d1d8db5..0ce116326 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -167,7 +167,7 @@ func (r *PEXReactor) AddPeer(p Peer) { } } else { // inbound peer is its own source - addr := p.NodeInfo().NetAddress() + addr := p.SocketAddr() src := addr // add to book. dont RequestAddrs right away because @@ -309,7 +309,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { } r.requestsSent.Delete(id) - srcAddr := src.NodeInfo().NetAddress() + srcAddr := src.SocketAddr() for _, netAddr := range addrs { // Validate netAddr. Disconnect from a peer if it sends us invalid data. if netAddr == nil { diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 9e23058a5..4a6118c63 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -96,7 +96,7 @@ func TestPEXReactorRunning(t *testing.T) { } addOtherNodeAddrToAddrBook := func(switchIndex, otherSwitchIndex int) { - addr := switches[otherSwitchIndex].NodeInfo().NetAddress() + addr := switches[otherSwitchIndex].NetAddress() books[switchIndex].AddAddress(addr, addr) } @@ -127,7 +127,7 @@ func TestPEXReactorReceive(t *testing.T) { r.RequestAddrs(peer) size := book.Size() - addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*p2p.NetAddress{peer.SocketAddr()} msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs}) r.Receive(PexChannel, peer, msg) assert.Equal(t, size+1, book.Size()) @@ -184,7 +184,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { assert.True(t, r.requestsSent.Has(id)) assert.True(t, sw.Peers().Has(peer.ID())) - addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*p2p.NetAddress{peer.SocketAddr()} msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs}) // receive some addrs. should clear the request @@ -229,7 +229,7 @@ func TestCheckSeeds(t *testing.T) { badPeerConfig = &PEXReactorConfig{ Seeds: []string{"ed3dfd27bfc4af18f67a49862f04cc100696e84d@bad.network.addr:26657", "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657", - seed.NodeInfo().NetAddress().String()}, + seed.NetAddress().String()}, } peer = testCreatePeerWithConfig(dir, 2, badPeerConfig) require.Nil(t, peer.Start()) @@ -263,12 +263,13 @@ func TestConnectionSpeedForPeerReceivedFromSeed(t *testing.T) { defer os.RemoveAll(dir) // nolint: errcheck // 1. create peer - peer := testCreateDefaultPeer(dir, 1) - require.Nil(t, peer.Start()) - defer peer.Stop() + peerSwitch := testCreateDefaultPeer(dir, 1) + require.Nil(t, peerSwitch.Start()) + defer peerSwitch.Stop() // 2. Create seed which knows about the peer - seed := testCreateSeed(dir, 2, []*p2p.NetAddress{peer.NodeInfo().NetAddress()}, []*p2p.NetAddress{peer.NodeInfo().NetAddress()}) + peerAddr := peerSwitch.NetAddress() + seed := testCreateSeed(dir, 2, []*p2p.NetAddress{peerAddr}, []*p2p.NetAddress{peerAddr}) require.Nil(t, seed.Start()) defer seed.Stop() @@ -295,7 +296,7 @@ func TestPEXReactorCrawlStatus(t *testing.T) { // Create a peer, add it to the peer set and the addrbook. peer := p2p.CreateRandomPeer(false) p2p.AddPeerToSwitch(pexR.Switch, peer) - addr1 := peer.NodeInfo().NetAddress() + addr1 := peer.SocketAddr() pexR.book.AddAddress(addr1, addr1) // Add a non-connected address to the book. @@ -359,7 +360,7 @@ func TestPEXReactorSeedModeFlushStop(t *testing.T) { reactor := switches[0].Reactors()["pex"].(*PEXReactor) peerID := switches[1].NodeInfo().ID() - err = switches[1].DialPeerWithAddress(switches[0].NodeInfo().NetAddress(), false) + err = switches[1].DialPeerWithAddress(switches[0].NetAddress(), false) assert.NoError(t, err) // sleep up to a second while waiting for the peer to send us a message. @@ -397,7 +398,7 @@ func TestPEXReactorDoesNotAddPrivatePeersToAddrBook(t *testing.T) { pexR.RequestAddrs(peer) size := book.Size() - addrs := []*p2p.NetAddress{peer.NodeInfo().NetAddress()} + addrs := []*p2p.NetAddress{peer.SocketAddr()} msg := cdc.MustMarshalBinaryBare(&pexAddrsMessage{Addrs: addrs}) pexR.Receive(PexChannel, peer, msg) assert.Equal(t, size, book.Size()) @@ -414,7 +415,7 @@ func TestPEXReactorDialPeer(t *testing.T) { sw.SetAddrBook(book) peer := mock.NewPeer(nil) - addr := peer.NodeInfo().NetAddress() + addr := peer.SocketAddr() assert.Equal(t, 0, pexR.AttemptsToDial(addr)) @@ -547,7 +548,7 @@ func testCreateSeed(dir string, id int, knownAddrs, srcAddrs []*p2p.NetAddress) // Starting and stopping the peer is left to the caller func testCreatePeerWithSeed(dir string, id int, seed *p2p.Switch) *p2p.Switch { conf := &PEXReactorConfig{ - Seeds: []string{seed.NodeInfo().NetAddress().String()}, + Seeds: []string{seed.NetAddress().String()}, } return testCreatePeerWithConfig(dir, id, conf) } diff --git a/p2p/switch.go b/p2p/switch.go index 9e04fe7ce..76da9ad0c 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -86,6 +86,12 @@ type Switch struct { metrics *Metrics } +// NetAddress returns the address the switch is listening on. +func (sw *Switch) NetAddress() *NetAddress { + addr := sw.transport.NetAddress() + return &addr +} + // SwitchOption sets an optional parameter on the Switch. type SwitchOption func(*Switch) @@ -289,13 +295,7 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) { sw.stopAndRemovePeer(peer, reason) if peer.IsPersistent() { - addr := peer.OriginalAddr() - if addr == nil { - // FIXME: persistent peers can't be inbound right now. - // self-reported address for inbound persistent peers - addr = peer.NodeInfo().NetAddress() - } - go sw.reconnectToPeer(addr) + go sw.reconnectToPeer(peer.SocketAddr()) } } @@ -383,7 +383,7 @@ func (sw *Switch) SetAddrBook(addrBook AddrBook) { // like contributed to consensus. func (sw *Switch) MarkPeerAsGood(peer Peer) { if sw.addrBook != nil { - sw.addrBook.MarkGood(peer.NodeInfo().NetAddress()) + sw.addrBook.MarkGood(peer.SocketAddr()) } } @@ -400,7 +400,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b sw.Logger.Error("Error in peer's address", "err", err) } - ourAddr := sw.nodeInfo.NetAddress() + ourAddr := sw.NetAddress() // TODO: this code feels like it's in the wrong place. // The integration tests depend on the addrBook being saved @@ -536,7 +536,7 @@ func (sw *Switch) acceptRoutine() { if in >= sw.config.MaxNumInboundPeers { sw.Logger.Info( "Ignoring inbound connection: already have enough inbound peers", - "address", p.NodeInfo().NetAddress().String(), + "address", p.SocketAddr(), "have", in, "max", sw.config.MaxNumInboundPeers, ) @@ -653,7 +653,7 @@ func (sw *Switch) addPeer(p Peer) error { return err } - p.SetLogger(sw.Logger.With("peer", p.NodeInfo().NetAddress())) + p.SetLogger(sw.Logger.With("peer", p.SocketAddr())) // Handle the shut down case where the switch has stopped but we're // concurrently trying to add a peer. diff --git a/p2p/switch_test.go b/p2p/switch_test.go index d5dd178b6..ab8ae9e9f 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -161,10 +161,6 @@ func assertMsgReceivedWithTimeout(t *testing.T, msgBytes []byte, channel byte, r func TestSwitchFiltersOutItself(t *testing.T) { s1 := MakeSwitch(cfg, 1, "127.0.0.1", "123.123.123", initSwitchFunc) - // addr := s1.NodeInfo().NetAddress() - - // // add ourselves like we do in node.go#427 - // s1.addrBook.AddOurAddress(addr) // simulate s1 having a public IP by creating a remote peer with the same ID rp := &remotePeer{PrivKey: s1.nodeKey.PrivKey, Config: cfg} @@ -498,7 +494,7 @@ func TestSwitchAcceptRoutine(t *testing.T) { rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} remotePeers = append(remotePeers, rp) rp.Start() - c, err := rp.Dial(sw.NodeInfo().NetAddress()) + c, err := rp.Dial(sw.NetAddress()) require.NoError(t, err) // spawn a reading routine to prevent connection from closing go func(c net.Conn) { @@ -517,7 +513,7 @@ func TestSwitchAcceptRoutine(t *testing.T) { // 2. check we close new connections if we already have MaxNumInboundPeers peers rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} rp.Start() - conn, err := rp.Dial(sw.NodeInfo().NetAddress()) + conn, err := rp.Dial(sw.NetAddress()) require.NoError(t, err) // check conn is closed one := make([]byte, 1) @@ -537,6 +533,10 @@ type errorTransport struct { acceptErr error } +func (et errorTransport) NetAddress() NetAddress { + panic("not implemented") +} + func (et errorTransport) Accept(c peerConfig) (Peer, error) { return nil, et.acceptErr } diff --git a/p2p/test_util.go b/p2p/test_util.go index 2d320df85..df60539ba 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -35,7 +35,8 @@ func CreateRandomPeer(outbound bool) *peer { addr, netAddr := CreateRoutableAddr() p := &peer{ peerConn: peerConn{ - outbound: outbound, + outbound: outbound, + socketAddr: netAddr, }, nodeInfo: mockNodeInfo{netAddr}, mconn: &conn.MConnection{}, @@ -174,10 +175,15 @@ func MakeSwitch( PrivKey: ed25519.GenPrivKey(), } nodeInfo := testNodeInfo(nodeKey.ID(), fmt.Sprintf("node%d", i)) + addr, err := NewNetAddressString( + IDAddressString(nodeKey.ID(), nodeInfo.(DefaultNodeInfo).ListenAddr), + ) + if err != nil { + panic(err) + } t := NewMultiplexTransport(nodeInfo, nodeKey, MConnConfig(cfg)) - addr := nodeInfo.NetAddress() if err := t.Listen(*addr); err != nil { panic(err) } @@ -214,7 +220,7 @@ func testPeerConn( cfg *config.P2PConfig, outbound, persistent bool, ourNodePrivKey crypto.PrivKey, - originalAddr *NetAddress, + socketAddr *NetAddress, ) (pc peerConn, err error) { conn := rawConn @@ -231,12 +237,7 @@ func testPeerConn( } // Only the information we already have - return peerConn{ - outbound: outbound, - persistent: persistent, - conn: conn, - originalAddr: originalAddr, - }, nil + return newPeerConn(outbound, persistent, conn, socketAddr), nil } //---------------------------------------------------------------- diff --git a/p2p/transport.go b/p2p/transport.go index d36065ab1..6717db483 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -24,6 +24,7 @@ type IPResolver interface { // accept is the container to carry the upgraded connection and NodeInfo from an // asynchronously running routine to the Accept method. type accept struct { + netAddr *NetAddress conn net.Conn nodeInfo NodeInfo err error @@ -47,6 +48,9 @@ type peerConfig struct { // the transport. Each transport is also responsible to filter establishing // peers specific to its domain. type Transport interface { + // Listening address. + NetAddress() NetAddress + // Accept returns a newly connected Peer. Accept(peerConfig) (Peer, error) @@ -115,6 +119,7 @@ func MultiplexTransportResolver(resolver IPResolver) MultiplexTransportOption { // MultiplexTransport accepts and dials tcp connections and upgrades them to // multiplexed peers. type MultiplexTransport struct { + netAddr NetAddress listener net.Listener acceptc chan accept @@ -161,6 +166,11 @@ func NewMultiplexTransport( } } +// NetAddress implements Transport. +func (mt *MultiplexTransport) NetAddress() NetAddress { + return mt.netAddr +} + // Accept implements Transport. func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) { select { @@ -173,7 +183,7 @@ func (mt *MultiplexTransport) Accept(cfg peerConfig) (Peer, error) { cfg.outbound = false - return mt.wrapPeer(a.conn, a.nodeInfo, cfg, nil), nil + return mt.wrapPeer(a.conn, a.nodeInfo, cfg, a.netAddr), nil case <-mt.closec: return nil, ErrTransportClosed{} } @@ -224,6 +234,7 @@ func (mt *MultiplexTransport) Listen(addr NetAddress) error { return err } + mt.netAddr = addr mt.listener = ln go mt.acceptPeers() @@ -258,15 +269,21 @@ func (mt *MultiplexTransport) acceptPeers() { var ( nodeInfo NodeInfo secretConn *conn.SecretConnection + netAddr *NetAddress ) err := mt.filterConn(c) if err == nil { secretConn, nodeInfo, err = mt.upgrade(c, nil) + if err == nil { + addr := c.RemoteAddr() + id := PubKeyToID(secretConn.RemotePubKey()) + netAddr = NewNetAddress(id, addr) + } } select { - case mt.acceptc <- accept{secretConn, nodeInfo, err}: + case mt.acceptc <- accept{netAddr, secretConn, nodeInfo, err}: // Make the upgraded peer available. case <-mt.closec: // Give up if the transport was closed. @@ -426,14 +443,14 @@ func (mt *MultiplexTransport) wrapPeer( c net.Conn, ni NodeInfo, cfg peerConfig, - dialedAddr *NetAddress, + socketAddr *NetAddress, ) Peer { peerConn := newPeerConn( cfg.outbound, cfg.persistent, c, - dialedAddr, + socketAddr, ) p := newPeer( diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 81f9d1b8e..35fd9c66b 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -8,6 +8,8 @@ import ( "testing" "time" + "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/p2p/conn" ) @@ -142,43 +144,23 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { func TestTransportMultiplexAcceptMultiple(t *testing.T) { mt := testSetupMultiplexTransport(t) + id, addr := mt.nodeKey.ID(), mt.listener.Addr().String() + laddr, err := NewNetAddressStringWithOptionalID(IDAddressString(id, addr)) + require.NoError(t, err) var ( - seed = rand.New(rand.NewSource(time.Now().UnixNano())) - errc = make(chan error, seed.Intn(64)+64) + seed = rand.New(rand.NewSource(time.Now().UnixNano())) + nDialers = seed.Intn(64) + 64 + errc = make(chan error, nDialers) ) // Setup dialers. - for i := 0; i < cap(errc); i++ { - go func() { - var ( - pv = ed25519.GenPrivKey() - dialer = newMultiplexTransport( - testNodeInfo(PubKeyToID(pv.PubKey()), defaultNodeName), - NodeKey{ - PrivKey: pv, - }, - ) - ) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } - - _, err = dialer.Dial(*addr, peerConfig{}) - if err != nil { - errc <- err - return - } - - // Signal that the connection was established. - errc <- nil - }() + for i := 0; i < nDialers; i++ { + go testDialer(*laddr, errc) } // Catch connection errors. - for i := 0; i < cap(errc); i++ { + for i := 0; i < nDialers; i++ { if err := <-errc; err != nil { t.Fatal(err) } @@ -216,6 +198,27 @@ func TestTransportMultiplexAcceptMultiple(t *testing.T) { } } +func testDialer(dialAddr NetAddress, errc chan error) { + var ( + pv = ed25519.GenPrivKey() + dialer = newMultiplexTransport( + testNodeInfo(PubKeyToID(pv.PubKey()), defaultNodeName), + NodeKey{ + PrivKey: pv, + }, + ) + ) + + _, err := dialer.Dial(dialAddr, peerConfig{}) + if err != nil { + errc <- err + return + } + + // Signal that the connection was established. + errc <- nil +} + func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { mt := testSetupMultiplexTransport(t) @@ -591,6 +594,7 @@ func TestTransportHandshake(t *testing.T) { } } +// create listener func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport { var ( pv = ed25519.GenPrivKey() diff --git a/rpc/core/consensus.go b/rpc/core/consensus.go index 3850999d3..ad23a461c 100644 --- a/rpc/core/consensus.go +++ b/rpc/core/consensus.go @@ -218,7 +218,7 @@ func DumpConsensusState(ctx *rpctypes.Context) (*ctypes.ResultDumpConsensusState } peerStates[i] = ctypes.PeerStateInfo{ // Peer basic info. - NodeAddress: peer.NodeInfo().NetAddress().String(), + NodeAddress: peer.SocketAddr().String(), // Peer consensus state. PeerState: peerStateJSON, } From 3cfd9757a78fc2398c6853aace2541338be8eaef Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 2 Apr 2019 09:14:33 -0400 Subject: [PATCH 149/259] changelog and version v0.31.3 --- CHANGELOG.md | 18 ++++++++++++++++++ version/version.go | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5ac75e9a..52a926aed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,23 @@ # Changelog +## v0.31.3 + +*April 1st, 2019* + +This release includes two security sensitive fixes: it ensures generated private +keys are valid, and it prevents certain DNS lookups that would cause the node to +panic if the lookup failed. + +### BUG FIXES: + +- [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) + Ensure generated private keys are valid by randomly sampling until a valid key is found. + Previously, it was possible (though rare!) to generate keys that exceeded the curve order. + Such keys would lead to invalid signatures. +- [p2p] [\#3522](https://github.com/tendermint/tendermint/issues/3522) Memoize + socket address in peer connections to avoid DNS lookups. Previously, failed + DNS lookups could cause the node to panic. + ## v0.31.2 *March 30th, 2019* diff --git a/version/version.go b/version/version.go index ac52c4ec1..a42a8f005 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.2" + TMCoreSemVer = "0.31.3" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From f965a4db15796dfca4e504d93eaa83760ced4af2 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 3 Apr 2019 11:22:52 +0200 Subject: [PATCH 150/259] p2p: seed mode refactoring (#3011) ListOfKnownAddresses is removed panic if addrbook size is less than zero CrawlPeers does not attempt to connect to existing or peers we're currently dialing various perf. fixes improved tests (though not complete) move IsDialingOrExistingAddress check into DialPeerWithAddress (Fixes #2716) * addrbook: preallocate memory when saving addrbook to file * addrbook: remove oldestFirst struct and check for ID * oldestFirst replaced with sort.Slice * ID is now mandatory, so no need to check * addrbook: remove ListOfKnownAddresses GetSelection is used instead in seed mode. * addrbook: panic if size is less than 0 * rewrite addrbook#saveToFile to not use a counter * test AttemptDisconnects func * move IsDialingOrExistingAddress check into DialPeerWithAddress * save and cleanup crawl peer data * get rid of DefaultSeedDisconnectWaitPeriod * make linter happy * fix TestPEXReactorSeedMode * fix comment * add a changelog entry * Apply suggestions from code review Co-Authored-By: melekes * rename ErrDialingOrExistingAddress to ErrCurrentlyDialingOrExistingAddress * lowercase errors * do not persist seed data pros: - no extra files - less IO cons: - if the node crashes, seed might crawl a peer too soon * fixes after Ethan's review * add a changelog entry * we should only consult Switch about peers checking addrbook size does not make sense since only PEX reactor uses it for dialing peers! https://github.com/tendermint/tendermint/pull/3011#discussion_r270948875 --- CHANGELOG_PENDING.md | 2 +- node/node.go | 6 ++ p2p/errors.go | 24 ++++-- p2p/pex/addrbook.go | 24 ++---- p2p/pex/file.go | 9 +- p2p/pex/known_address.go | 12 --- p2p/pex/pex_reactor.go | 167 +++++++++++++++++++----------------- p2p/pex/pex_reactor_test.go | 66 ++++++++------ p2p/switch.go | 30 ++++--- 9 files changed, 175 insertions(+), 165 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 045247937..21e52fe60 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -19,4 +19,4 @@ ### IMPROVEMENTS: ### BUG FIXES: - +- [p2p] \#2716 Check if we're already connected to peer right before dialing it (@melekes) diff --git a/node/node.go b/node/node.go index e91d36357..c0a4d736e 100644 --- a/node/node.go +++ b/node/node.go @@ -498,6 +498,12 @@ func NewNode(config *cfg.Config, &pex.PEXReactorConfig{ Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "), SeedMode: config.P2P.SeedMode, + // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000 + // blocks assuming 10s blocks ~ 28 hours. + // TODO (melekes): make it dynamic based on the actual block latencies + // from the live network. + // https://github.com/tendermint/tendermint/issues/3523 + SeedDisconnectWaitPeriod: 28 * time.Hour, }) pexReactor.SetLogger(logger.With("module", "pex")) sw.AddReactor("PEX", pexReactor) diff --git a/p2p/errors.go b/p2p/errors.go index 706150945..3650a7a0a 100644 --- a/p2p/errors.go +++ b/p2p/errors.go @@ -103,7 +103,7 @@ type ErrSwitchDuplicatePeerID struct { } func (e ErrSwitchDuplicatePeerID) Error() string { - return fmt.Sprintf("Duplicate peer ID %v", e.ID) + return fmt.Sprintf("duplicate peer ID %v", e.ID) } // ErrSwitchDuplicatePeerIP to be raised whena a peer is connecting with a known @@ -113,7 +113,7 @@ type ErrSwitchDuplicatePeerIP struct { } func (e ErrSwitchDuplicatePeerIP) Error() string { - return fmt.Sprintf("Duplicate peer IP %v", e.IP.String()) + return fmt.Sprintf("duplicate peer IP %v", e.IP.String()) } // ErrSwitchConnectToSelf to be raised when trying to connect to itself. @@ -122,7 +122,7 @@ type ErrSwitchConnectToSelf struct { } func (e ErrSwitchConnectToSelf) Error() string { - return fmt.Sprintf("Connect to self: %v", e.Addr) + return fmt.Sprintf("connect to self: %v", e.Addr) } type ErrSwitchAuthenticationFailure struct { @@ -132,7 +132,7 @@ type ErrSwitchAuthenticationFailure struct { func (e ErrSwitchAuthenticationFailure) Error() string { return fmt.Sprintf( - "Failed to authenticate peer. Dialed %v, but got peer with ID %s", + "failed to authenticate peer. Dialed %v, but got peer with ID %s", e.Dialed, e.Got, ) @@ -152,7 +152,7 @@ type ErrNetAddressNoID struct { } func (e ErrNetAddressNoID) Error() string { - return fmt.Sprintf("Address (%s) does not contain ID", e.Addr) + return fmt.Sprintf("address (%s) does not contain ID", e.Addr) } type ErrNetAddressInvalid struct { @@ -161,7 +161,7 @@ type ErrNetAddressInvalid struct { } func (e ErrNetAddressInvalid) Error() string { - return fmt.Sprintf("Invalid address (%s): %v", e.Addr, e.Err) + return fmt.Sprintf("invalid address (%s): %v", e.Addr, e.Err) } type ErrNetAddressLookup struct { @@ -170,5 +170,15 @@ type ErrNetAddressLookup struct { } func (e ErrNetAddressLookup) Error() string { - return fmt.Sprintf("Error looking up host (%s): %v", e.Addr, e.Err) + return fmt.Sprintf("error looking up host (%s): %v", e.Addr, e.Err) +} + +// ErrCurrentlyDialingOrExistingAddress indicates that we're currently +// dialing this address or it belongs to an existing peer. +type ErrCurrentlyDialingOrExistingAddress struct { + Addr string +} + +func (e ErrCurrentlyDialingOrExistingAddress) Error() string { + return fmt.Sprintf("connection with %s has been established or dialed", e.Addr) } diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 3cb91c380..6be03d75b 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -66,8 +66,7 @@ type AddrBook interface { // Send a selection of addresses with bias GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddress - // TODO: remove - ListOfKnownAddresses() []*knownAddress + Size() int // Persist to disk Save() @@ -254,7 +253,7 @@ func (a *addrBook) PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress { bookSize := a.size() if bookSize <= 0 { if bookSize < 0 { - a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) + panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) } return nil } @@ -339,7 +338,7 @@ func (a *addrBook) GetSelection() []*p2p.NetAddress { bookSize := a.size() if bookSize <= 0 { if bookSize < 0 { - a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) + panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) } return nil } @@ -389,7 +388,7 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre bookSize := a.size() if bookSize <= 0 { if bookSize < 0 { - a.Logger.Error("Addrbook size less than 0", "nNew", a.nNew, "nOld", a.nOld) + panic(fmt.Sprintf("Addrbook size %d (new: %d + old: %d) is less than 0", a.nNew+a.nOld, a.nNew, a.nOld)) } return nil } @@ -414,18 +413,6 @@ func (a *addrBook) GetSelectionWithBias(biasTowardsNewAddrs int) []*p2p.NetAddre return selection } -// ListOfKnownAddresses returns the new and old addresses. -func (a *addrBook) ListOfKnownAddresses() []*knownAddress { - a.mtx.Lock() - defer a.mtx.Unlock() - - addrs := []*knownAddress{} - for _, addr := range a.addrLookup { - addrs = append(addrs, addr.copy()) - } - return addrs -} - //------------------------------------------------ // Size returns the number of addresses in the book. @@ -473,8 +460,7 @@ func (a *addrBook) getBucket(bucketType byte, bucketIdx int) map[string]*knownAd case bucketTypeOld: return a.bucketsOld[bucketIdx] default: - cmn.PanicSanity("Should not happen") - return nil + panic("Invalid bucket type") } } diff --git a/p2p/pex/file.go b/p2p/pex/file.go index 33fec0336..d4a516850 100644 --- a/p2p/pex/file.go +++ b/p2p/pex/file.go @@ -16,16 +16,15 @@ type addrBookJSON struct { } func (a *addrBook) saveToFile(filePath string) { - a.Logger.Info("Saving AddrBook to file", "size", a.Size()) - a.mtx.Lock() defer a.mtx.Unlock() - // Compile Addrs - addrs := []*knownAddress{} + + a.Logger.Info("Saving AddrBook to file", "size", a.size()) + + addrs := make([]*knownAddress, 0, len(a.addrLookup)) for _, ka := range a.addrLookup { addrs = append(addrs, ka) } - aJSON := &addrBookJSON{ Key: a.key, Addrs: addrs, diff --git a/p2p/pex/known_address.go b/p2p/pex/known_address.go index 5673dec11..acde385bc 100644 --- a/p2p/pex/known_address.go +++ b/p2p/pex/known_address.go @@ -33,18 +33,6 @@ func (ka *knownAddress) ID() p2p.ID { return ka.Addr.ID } -func (ka *knownAddress) copy() *knownAddress { - return &knownAddress{ - Addr: ka.Addr, - Src: ka.Src, - Attempts: ka.Attempts, - LastAttempt: ka.LastAttempt, - LastSuccess: ka.LastSuccess, - BucketType: ka.BucketType, - Buckets: ka.Buckets, - } -} - func (ka *knownAddress) isOld() bool { return ka.BucketType == bucketTypeOld } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 0ce116326..cf8cfe6f5 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -3,7 +3,6 @@ package pex import ( "fmt" "reflect" - "sort" "sync" "time" @@ -35,16 +34,11 @@ const ( // Seed/Crawler constants - // We want seeds to only advertise good peers. Therefore they should wait at - // least as long as we expect it to take for a peer to become good before - // disconnecting. - // see consensus/reactor.go: blocksToContributeToBecomeGoodPeer - // 10000 blocks assuming 1s blocks ~ 2.7 hours. - defaultSeedDisconnectWaitPeriod = 3 * time.Hour + // minTimeBetweenCrawls is a minimum time between attempts to crawl a peer. + minTimeBetweenCrawls = 2 * time.Minute - defaultCrawlPeerInterval = 2 * time.Minute // don't redial for this. TODO: back-off. what for? - - defaultCrawlPeersPeriod = 30 * time.Second // check some peers every this + // check some peers every this + crawlPeerPeriod = 30 * time.Second maxAttemptsToDial = 16 // ~ 35h in total (last attempt - 18h) @@ -77,6 +71,9 @@ type PEXReactor struct { seedAddrs []*p2p.NetAddress attemptsToDial sync.Map // address (string) -> {number of attempts (int), last time dialed (time.Time)} + + // seed/crawled mode fields + crawlPeerInfos map[p2p.ID]crawlPeerInfo } func (r *PEXReactor) minReceiveRequestInterval() time.Duration { @@ -90,6 +87,11 @@ type PEXReactorConfig struct { // Seed/Crawler mode SeedMode bool + // We want seeds to only advertise good peers. Therefore they should wait at + // least as long as we expect it to take for a peer to become good before + // disconnecting. + SeedDisconnectWaitPeriod time.Duration + // Seeds is a list of addresses reactor may use // if it can't connect to peers in the addrbook. Seeds []string @@ -108,6 +110,7 @@ func NewPEXReactor(b AddrBook, config *PEXReactorConfig) *PEXReactor { ensurePeersPeriod: defaultEnsurePeersPeriod, requestsSent: cmn.NewCMap(), lastReceivedRequests: cmn.NewCMap(), + crawlPeerInfos: make(map[p2p.ID]crawlPeerInfo), } r.BaseReactor = *p2p.NewBaseReactor("PEXReactor", r) return r @@ -363,9 +366,9 @@ func (r *PEXReactor) ensurePeersRoutine() { ) // Randomize first round of communication to avoid thundering herd. - // If no potential peers are present directly start connecting so we guarantee - // swift setup with the help of configured seeds. - if r.hasPotentialPeers() { + // If no peers are present directly start connecting so we guarantee swift + // setup with the help of configured seeds. + if r.nodeHasSomePeersOrDialingAny() { time.Sleep(time.Duration(jitter)) } @@ -493,23 +496,26 @@ func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { err := r.Switch.DialPeerWithAddress(addr, false) if err != nil { + if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { + return + } + r.Logger.Error("Dialing failed", "addr", addr, "err", err, "attempts", attempts) - // TODO: detect more "bad peer" scenarios + markAddrInBookBasedOnErr(addr, r.book, err) if _, ok := err.(p2p.ErrSwitchAuthenticationFailure); ok { - r.book.MarkBad(addr) r.attemptsToDial.Delete(addr.DialString()) } else { - r.book.MarkAttempt(addr) // FIXME: if the addr is going to be removed from the addrbook (hard to // tell at this point), we need to Delete it from attemptsToDial, not // record another attempt. // record attempt r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) } - } else { - // cleanup any history - r.attemptsToDial.Delete(addr.DialString()) + return } + + // cleanup any history + r.attemptsToDial.Delete(addr.DialString()) } // checkSeeds checks that addresses are well formed. @@ -568,101 +574,92 @@ func (r *PEXReactor) AttemptsToDial(addr *p2p.NetAddress) int { // from peers, except other seed nodes. func (r *PEXReactor) crawlPeersRoutine() { // Do an initial crawl - r.crawlPeers() + r.crawlPeers(r.book.GetSelection()) // Fire periodically - ticker := time.NewTicker(defaultCrawlPeersPeriod) + ticker := time.NewTicker(crawlPeerPeriod) for { select { case <-ticker.C: r.attemptDisconnects() - r.crawlPeers() + r.crawlPeers(r.book.GetSelection()) + r.cleanupCrawlPeerInfos() case <-r.Quit(): return } } } -// hasPotentialPeers indicates if there is a potential peer to connect to, by -// consulting the Switch as well as the AddrBook. -func (r *PEXReactor) hasPotentialPeers() bool { +// nodeHasSomePeersOrDialingAny returns true if the node is connected to some +// peers or dialing them currently. +func (r *PEXReactor) nodeHasSomePeersOrDialingAny() bool { out, in, dial := r.Switch.NumPeers() - - return out+in+dial > 0 && len(r.book.ListOfKnownAddresses()) > 0 + return out+in+dial > 0 } -// crawlPeerInfo handles temporary data needed for the -// network crawling performed during seed/crawler mode. +// crawlPeerInfo handles temporary data needed for the network crawling +// performed during seed/crawler mode. type crawlPeerInfo struct { - // The listening address of a potential peer we learned about - Addr *p2p.NetAddress - - // The last time we attempt to reach this address - LastAttempt time.Time - - // The last time we successfully reached this address - LastSuccess time.Time + Addr *p2p.NetAddress `json:"addr"` + // The last time we crawled the peer or attempted to do so. + LastCrawled time.Time `json:"last_crawled"` } -// oldestFirst implements sort.Interface for []crawlPeerInfo -// based on the LastAttempt field. -type oldestFirst []crawlPeerInfo - -func (of oldestFirst) Len() int { return len(of) } -func (of oldestFirst) Swap(i, j int) { of[i], of[j] = of[j], of[i] } -func (of oldestFirst) Less(i, j int) bool { return of[i].LastAttempt.Before(of[j].LastAttempt) } - -// getPeersToCrawl returns addresses of potential peers that we wish to validate. -// NOTE: The status information is ordered as described above. -func (r *PEXReactor) getPeersToCrawl() []crawlPeerInfo { - // TODO: be more selective - addrs := r.book.ListOfKnownAddresses() - of := make(oldestFirst, 0, len(addrs)) - for _, addr := range addrs { - if len(addr.ID()) == 0 { - continue // dont use peers without id - } - - of = append(of, crawlPeerInfo{ - Addr: addr.Addr, - LastAttempt: addr.LastAttempt, - LastSuccess: addr.LastSuccess, - }) - } - sort.Sort(of) - return of -} - -// crawlPeers will crawl the network looking for new peer addresses. (once) -func (r *PEXReactor) crawlPeers() { - peerInfos := r.getPeersToCrawl() - +// crawlPeers will crawl the network looking for new peer addresses. +func (r *PEXReactor) crawlPeers(addrs []*p2p.NetAddress) { now := time.Now() - // Use addresses we know of to reach additional peers - for _, pi := range peerInfos { - // Do not attempt to connect with peers we recently dialed - if now.Sub(pi.LastAttempt) < defaultCrawlPeerInterval { + + for _, addr := range addrs { + peerInfo, ok := r.crawlPeerInfos[addr.ID] + + // Do not attempt to connect with peers we recently crawled. + if ok && now.Sub(peerInfo.LastCrawled) < minTimeBetweenCrawls { continue } - // Otherwise, attempt to connect with the known address - err := r.Switch.DialPeerWithAddress(pi.Addr, false) + + // Record crawling attempt. + r.crawlPeerInfos[addr.ID] = crawlPeerInfo{ + Addr: addr, + LastCrawled: now, + } + + err := r.Switch.DialPeerWithAddress(addr, false) if err != nil { - r.book.MarkAttempt(pi.Addr) + if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { + continue + } + + r.Logger.Error("Dialing failed", "addr", addr, "err", err) + markAddrInBookBasedOnErr(addr, r.book, err) continue } - // Ask for more addresses - peer := r.Switch.Peers().Get(pi.Addr.ID) + + peer := r.Switch.Peers().Get(addr.ID) if peer != nil { r.RequestAddrs(peer) } } } +func (r *PEXReactor) cleanupCrawlPeerInfos() { + for id, info := range r.crawlPeerInfos { + // If we did not crawl a peer for 24 hours, it means the peer was removed + // from the addrbook => remove + // + // 10000 addresses / maxGetSelection = 40 cycles to get all addresses in + // the ideal case, + // 40 * crawlPeerPeriod ~ 20 minutes + if time.Since(info.LastCrawled) > 24*time.Hour { + delete(r.crawlPeerInfos, id) + } + } +} + // attemptDisconnects checks if we've been with each peer long enough to disconnect func (r *PEXReactor) attemptDisconnects() { for _, peer := range r.Switch.Peers().List() { - if peer.Status().Duration < defaultSeedDisconnectWaitPeriod { + if peer.Status().Duration < r.config.SeedDisconnectWaitPeriod { continue } if peer.IsPersistent() { @@ -672,6 +669,16 @@ func (r *PEXReactor) attemptDisconnects() { } } +func markAddrInBookBasedOnErr(addr *p2p.NetAddress, book AddrBook, err error) { + // TODO: detect more "bad peer" scenarios + switch err.(type) { + case p2p.ErrSwitchAuthenticationFailure: + book.MarkBad(addr) + default: + book.MarkAttempt(addr) + } +} + //----------------------------------------------------------------------------- // Messages diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 4a6118c63..dda60daaa 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -204,26 +204,26 @@ func TestCheckSeeds(t *testing.T) { defer os.RemoveAll(dir) // nolint: errcheck // 1. test creating peer with no seeds works - peer := testCreateDefaultPeer(dir, 0) - require.Nil(t, peer.Start()) - peer.Stop() + peerSwitch := testCreateDefaultPeer(dir, 0) + require.Nil(t, peerSwitch.Start()) + peerSwitch.Stop() // 2. create seed seed := testCreateSeed(dir, 1, []*p2p.NetAddress{}, []*p2p.NetAddress{}) // 3. test create peer with online seed works - peer = testCreatePeerWithSeed(dir, 2, seed) - require.Nil(t, peer.Start()) - peer.Stop() + peerSwitch = testCreatePeerWithSeed(dir, 2, seed) + require.Nil(t, peerSwitch.Start()) + peerSwitch.Stop() // 4. test create peer with all seeds having unresolvable DNS fails badPeerConfig := &PEXReactorConfig{ Seeds: []string{"ed3dfd27bfc4af18f67a49862f04cc100696e84d@bad.network.addr:26657", "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657"}, } - peer = testCreatePeerWithConfig(dir, 2, badPeerConfig) - require.Error(t, peer.Start()) - peer.Stop() + peerSwitch = testCreatePeerWithConfig(dir, 2, badPeerConfig) + require.Error(t, peerSwitch.Start()) + peerSwitch.Stop() // 5. test create peer with one good seed address succeeds badPeerConfig = &PEXReactorConfig{ @@ -231,9 +231,9 @@ func TestCheckSeeds(t *testing.T) { "d824b13cb5d40fa1d8a614e089357c7eff31b670@anotherbad.network.addr:26657", seed.NetAddress().String()}, } - peer = testCreatePeerWithConfig(dir, 2, badPeerConfig) - require.Nil(t, peer.Start()) - peer.Stop() + peerSwitch = testCreatePeerWithConfig(dir, 2, badPeerConfig) + require.Nil(t, peerSwitch.Start()) + peerSwitch.Stop() } func TestPEXReactorUsesSeedsIfNeeded(t *testing.T) { @@ -285,31 +285,41 @@ func TestConnectionSpeedForPeerReceivedFromSeed(t *testing.T) { assertPeersWithTimeout(t, []*p2p.Switch{secondPeer}, 10*time.Millisecond, 1*time.Second, 2) } -func TestPEXReactorCrawlStatus(t *testing.T) { - pexR, book := createReactor(&PEXReactorConfig{SeedMode: true}) +func TestPEXReactorSeedMode(t *testing.T) { + // directory to store address books + dir, err := ioutil.TempDir("", "pex_reactor") + require.Nil(t, err) + defer os.RemoveAll(dir) // nolint: errcheck + + pexR, book := createReactor(&PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 10 * time.Millisecond}) defer teardownReactor(book) - // Seed/Crawler mode uses data from the Switch sw := createSwitchAndAddReactors(pexR) sw.SetAddrBook(book) + err = sw.Start() + require.NoError(t, err) + defer sw.Stop() - // Create a peer, add it to the peer set and the addrbook. - peer := p2p.CreateRandomPeer(false) - p2p.AddPeerToSwitch(pexR.Switch, peer) - addr1 := peer.SocketAddr() - pexR.book.AddAddress(addr1, addr1) + assert.Zero(t, sw.Peers().Size()) - // Add a non-connected address to the book. - _, addr2 := p2p.CreateRoutableAddr() - pexR.book.AddAddress(addr2, addr1) + peerSwitch := testCreateDefaultPeer(dir, 1) + require.NoError(t, peerSwitch.Start()) + defer peerSwitch.Stop() - // Get some peerInfos to crawl - peerInfos := pexR.getPeersToCrawl() + // 1. Test crawlPeers dials the peer + pexR.crawlPeers([]*p2p.NetAddress{peerSwitch.NetAddress()}) + assert.Equal(t, 1, sw.Peers().Size()) + assert.True(t, sw.Peers().Has(peerSwitch.NodeInfo().ID())) - // Make sure it has the proper number of elements - assert.Equal(t, 2, len(peerInfos)) + // 2. attemptDisconnects should not disconnect because of wait period + pexR.attemptDisconnects() + assert.Equal(t, 1, sw.Peers().Size()) - // TODO: test + time.Sleep(100 * time.Millisecond) + + // 3. attemptDisconnects should disconnect after wait period + pexR.attemptDisconnects() + assert.Equal(t, 0, sw.Peers().Size()) } // connect a peer to a seed, wait a bit, then stop it. diff --git a/p2p/switch.go b/p2p/switch.go index 76da9ad0c..5b1da1ead 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -339,14 +339,11 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { return } - if sw.IsDialingOrExistingAddress(addr) { - sw.Logger.Debug("Peer connection has been established or dialed while we waiting next try", "addr", addr) - return - } - err := sw.DialPeerWithAddress(addr, true) if err == nil { return // success + } else if _, ok := err.(ErrCurrentlyDialingOrExistingAddress); ok { + return } sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "addr", addr) @@ -365,9 +362,12 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { // sleep an exponentially increasing amount sleepIntervalSeconds := math.Pow(reconnectBackOffBaseSeconds, float64(i)) sw.randomSleep(time.Duration(sleepIntervalSeconds) * time.Second) + err := sw.DialPeerWithAddress(addr, true) if err == nil { return // success + } else if _, ok := err.(ErrCurrentlyDialingOrExistingAddress); ok { + return } sw.Logger.Info("Error reconnecting to peer. Trying again", "tries", i, "err", err, "addr", addr) } @@ -435,15 +435,10 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b sw.randomSleep(0) - if sw.IsDialingOrExistingAddress(addr) { - sw.Logger.Debug("Ignore attempt to connect to an existing peer", "addr", addr) - return - } - err := sw.DialPeerWithAddress(addr, persistent) if err != nil { switch err.(type) { - case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeerID: + case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeerID, ErrCurrentlyDialingOrExistingAddress: sw.Logger.Debug("Error dialing peer", "err", err) default: sw.Logger.Error("Error dialing peer", "err", err) @@ -454,11 +449,20 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b return nil } -// DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects and authenticates successfully. -// If `persistent == true`, the switch will always try to reconnect to this peer if the connection ever fails. +// DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects +// and authenticates successfully. +// If `persistent == true`, the switch will always try to reconnect to this +// peer if the connection ever fails. +// If we're currently dialing this address or it belongs to an existing peer, +// ErrCurrentlyDialingOrExistingAddress is returned. func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) error { + if sw.IsDialingOrExistingAddress(addr) { + return ErrCurrentlyDialingOrExistingAddress{addr.String()} + } + sw.dialing.Set(string(addr.ID), addr) defer sw.dialing.Delete(string(addr.ID)) + return sw.addOutboundPeerWithConfig(addr, sw.config, persistent) } From 40da355234bda9d510dca370e94b646450673879 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 3 Apr 2019 14:56:51 +0200 Subject: [PATCH 151/259] docs: fix block.Header.Time description (#3529) It's not proposer local time anymore, but a weighted median Fixes #3514 --- CHANGELOG_PENDING.md | 1 + docs/spec/abci/abci.md | 6 ++++-- docs/spec/blockchain/blockchain.md | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 21e52fe60..22854a530 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,3 +20,4 @@ ### BUG FIXES: - [p2p] \#2716 Check if we're already connected to peer right before dialing it (@melekes) +- [docs] \#3514 Fix block.Header.Time description (@melekes) diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index c696c9384..c65d96ec1 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -347,8 +347,10 @@ Commit are included in the header of the next block. - `Version (Version)`: Version of the blockchain and the application - `ChainID (string)`: ID of the blockchain - `Height (int64)`: Height of the block in the chain - - `Time (google.protobuf.Timestamp)`: Time of the block. It is the proposer's - local time when block was created. + - `Time (google.protobuf.Timestamp)`: Time of the previous block. + For heights > 1, it's the weighted median of the timestamps of the valid + votes in the block.LastCommit. + For height == 1, it's genesis time. - `NumTxs (int32)`: Number of transactions in the block - `TotalTxs (int64)`: Total number of transactions in the blockchain until now diff --git a/docs/spec/blockchain/blockchain.md b/docs/spec/blockchain/blockchain.md index 9f88d6417..cd31c5dc1 100644 --- a/docs/spec/blockchain/blockchain.md +++ b/docs/spec/blockchain/blockchain.md @@ -244,7 +244,7 @@ The height is an incrementing integer. The first block has `block.Header.Height ### Time ``` -block.Header.Timestamp >= prevBlock.Header.Timestamp + 1 ms +block.Header.Timestamp >= prevBlock.Header.Timestamp + state.consensusParams.Block.TimeIotaMs block.Header.Timestamp == MedianTime(block.LastCommit, state.LastValidators) ``` From 9a415b057238d118c1edf23d673ebf9ce4f8d2ae Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 9 Apr 2019 18:21:35 +0200 Subject: [PATCH 152/259] docs: abci#Commit: better explain the possible deadlock (#3536) --- docs/spec/abci/apps.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/spec/abci/apps.md b/docs/spec/abci/apps.md index ca6abe7f8..47e62eed9 100644 --- a/docs/spec/abci/apps.md +++ b/docs/spec/abci/apps.md @@ -31,8 +31,13 @@ states to the latest committed state at once. When `Commit` completes, it unlocks the mempool. -Note that it is not possible to send transactions to Tendermint during `Commit` - if your app -tries to send a `/broadcast_tx` to Tendermint during Commit, it will deadlock. +WARNING: if the ABCI app logic processing the `Commit` message sends a +`/broadcast_tx_sync` or `/broadcast_tx_commit` and waits for the response +before proceeding, it will deadlock. Executing those `broadcast_tx` calls +involves acquiring a lock that is held during the `Commit` call, so it's not +possible. If you make the call to the `broadcast_tx` endpoints concurrently, +that's no problem, it just can't be part of the sequential logic of the +`Commit` function. ### Consensus Connection From bcec8be035a89b5d08ee00b8cc7115017a58c7e0 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 11 Apr 2019 15:32:16 +0200 Subject: [PATCH 153/259] p2p: do not log err if peer is private (#3474) * add actionable advice for ErrAddrBookNonRoutable err Should replace https://github.com/tendermint/tendermint/pull/3463 * reorder checks in addrbook#addAddress so ErrAddrBookPrivate is returned first and do not log error in DialPeersAsync if the address is private because it's not an error --- p2p/pex/addrbook.go | 26 +++++++++++++------------- p2p/pex/errors.go | 8 ++++++++ p2p/switch.go | 17 ++++++++++++++++- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index 6be03d75b..ca788aa66 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -586,23 +586,10 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return ErrAddrBookNilAddr{addr, src} } - if a.routabilityStrict && !addr.Routable() { - return ErrAddrBookNonRoutable{addr} - } - - if !addr.Valid() { - return ErrAddrBookInvalidAddr{addr} - } - if !addr.HasID() { return ErrAddrBookInvalidAddrNoID{addr} } - // TODO: we should track ourAddrs by ID and by IP:PORT and refuse both. - if _, ok := a.ourAddrs[addr.String()]; ok { - return ErrAddrBookSelf{addr} - } - if _, ok := a.privateIDs[addr.ID]; ok { return ErrAddrBookPrivate{addr} } @@ -611,6 +598,19 @@ func (a *addrBook) addAddress(addr, src *p2p.NetAddress) error { return ErrAddrBookPrivateSrc{src} } + // TODO: we should track ourAddrs by ID and by IP:PORT and refuse both. + if _, ok := a.ourAddrs[addr.String()]; ok { + return ErrAddrBookSelf{addr} + } + + if a.routabilityStrict && !addr.Routable() { + return ErrAddrBookNonRoutable{addr} + } + + if !addr.Valid() { + return ErrAddrBookInvalidAddr{addr} + } + ka := a.addrLookup[addr.ID] if ka != nil { // If its already old and the addr is the same, ignore it. diff --git a/p2p/pex/errors.go b/p2p/pex/errors.go index 1f44ceee7..543056af5 100644 --- a/p2p/pex/errors.go +++ b/p2p/pex/errors.go @@ -30,6 +30,10 @@ func (err ErrAddrBookPrivate) Error() string { return fmt.Sprintf("Cannot add private peer with address %v", err.Addr) } +func (err ErrAddrBookPrivate) PrivateAddr() bool { + return true +} + type ErrAddrBookPrivateSrc struct { Src *p2p.NetAddress } @@ -38,6 +42,10 @@ func (err ErrAddrBookPrivateSrc) Error() string { return fmt.Sprintf("Cannot add peer coming from private peer with address %v", err.Src) } +func (err ErrAddrBookPrivateSrc) PrivateAddr() bool { + return true +} + type ErrAddrBookNilAddr struct { Addr *p2p.NetAddress Src *p2p.NetAddress diff --git a/p2p/switch.go b/p2p/switch.go index 5b1da1ead..04caabdc7 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -6,6 +6,8 @@ import ( "sync" "time" + "github.com/pkg/errors" + "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p/conn" @@ -390,6 +392,15 @@ func (sw *Switch) MarkPeerAsGood(peer Peer) { //--------------------------------------------------------------------- // Dialing +type privateAddr interface { + PrivateAddr() bool +} + +func isPrivateAddr(err error) bool { + te, ok := errors.Cause(err).(privateAddr) + return ok && te.PrivateAddr() +} + // DialPeersAsync dials a list of peers asynchronously in random order (optionally, making them persistent). // Used to dial peers from config on startup or from unsafe-RPC (trusted sources). // TODO: remove addrBook arg since it's now set on the switch @@ -412,7 +423,11 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b // do not add our address or ID if !netAddr.Same(ourAddr) { if err := addrBook.AddAddress(netAddr, ourAddr); err != nil { - sw.Logger.Error("Can't add peer's address to addrbook", "err", err) + if isPrivateAddr(err) { + sw.Logger.Debug("Won't add peer's address to addrbook", "err", err) + } else { + sw.Logger.Error("Can't add peer's address to addrbook", "err", err) + } } } } From c3df21fe827aad0447bb59a9546c32db8406a5fd Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 11 Apr 2019 18:59:14 +0300 Subject: [PATCH 154/259] add missing changelog entry (#3544) * add missing changelog entry --- CHANGELOG_PENDING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 22854a530..91475e3a9 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -17,6 +17,7 @@ ### FEATURES: ### IMPROVEMENTS: +- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer ### BUG FIXES: - [p2p] \#2716 Check if we're already connected to peer right before dialing it (@melekes) From 18d2c45c334d9f22621ea0af0686e2e54937fb40 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 12 Apr 2019 10:46:07 +0200 Subject: [PATCH 155/259] rpc: Fix response time grow over time (#3537) * rpc: store validator info periodly * increase ValidatorSetStoreInterval also - unexpose it - add a comment - refactor code - add a benchmark, which shows that 100000 results in ~ 100ms to get 100 validators * make the change non-breaking * expand comment * rename valSetStoreInterval to valSetCheckpointInterval * change the panic msg * add a test and changelog entry * update changelog entry * update changelog entry * add a link to PR * fix test * Update CHANGELOG_PENDING.md Co-Authored-By: melekes * update comment * use MaxInt64 func --- CHANGELOG_PENDING.md | 9 ++++++ state/store.go | 53 +++++++++++++++++++++++++---------- state/store_test.go | 67 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 15 deletions(-) create mode 100644 state/store_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 91475e3a9..33f5f27ce 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,5 +20,14 @@ - [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer ### BUG FIXES: + +- [state] [\#3438](https://github.com/tendermint/tendermint/pull/3438) + Persist validators every 100000 blocks even if no changes to the set + occurred (@guagualvcha). This + 1) Prevents possible DoS attack using `/validators` or `/status` RPC + endpoints. Before response time was growing linearly with height if no + changes were made to the validator set. + 2) Fixes performance degradation in `ExecCommitBlock` where we call + `LoadValidators` for each `Evidence` in the block. - [p2p] \#2716 Check if we're already connected to peer right before dialing it (@melekes) - [docs] \#3514 Fix block.Header.Time description (@melekes) diff --git a/state/store.go b/state/store.go index 6b01a8295..73116b43f 100644 --- a/state/store.go +++ b/state/store.go @@ -9,6 +9,14 @@ import ( "github.com/tendermint/tendermint/types" ) +const ( + // persist validators every valSetCheckpointInterval blocks to avoid + // LoadValidators taking too much time. + // https://github.com/tendermint/tendermint/pull/3438 + // 100000 results in ~ 100ms to get 100 validators (see BenchmarkLoadValidators) + valSetCheckpointInterval = 100000 +) + //------------------------------------------------------------------------ func calcValidatorsKey(height int64) []byte { @@ -182,25 +190,38 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { if valInfo == nil { return nil, ErrNoValSetForHeight{height} } - if valInfo.ValidatorSet == nil { - valInfo2 := loadValidatorsInfo(db, valInfo.LastHeightChanged) + lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) + valInfo2 := loadValidatorsInfo(db, lastStoredHeight) if valInfo2 == nil { - panic( - fmt.Sprintf( - "Couldn't find validators at height %d as last changed from height %d", - valInfo.LastHeightChanged, - height, - ), - ) + // TODO (melekes): remove the below if condition in the 0.33 major + // release and just panic. Old chains might panic otherwise if they + // haven't saved validators at intermediate (%valSetCheckpointInterval) + // height yet. + // https://github.com/tendermint/tendermint/issues/3543 + valInfo2 = loadValidatorsInfo(db, valInfo.LastHeightChanged) + lastStoredHeight = valInfo.LastHeightChanged + if valInfo2 == nil { + panic( + fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)", + lastStoredHeight, + height, + ), + ) + } } - valInfo2.ValidatorSet.IncrementProposerPriority(int(height - valInfo.LastHeightChanged)) // mutate + valInfo2.ValidatorSet.IncrementProposerPriority(int(height - lastStoredHeight)) // mutate valInfo = valInfo2 } return valInfo.ValidatorSet, nil } +func lastStoredHeightFor(height, lastHeightChanged int64) int64 { + checkpointHeight := height - height%valSetCheckpointInterval + return cmn.MaxInt64(checkpointHeight, lastHeightChanged) +} + // CONTRACT: Returned ValidatorsInfo can be mutated. func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { buf := db.Get(calcValidatorsKey(height)) @@ -221,10 +242,10 @@ func loadValidatorsInfo(db dbm.DB, height int64) *ValidatorsInfo { } // saveValidatorsInfo persists the validator set. -// `height` is the effective height for which the validator is responsible for signing. -// It should be called from s.Save(), right before the state itself is persisted. -// If the validator set did not change after processing the latest block, -// only the last height for which the validators changed is persisted. +// +// `height` is the effective height for which the validator is responsible for +// signing. It should be called from s.Save(), right before the state itself is +// persisted. func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) { if lastHeightChanged > height { panic("LastHeightChanged cannot be greater than ValidatorsInfo height") @@ -232,7 +253,9 @@ func saveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *type valInfo := &ValidatorsInfo{ LastHeightChanged: lastHeightChanged, } - if lastHeightChanged == height { + // Only persist validator set if it was updated or checkpoint height (see + // valSetCheckpointInterval) is reached. + if height == lastHeightChanged || height%valSetCheckpointInterval == 0 { valInfo.ValidatorSet = valSet } db.Set(calcValidatorsKey(height), valInfo.Bytes()) diff --git a/state/store_test.go b/state/store_test.go new file mode 100644 index 000000000..dd48cae71 --- /dev/null +++ b/state/store_test.go @@ -0,0 +1,67 @@ +package state + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/assert" + + cfg "github.com/tendermint/tendermint/config" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/types" +) + +func TestSaveValidatorsInfo(t *testing.T) { + // test we persist validators every valSetCheckpointInterval blocks + stateDB := dbm.NewMemDB() + val, _ := types.RandValidator(true, 10) + vals := types.NewValidatorSet([]*types.Validator{val}) + + // TODO(melekes): remove in 0.33 release + // https://github.com/tendermint/tendermint/issues/3543 + saveValidatorsInfo(stateDB, 1, 1, vals) + saveValidatorsInfo(stateDB, 2, 1, vals) + assert.NotPanics(t, func() { + _, err := LoadValidators(stateDB, 2) + if err != nil { + panic(err) + } + }) + //ENDREMOVE + + saveValidatorsInfo(stateDB, valSetCheckpointInterval, 1, vals) + + loadedVals, err := LoadValidators(stateDB, valSetCheckpointInterval) + assert.NoError(t, err) + assert.NotZero(t, loadedVals.Size()) +} + +func BenchmarkLoadValidators(b *testing.B) { + const valSetSize = 100 + + config := cfg.ResetTestRoot("state_") + defer os.RemoveAll(config.RootDir) + dbType := dbm.DBBackendType(config.DBBackend) + stateDB := dbm.NewDB("state", dbType, config.DBDir()) + state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) + if err != nil { + b.Fatal(err) + } + state.Validators = genValSet(valSetSize) + state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) + SaveState(stateDB, state) + + for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ... + saveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators) + + b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) { + for n := 0; n < b.N; n++ { + _, err := LoadValidators(stateDB, int64(i)) + if err != nil { + b.Fatal(err) + } + } + }) + } +} From b5b3b85697a9c29adaab0e1ab5d1380459e2c5eb Mon Sep 17 00:00:00 2001 From: Alexander Simmerl Date: Fri, 12 Apr 2019 12:31:02 +0200 Subject: [PATCH 156/259] Bring back NodeInfo NetAddress form the dead (#3545) A prior change to address accidental DNS lookups introduced the SocketAddr on peer, which was then used to add it to the addressbook. Which in turn swallowed the self reported port of the peer, which is important on a reconnect. This change revives the NetAddress on NodeInfo which the Peer carries, but now returns an error to avoid nil dereferencing another issue observed in the past. Additionally we could potentially address #3532, yet the original problem statemenf of that issue stands. As a drive-by optimisation `MarkAsGood` now takes only a `p2p.ID` which makes it interface a bit stricter and leaner. --- p2p/node_info.go | 20 +++++++------------- p2p/pex/addrbook.go | 6 +++--- p2p/pex/addrbook_test.go | 8 ++++---- p2p/pex/pex_reactor.go | 15 ++++++++++++--- p2p/pex/pex_reactor_test.go | 2 +- p2p/switch.go | 4 ++-- p2p/switch_test.go | 2 +- p2p/test_util.go | 2 +- 8 files changed, 31 insertions(+), 28 deletions(-) diff --git a/p2p/node_info.go b/p2p/node_info.go index e80f1e1b7..8195471ed 100644 --- a/p2p/node_info.go +++ b/p2p/node_info.go @@ -24,9 +24,14 @@ func MaxNodeInfoSize() int { // and determines if we're compatible. type NodeInfo interface { ID() ID + nodeInfoAddress nodeInfoTransport } +type nodeInfoAddress interface { + NetAddress() (*NetAddress, error) +} + // nodeInfoTransport validates a nodeInfo and checks // our compatibility with it. It's for use in the handshake. type nodeInfoTransport interface { @@ -209,20 +214,9 @@ OUTER_LOOP: // it includes the authenticated peer ID and the self-reported // ListenAddr. Note that the ListenAddr is not authenticated and // may not match that address actually dialed if its an outbound peer. -func (info DefaultNodeInfo) NetAddress() *NetAddress { +func (info DefaultNodeInfo) NetAddress() (*NetAddress, error) { idAddr := IDAddressString(info.ID(), info.ListenAddr) - netAddr, err := NewNetAddressString(idAddr) - if err != nil { - switch err.(type) { - case ErrNetAddressLookup: - // XXX If the peer provided a host name and the lookup fails here - // we're out of luck. - // TODO: use a NetAddress in DefaultNodeInfo - default: - panic(err) // everything should be well formed by now - } - } - return netAddr + return NewNetAddressString(idAddr) } //----------------------------------------------------------- diff --git a/p2p/pex/addrbook.go b/p2p/pex/addrbook.go index ca788aa66..85dd05248 100644 --- a/p2p/pex/addrbook.go +++ b/p2p/pex/addrbook.go @@ -55,7 +55,7 @@ type AddrBook interface { PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress // Mark address - MarkGood(*p2p.NetAddress) + MarkGood(p2p.ID) MarkAttempt(*p2p.NetAddress) MarkBad(*p2p.NetAddress) @@ -296,11 +296,11 @@ func (a *addrBook) PickAddress(biasTowardsNewAddrs int) *p2p.NetAddress { // MarkGood implements AddrBook - it marks the peer as good and // moves it into an "old" bucket. -func (a *addrBook) MarkGood(addr *p2p.NetAddress) { +func (a *addrBook) MarkGood(id p2p.ID) { a.mtx.Lock() defer a.mtx.Unlock() - ka := a.addrLookup[addr.ID] + ka := a.addrLookup[id] if ka == nil { return } diff --git a/p2p/pex/addrbook_test.go b/p2p/pex/addrbook_test.go index fdcb0c8ad..13bac28c8 100644 --- a/p2p/pex/addrbook_test.go +++ b/p2p/pex/addrbook_test.go @@ -41,7 +41,7 @@ func TestAddrBookPickAddress(t *testing.T) { assert.NotNil(t, addr, "expected an address") // pick an address when we only have old address - book.MarkGood(addrSrc.addr) + book.MarkGood(addrSrc.addr.ID) addr = book.PickAddress(0) assert.NotNil(t, addr, "expected an address") addr = book.PickAddress(50) @@ -126,7 +126,7 @@ func TestAddrBookPromoteToOld(t *testing.T) { // Promote half of them for i, addrSrc := range randAddrs { if i%2 == 0 { - book.MarkGood(addrSrc.addr) + book.MarkGood(addrSrc.addr.ID) } } @@ -330,7 +330,7 @@ func TestAddrBookGetSelectionWithBias(t *testing.T) { randAddrsLen := len(randAddrs) for i, addrSrc := range randAddrs { if int((float64(i)/float64(randAddrsLen))*100) >= 20 { - book.MarkGood(addrSrc.addr) + book.MarkGood(addrSrc.addr.ID) } } @@ -569,7 +569,7 @@ func createAddrBookWithMOldAndNNewAddrs(t *testing.T, nOld, nNew int) (book *add randAddrs := randNetAddressPairs(t, nOld) for _, addr := range randAddrs { book.AddAddress(addr.addr, addr.src) - book.MarkGood(addr.addr) + book.MarkGood(addr.addr.ID) } randAddrs = randNetAddressPairs(t, nNew) diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index cf8cfe6f5..c24ee983c 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -170,12 +170,18 @@ func (r *PEXReactor) AddPeer(p Peer) { } } else { // inbound peer is its own source - addr := p.SocketAddr() + addr, err := p.NodeInfo().NetAddress() + if err != nil { + r.Logger.Error("Failed to get peer NetAddress", "err", err, "peer", p) + return + } + + // Make it explicit that addr and src are the same for an inbound peer. src := addr // add to book. dont RequestAddrs right away because // we don't trust inbound as much - let ensurePeersRoutine handle it. - err := r.book.AddAddress(addr, src) + err = r.book.AddAddress(addr, src) r.logErrAddrBook(err) } } @@ -312,7 +318,10 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { } r.requestsSent.Delete(id) - srcAddr := src.SocketAddr() + srcAddr, err := src.NodeInfo().NetAddress() + if err != nil { + return err + } for _, netAddr := range addrs { // Validate netAddr. Disconnect from a peer if it sends us invalid data. if netAddr == nil { diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index dda60daaa..077f07a60 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -539,7 +539,7 @@ func testCreateSeed(dir string, id int, knownAddrs, srcAddrs []*p2p.NetAddress) book.SetLogger(log.TestingLogger()) for j := 0; j < len(knownAddrs); j++ { book.AddAddress(knownAddrs[j], srcAddrs[j]) - book.MarkGood(knownAddrs[j]) + book.MarkGood(knownAddrs[j].ID) } sw.SetAddrBook(book) diff --git a/p2p/switch.go b/p2p/switch.go index 04caabdc7..afd7d9656 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -48,7 +48,7 @@ type AddrBook interface { AddAddress(addr *NetAddress, src *NetAddress) error AddOurAddress(*NetAddress) OurAddress(*NetAddress) bool - MarkGood(*NetAddress) + MarkGood(ID) RemoveAddress(*NetAddress) HasAddress(*NetAddress) bool Save() @@ -385,7 +385,7 @@ func (sw *Switch) SetAddrBook(addrBook AddrBook) { // like contributed to consensus. func (sw *Switch) MarkPeerAsGood(peer Peer) { if sw.addrBook != nil { - sw.addrBook.MarkGood(peer.SocketAddr()) + sw.addrBook.MarkGood(peer.ID()) } } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index ab8ae9e9f..bf105e0fa 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -626,7 +626,7 @@ func (book *addrBookMock) OurAddress(addr *NetAddress) bool { _, ok := book.ourAddrs[addr.String()] return ok } -func (book *addrBookMock) MarkGood(*NetAddress) {} +func (book *addrBookMock) MarkGood(ID) {} func (book *addrBookMock) HasAddress(addr *NetAddress) bool { _, ok := book.addrs[addr.String()] return ok diff --git a/p2p/test_util.go b/p2p/test_util.go index df60539ba..f8020924c 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -23,7 +23,7 @@ type mockNodeInfo struct { } func (ni mockNodeInfo) ID() ID { return ni.addr.ID } -func (ni mockNodeInfo) NetAddress() *NetAddress { return ni.addr } +func (ni mockNodeInfo) NetAddress() (*NetAddress, error) { return ni.addr, nil } func (ni mockNodeInfo) Validate() error { return nil } func (ni mockNodeInfo) CompatibleWith(other NodeInfo) error { return nil } From 4e4224213f3eaa89abeac6686efe4ac970ad63d0 Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Fri, 12 Apr 2019 12:32:00 +0200 Subject: [PATCH 157/259] adr: Peer Behaviour (#3539) * [adr] ADR 037: Peer Behaviour inital draft * Update docs/architecture/adr-037-peer-behaviour.md Co-Authored-By: brapse * Update docs/architecture/adr-037-peer-behaviour.md Co-Authored-By: brapse * [docs] adr-037 Better footnote styling * [ADR] ADR-037 adjust Footnotes for github markdown * [ADR] ADR-037 fix numbered list --- docs/architecture/adr-037-peer-behaviour.md | 145 ++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 docs/architecture/adr-037-peer-behaviour.md diff --git a/docs/architecture/adr-037-peer-behaviour.md b/docs/architecture/adr-037-peer-behaviour.md new file mode 100644 index 000000000..36b024482 --- /dev/null +++ b/docs/architecture/adr-037-peer-behaviour.md @@ -0,0 +1,145 @@ +# ADR 037: Peer Behaviour Interface + +## Changelog +* 07-03-2019: Initial draft + +## Context + +The responsibility for signaling and acting upon peer behaviour lacks a single +owning component and is heavily coupled with the network stack[1](#references). Reactors +maintain a reference to the `p2p.Switch` which they use to call +`switch.StopPeerForError(...)` when a peer misbehaves and +`switch.MarkAsGood(...)` when a peer contributes in some meaningful way. +While the switch handles `StopPeerForError` internally, the `MarkAsGood` +method delegates to another component, `p2p.AddrBook`. This scheme of delegation +across Switch obscures the responsibility for handling peer behaviour +and ties up the reactors in a larger dependency graph when testing. + +## Decision + +Introduce a `PeerBehaviour` interface and concrete implementations which +provide methods for reactors to signal peer behaviour without direct +coupling `p2p.Switch`. Introduce a ErrPeer to provide +concrete reasons for stopping peers. + +### Implementation Changes + +PeerBehaviour then becomes an interface for signaling peer errors as well +as for marking peers as `good`. + +XXX: It might be better to pass p2p.ID instead of the whole peer but as +a first draft maintain the underlying implementation as much as +possible. + +```go +type PeerBehaviour interface { + Errored(peer Peer, reason ErrPeer) + MarkPeerAsGood(peer Peer) +} +``` + +Instead of signaling peers to stop with arbitrary reasons: +`reason interface{}` + +We introduce a concrete error type ErrPeer: +```go +type ErrPeer int + +const ( + ErrPeerUnknown = iota + ErrPeerBadMessage + ErrPeerMessageOutofOrder + ... +) +``` + +As a first iteration we provide a concrete implementation which wraps +the switch: +```go +type SwitchedPeerBehaviour struct { + sw *Switch +} + +func (spb *SwitchedPeerBehaviour) Errored(peer Peer, reason ErrPeer) { + spb.sw.StopPeerForError(peer, reason) +} + +func (spb *SwitchedPeerBehaviour) MarkPeerAsGood(peer Peer) { + spb.sw.MarkPeerAsGood(peer) +} + +func NewSwitchedPeerBehaviour(sw *Switch) *SwitchedPeerBehaviour { + return &SwitchedPeerBehaviour{ + sw: sw, + } +} +``` + +Reactors, which are often difficult to unit test[2](#references). could use an implementation which exposes the signals produced by the reactor in +manufactured scenarios: + +```go +type PeerErrors map[Peer][]ErrPeer +type GoodPeers map[Peer]bool + +type StorePeerBehaviour struct { + pe PeerErrors + gp GoodPeers +} + +func NewStorePeerBehaviour() *StorePeerBehaviour{ + return &StorePeerBehaviour{ + pe: make(PeerErrors), + gp: GoodPeers{}, + } +} + +func (spb StorePeerBehaviour) Errored(peer Peer, reason ErrPeer) { + if _, ok := spb.pe[peer]; !ok { + spb.pe[peer] = []ErrPeer{reason} + } else { + spb.pe[peer] = append(spb.pe[peer], reason) + } +} + +func (mpb *StorePeerBehaviour) GetPeerErrors() PeerErrors { + return mpb.pe +} + +func (spb *StorePeerBehaviour) MarkPeerAsGood(peer Peer) { + if _, ok := spb.gp[peer]; !ok { + spb.gp[peer] = true + } +} + +func (spb *StorePeerBehaviour) GetGoodPeers() GoodPeers { + return spb.gp +} +``` + +## Status + +Proposed + +## Consequences + +### Positive + + * De-couple signaling from acting upon peer behaviour. + * Reduce the coupling of reactors and the Switch and the network + stack + * The responsibility of managing peer behaviour can be migrated to + a single component instead of split between the switch and the + address book. + +### Negative + + * The first iteration will simply wrap the Switch and introduce a + level of indirection. + +### Neutral + +## References + +1. Issue [#2067](https://github.com/tendermint/tendermint/issues/2067): P2P Refactor +2. PR: [#3506](https://github.com/tendermint/tendermint/pull/3506): ADR 036: Blockchain Reactor Refactor From a453628c4e43c67c747d5078704479aa73716182 Mon Sep 17 00:00:00 2001 From: Martin Dyring-Andersen Date: Fri, 12 Apr 2019 13:25:14 +0200 Subject: [PATCH 158/259] Fix a couple of typos (#3547) Fix some typos in p2p/transport.go --- p2p/transport.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/p2p/transport.go b/p2p/transport.go index 6717db483..ebf77c9f4 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -364,7 +364,7 @@ func (mt *MultiplexTransport) upgrade( if err != nil { return nil, nil, ErrRejected{ conn: c, - err: fmt.Errorf("secrect conn failed: %v", err), + err: fmt.Errorf("secret conn failed: %v", err), isAuthFailure: true, } } @@ -377,7 +377,7 @@ func (mt *MultiplexTransport) upgrade( conn: c, id: connID, err: fmt.Errorf( - "conn.ID (%v) dialed ID (%v) missmatch", + "conn.ID (%v) dialed ID (%v) mismatch", connID, dialedID, ), @@ -409,7 +409,7 @@ func (mt *MultiplexTransport) upgrade( conn: c, id: connID, err: fmt.Errorf( - "conn.ID (%v) NodeInfo.ID (%v) missmatch", + "conn.ID (%v) NodeInfo.ID (%v) mismatch", connID, nodeInfo.ID(), ), From b6da8880c22202a7061ae2093ef01819438a136e Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Fri, 12 Apr 2019 14:24:51 +0200 Subject: [PATCH 159/259] prepare v0.31.4 release: - prep changelog - add missing changelog entries - fix minor glitch in existing changelog (v0.31.2) - bump versions --- CHANGELOG.md | 39 ++++++++++++++++++++++++++++++++++++++- CHANGELOG_PENDING.md | 14 +------------- version/version.go | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 52a926aed..d14f6851c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,42 @@ # Changelog +## v0.31.4 + +*April 12th, 2019* + +This release fixes a regression from v0.31.3 which used the peer's `SocketAddr` to add the peer to +the address book. This swallowed the peer's self-reported port which is important in case of reconnect. +It brings back `NetAddress()` to `NodeInfo` and uses it instead of `SocketAddr` for adding peers. +Additionally, it improves response time on the `/validators` or `/status` RPC endpoints. +As a side-effect it makes these RPC endpoint more difficult to DoS and fixes a performance degradation in `ExecCommitBlock`. + +Special thanks to external contributors on this release: +@brapse, @guagualvcha, @mydring + +### BREAKING CHANGES: + +* Go API + - [p2p] [\#3545](https://github.com/tendermint/tendermint/pull/3545) The `AddrBook` interface method `MarkAsGood` now only takes a `p2p.ID` instead of a `p2p.NetAddress` + - [p2p] [\#3011](https://github.com/tendermint/tendermint/pull/3011) Remove `ListOfKnownAddresses` from the `AddrBook` interface + +### IMPROVEMENTS: +- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer +- [p2p] [\#3547](https://github.com/tendermint/tendermint/pull/3547) Fix a couple of annoying typos (@mdyring) + +### BUG FIXES: + +- [docs] [\#3514](https://github.com/tendermint/tendermint/issues/3514) Fix block.Header.Time description (@melekes) +- [p2p] [\#2716](https://github.com/tendermint/tendermint/issues/2716) Check if we're already connected to peer right before dialing it (@melekes) +- [p2p] [\#3545](https://github.com/tendermint/tendermint/issues/3545) Add back `NetAddress()` to `NodeInfo` and use it instead of peer's `SocketAddr()` when adding a peer to the `PEXReactor` (potential fix for [\#3532](https://github.com/tendermint/tendermint/issues/3532)) +- [state] [\#3438](https://github.com/tendermint/tendermint/pull/3438) + Persist validators every 100000 blocks even if no changes to the set + occurred (@guagualvcha). This + 1) Prevents possible DoS attack using `/validators` or `/status` RPC + endpoints. Before response time was growing linearly with height if no + changes were made to the validator set. + 2) Fixes performance degradation in `ExecCommitBlock` where we call + `LoadValidators` for each `Evidence` in the block. + ## v0.31.3 *April 1st, 2019* @@ -35,7 +72,7 @@ Special thanks to external contributors on this release: * Apps * Go API -- [libs/autofile] [\#3504](https://github.com/tendermint/tendermint/issues/3504) Remove unused code in autofile package. Deleted functions: `Group.Search`, `Group.FindLast`, `GroupReader.ReadLine`, `GroupReader.PushLine`, `MakeSimpleSearchFunc` (@guagualvcha) + - [libs/autofile] [\#3504](https://github.com/tendermint/tendermint/issues/3504) Remove unused code in autofile package. Deleted functions: `Group.Search`, `Group.FindLast`, `GroupReader.ReadLine`, `GroupReader.PushLine`, `MakeSimpleSearchFunc` (@guagualvcha) * Blockchain Protocol diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 33f5f27ce..bcb2af539 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.31.2 +## v0.31.5 ** @@ -17,17 +17,5 @@ ### FEATURES: ### IMPROVEMENTS: -- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer ### BUG FIXES: - -- [state] [\#3438](https://github.com/tendermint/tendermint/pull/3438) - Persist validators every 100000 blocks even if no changes to the set - occurred (@guagualvcha). This - 1) Prevents possible DoS attack using `/validators` or `/status` RPC - endpoints. Before response time was growing linearly with height if no - changes were made to the validator set. - 2) Fixes performance degradation in `ExecCommitBlock` where we call - `LoadValidators` for each `Evidence` in the block. -- [p2p] \#2716 Check if we're already connected to peer right before dialing it (@melekes) -- [docs] \#3514 Fix block.Header.Time description (@melekes) diff --git a/version/version.go b/version/version.go index a42a8f005..9ba38de6c 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.3" + TMCoreSemVer = "0.31.4" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From def5c8cf124ff58cc9c4a61ea017808a0c81c722 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Fri, 12 Apr 2019 16:48:34 +0200 Subject: [PATCH 160/259] address review comments: (#3550) - mention ADR in release summary - remove [p2p] api changes - amend v0.31.3 log to contain note about breaking change --- CHANGELOG.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d14f6851c..057b2e7be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,16 +9,12 @@ the address book. This swallowed the peer's self-reported port which is importan It brings back `NetAddress()` to `NodeInfo` and uses it instead of `SocketAddr` for adding peers. Additionally, it improves response time on the `/validators` or `/status` RPC endpoints. As a side-effect it makes these RPC endpoint more difficult to DoS and fixes a performance degradation in `ExecCommitBlock`. +Also, it contains an [ADR](https://github.com/tendermint/tendermint/pull/3539) that proposes decoupling the +responsibility for peer behaviour from the `p2p.Switch` (by @brapse). Special thanks to external contributors on this release: @brapse, @guagualvcha, @mydring -### BREAKING CHANGES: - -* Go API - - [p2p] [\#3545](https://github.com/tendermint/tendermint/pull/3545) The `AddrBook` interface method `MarkAsGood` now only takes a `p2p.ID` instead of a `p2p.NetAddress` - - [p2p] [\#3011](https://github.com/tendermint/tendermint/pull/3011) Remove `ListOfKnownAddresses` from the `AddrBook` interface - ### IMPROVEMENTS: - [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer - [p2p] [\#3547](https://github.com/tendermint/tendermint/pull/3547) Fix a couple of annoying typos (@mdyring) @@ -45,6 +41,12 @@ This release includes two security sensitive fixes: it ensures generated private keys are valid, and it prevents certain DNS lookups that would cause the node to panic if the lookup failed. +### BREAKING CHANGES: +* Go API + - [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) + The `secp256k1.GenPrivKeySecp256k1` function has changed to guarantee that it returns a valid key, which means it + will return a different private key than in previous versions for the same secret. + ### BUG FIXES: - [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) From f2119c35de4118fbe6b45e578e9b6cc72ad8b3cb Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Mon, 15 Apr 2019 16:38:45 +0200 Subject: [PATCH 161/259] adr: PeerBehaviour updates (#3558) * [adr] Peer behaviour adr updates * [docs] fix Behaved function signature * [adr] typo fix in code example --- ...behaviour.md => adr-039-peer-behaviour.md} | 84 +++++++++++-------- 1 file changed, 49 insertions(+), 35 deletions(-) rename docs/architecture/{adr-037-peer-behaviour.md => adr-039-peer-behaviour.md} (57%) diff --git a/docs/architecture/adr-037-peer-behaviour.md b/docs/architecture/adr-039-peer-behaviour.md similarity index 57% rename from docs/architecture/adr-037-peer-behaviour.md rename to docs/architecture/adr-039-peer-behaviour.md index 36b024482..4ad051a35 100644 --- a/docs/architecture/adr-037-peer-behaviour.md +++ b/docs/architecture/adr-039-peer-behaviour.md @@ -1,7 +1,8 @@ -# ADR 037: Peer Behaviour Interface +# ADR 039: Peer Behaviour Interface ## Changelog * 07-03-2019: Initial draft +* 14-03-2019: Updates from feedback ## Context @@ -19,36 +20,46 @@ and ties up the reactors in a larger dependency graph when testing. Introduce a `PeerBehaviour` interface and concrete implementations which provide methods for reactors to signal peer behaviour without direct -coupling `p2p.Switch`. Introduce a ErrPeer to provide -concrete reasons for stopping peers. +coupling `p2p.Switch`. Introduce a ErrorBehaviourPeer to provide +concrete reasons for stopping peers. Introduce GoodBehaviourPeer to provide +concrete ways in which a peer contributes. ### Implementation Changes PeerBehaviour then becomes an interface for signaling peer errors as well as for marking peers as `good`. -XXX: It might be better to pass p2p.ID instead of the whole peer but as -a first draft maintain the underlying implementation as much as -possible. - ```go type PeerBehaviour interface { - Errored(peer Peer, reason ErrPeer) - MarkPeerAsGood(peer Peer) + Behaved(peer Peer, reason GoodBehaviourPeer) + Errored(peer Peer, reason ErrorBehaviourPeer) } ``` Instead of signaling peers to stop with arbitrary reasons: `reason interface{}` -We introduce a concrete error type ErrPeer: +We introduce a concrete error type ErrorBehaviourPeer: ```go -type ErrPeer int +type ErrorBehaviourPeer int const ( - ErrPeerUnknown = iota - ErrPeerBadMessage - ErrPeerMessageOutofOrder + ErrorBehaviourUnknown = iota + ErrorBehaviourBadMessage + ErrorBehaviourMessageOutofOrder + ... +) +``` + +To provide additional information on the ways a peer contributed, we introduce +the GoodBehaviourPeer type. + +```go +type GoodBehaviourPeer int + +const ( + GoodBehaviourVote = iota + GoodBehaviourBlockPart ... ) ``` @@ -60,11 +71,11 @@ type SwitchedPeerBehaviour struct { sw *Switch } -func (spb *SwitchedPeerBehaviour) Errored(peer Peer, reason ErrPeer) { +func (spb *SwitchedPeerBehaviour) Errored(peer Peer, reason ErrorBehaviourPeer) { spb.sw.StopPeerForError(peer, reason) } -func (spb *SwitchedPeerBehaviour) MarkPeerAsGood(peer Peer) { +func (spb *SwitchedPeerBehaviour) Behaved(peer Peer, reason GoodBehaviourPeer) { spb.sw.MarkPeerAsGood(peer) } @@ -75,51 +86,54 @@ func NewSwitchedPeerBehaviour(sw *Switch) *SwitchedPeerBehaviour { } ``` -Reactors, which are often difficult to unit test[2](#references). could use an implementation which exposes the signals produced by the reactor in +Reactors, which are often difficult to unit test[2](#references) could use an implementation which exposes the signals produced by the reactor in manufactured scenarios: ```go -type PeerErrors map[Peer][]ErrPeer -type GoodPeers map[Peer]bool +type ErrorBehaviours map[Peer][]ErrorBehaviourPeer +type GoodBehaviours map[Peer][]GoodBehaviourPeer type StorePeerBehaviour struct { - pe PeerErrors - gp GoodPeers + eb ErrorBehaviours + gb GoodBehaviours } func NewStorePeerBehaviour() *StorePeerBehaviour{ return &StorePeerBehaviour{ - pe: make(PeerErrors), - gp: GoodPeers{}, + eb: make(ErrorBehaviours), + gb: make(GoodBehaviours), } } -func (spb StorePeerBehaviour) Errored(peer Peer, reason ErrPeer) { - if _, ok := spb.pe[peer]; !ok { - spb.pe[peer] = []ErrPeer{reason} +func (spb StorePeerBehaviour) Errored(peer Peer, reason ErrorBehaviourPeer) { + if _, ok := spb.eb[peer]; !ok { + spb.eb[peer] = []ErrorBehaviours{reason} } else { - spb.pe[peer] = append(spb.pe[peer], reason) + spb.eb[peer] = append(spb.eb[peer], reason) } } -func (mpb *StorePeerBehaviour) GetPeerErrors() PeerErrors { - return mpb.pe +func (mpb *StorePeerBehaviour) GetErrored() ErrorBehaviours { + return mpb.eb } -func (spb *StorePeerBehaviour) MarkPeerAsGood(peer Peer) { - if _, ok := spb.gp[peer]; !ok { - spb.gp[peer] = true + +func (spb StorePeerBehaviour) Behaved(peer Peer, reason GoodBehaviourPeer) { + if _, ok := spb.gb[peer]; !ok { + spb.gb[peer] = []GoodBehaviourPeer{reason} + } else { + spb.gb[peer] = append(spb.gb[peer], reason) } } -func (spb *StorePeerBehaviour) GetGoodPeers() GoodPeers { - return spb.gp +func (spb *StorePeerBehaviour) GetBehaved() GoodBehaviours { + return spb.gb } ``` ## Status -Proposed +Accepted ## Consequences From 50b87c344503d07a12372b2fffece3ed66d915fe Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 15 Apr 2019 18:53:38 +0400 Subject: [PATCH 162/259] state: Use last height changed if validator set is empty (#3560) What happened: New code was supposed to fall back to last height changed when/if it failed to find validators at checkpoint height (to make release non-breaking). But because we did not check if validator set is empty, the fall back logic was never executed => resulting in LoadValidators returning an empty validator set for cases where `lastStoredHeight` is checkpoint height (i.e. almost all heights if the application does not change validator set often). How it was found: one of our users - @sunboshan reported a bug here https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833 * use last height changed in validator set is empty * add a changelog entry --- CHANGELOG_PENDING.md | 1 + state/store.go | 4 ++-- state/store_test.go | 34 +++++++++++++++++++++++++--------- 3 files changed, 28 insertions(+), 11 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index bcb2af539..36c64be5e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -19,3 +19,4 @@ ### IMPROVEMENTS: ### BUG FIXES: +- [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set diff --git a/state/store.go b/state/store.go index 73116b43f..0301bc7c3 100644 --- a/state/store.go +++ b/state/store.go @@ -193,7 +193,7 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { if valInfo.ValidatorSet == nil { lastStoredHeight := lastStoredHeightFor(height, valInfo.LastHeightChanged) valInfo2 := loadValidatorsInfo(db, lastStoredHeight) - if valInfo2 == nil { + if valInfo2 == nil || valInfo2.ValidatorSet == nil { // TODO (melekes): remove the below if condition in the 0.33 major // release and just panic. Old chains might panic otherwise if they // haven't saved validators at intermediate (%valSetCheckpointInterval) @@ -201,7 +201,7 @@ func LoadValidators(db dbm.DB, height int64) (*types.ValidatorSet, error) { // https://github.com/tendermint/tendermint/issues/3543 valInfo2 = loadValidatorsInfo(db, valInfo.LastHeightChanged) lastStoredHeight = valInfo.LastHeightChanged - if valInfo2 == nil { + if valInfo2 == nil || valInfo2.ValidatorSet == nil { panic( fmt.Sprintf("Couldn't find validators at height %d (height %d was originally requested)", lastStoredHeight, diff --git a/state/store_test.go b/state/store_test.go index dd48cae71..06adeefab 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -6,34 +6,50 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" cfg "github.com/tendermint/tendermint/config" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" ) -func TestSaveValidatorsInfo(t *testing.T) { - // test we persist validators every valSetCheckpointInterval blocks +func TestStoreLoadValidators(t *testing.T) { stateDB := dbm.NewMemDB() val, _ := types.RandValidator(true, 10) vals := types.NewValidatorSet([]*types.Validator{val}) - // TODO(melekes): remove in 0.33 release - // https://github.com/tendermint/tendermint/issues/3543 + // 1) LoadValidators loads validators using a height where they were last changed saveValidatorsInfo(stateDB, 1, 1, vals) saveValidatorsInfo(stateDB, 2, 1, vals) + loadedVals, err := LoadValidators(stateDB, 2) + require.NoError(t, err) + assert.NotZero(t, loadedVals.Size()) + + // 2) LoadValidators loads validators using a checkpoint height + + // TODO(melekes): REMOVE in 0.33 release + // https://github.com/tendermint/tendermint/issues/3543 + // for releases prior to v0.31.4, it uses last height changed + valInfo := &ValidatorsInfo{ + LastHeightChanged: valSetCheckpointInterval, + } + stateDB.Set(calcValidatorsKey(valSetCheckpointInterval), valInfo.Bytes()) assert.NotPanics(t, func() { - _, err := LoadValidators(stateDB, 2) + saveValidatorsInfo(stateDB, valSetCheckpointInterval+1, 1, vals) + loadedVals, err := LoadValidators(stateDB, valSetCheckpointInterval+1) if err != nil { - panic(err) + t.Fatal(err) + } + if loadedVals.Size() == 0 { + t.Fatal("Expected validators to be non-empty") } }) - //ENDREMOVE + // ENDREMOVE saveValidatorsInfo(stateDB, valSetCheckpointInterval, 1, vals) - loadedVals, err := LoadValidators(stateDB, valSetCheckpointInterval) - assert.NoError(t, err) + loadedVals, err = LoadValidators(stateDB, valSetCheckpointInterval) + require.NoError(t, err) assert.NotZero(t, loadedVals.Size()) } From f1cf10150a1a75832cb2c1fd3679d5beef25884f Mon Sep 17 00:00:00 2001 From: dongsamb Date: Tue, 16 Apr 2019 13:49:03 +0900 Subject: [PATCH 163/259] gitignore: add .vendor-new (#3566) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1cf9cdb90..10ee3099c 100644 --- a/.gitignore +++ b/.gitignore @@ -35,6 +35,7 @@ shunit2 addrbook.json */vendor +.vendor-new/ */.glide .terraform terraform.tfstate From 439312b9c0ac6a21cf9a0d08589b512c017954e1 Mon Sep 17 00:00:00 2001 From: zjubfd Date: Tue, 16 Apr 2019 15:54:19 +0800 Subject: [PATCH 164/259] blockchain: dismiss request channel delay (#3459) Fixes #3457 The topic of the issue is that : write a BlockRequest int requestsCh channel will create an timer at the same time that stop the peer 15s later if no block have been received . But pop a BlockRequest from requestsCh and send it out may delay more than 15s later. So that the peer will be stopped for error("send nothing to us"). Extracting requestsCh into its own goroutine can make sure that every BlockRequest been handled timely. Instead of the requestsCh handling, we should probably pull the didProcessCh handling in a separate go routine since this is the one "starving" the other channel handlers. I believe the way it is right now, we still have issues with high delays in errorsCh handling that might cause sending requests to invalid/ disconnected peers. --- blockchain/reactor.go | 55 +++++++++++++++++------------- libs/common/throttle_timer_test.go | 1 + libs/db/mem_batch.go | 4 +-- 3 files changed, 33 insertions(+), 27 deletions(-) diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 4a3f90493..139393778 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -228,32 +228,40 @@ func (bcR *BlockchainReactor) poolRoutine() { didProcessCh := make(chan struct{}, 1) + go func() { + for { + select { + case <-bcR.Quit(): + return + case <-bcR.pool.Quit(): + return + case request := <-bcR.requestsCh: + peer := bcR.Switch.Peers().Get(request.PeerID) + if peer == nil { + continue + } + msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{request.Height}) + queued := peer.TrySend(BlockchainChannel, msgBytes) + if !queued { + bcR.Logger.Debug("Send queue is full, drop block request", "peer", peer.ID(), "height", request.Height) + } + case err := <-bcR.errorsCh: + peer := bcR.Switch.Peers().Get(err.peerID) + if peer != nil { + bcR.Switch.StopPeerForError(peer, err) + } + + case <-statusUpdateTicker.C: + // ask for status updates + go bcR.BroadcastStatusRequest() // nolint: errcheck + + } + } + }() + FOR_LOOP: for { select { - case request := <-bcR.requestsCh: - peer := bcR.Switch.Peers().Get(request.PeerID) - if peer == nil { - continue FOR_LOOP // Peer has since been disconnected. - } - msgBytes := cdc.MustMarshalBinaryBare(&bcBlockRequestMessage{request.Height}) - queued := peer.TrySend(BlockchainChannel, msgBytes) - if !queued { - // We couldn't make the request, send-queue full. - // The pool handles timeouts, just let it go. - continue FOR_LOOP - } - - case err := <-bcR.errorsCh: - peer := bcR.Switch.Peers().Get(err.peerID) - if peer != nil { - bcR.Switch.StopPeerForError(peer, err) - } - - case <-statusUpdateTicker.C: - // ask for status updates - go bcR.BroadcastStatusRequest() // nolint: errcheck - case <-switchToConsensusTicker.C: height, numPending, lenRequesters := bcR.pool.GetStatus() outbound, inbound, _ := bcR.Switch.NumPeers() @@ -262,7 +270,6 @@ FOR_LOOP: if bcR.pool.IsCaughtUp() { bcR.Logger.Info("Time to switch to consensus reactor!", "height", height) bcR.pool.Stop() - conR, ok := bcR.Switch.Reactor("CONSENSUS").(consensusReactor) if ok { conR.SwitchToConsensus(state, blocksSynced) diff --git a/libs/common/throttle_timer_test.go b/libs/common/throttle_timer_test.go index 00f5abdec..f5c9dfeff 100644 --- a/libs/common/throttle_timer_test.go +++ b/libs/common/throttle_timer_test.go @@ -6,6 +6,7 @@ import ( "time" // make govet noshadow happy... + asrt "github.com/stretchr/testify/assert" ) diff --git a/libs/db/mem_batch.go b/libs/db/mem_batch.go index ebba43f54..2ce765786 100644 --- a/libs/db/mem_batch.go +++ b/libs/db/mem_batch.go @@ -1,8 +1,6 @@ package db -import ( - "sync" -) +import "sync" type atomicSetDeleter interface { Mutex() *sync.Mutex From 5b8888b01b7542cd6dd856e7a322f3d4e5ccba16 Mon Sep 17 00:00:00 2001 From: hucc Date: Tue, 16 Apr 2019 16:04:09 +0800 Subject: [PATCH 165/259] common: CMap: slight optimization in Keys() and Values(). (#3567) --- libs/common/cmap.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/common/cmap.go b/libs/common/cmap.go index 2f7720d2e..d87adb762 100644 --- a/libs/common/cmap.go +++ b/libs/common/cmap.go @@ -56,7 +56,7 @@ func (cm *CMap) Clear() { func (cm *CMap) Keys() []string { cm.l.Lock() - keys := []string{} + keys := make([]string, 0, len(cm.m)) for k := range cm.m { keys = append(keys, k) } @@ -66,7 +66,7 @@ func (cm *CMap) Keys() []string { func (cm *CMap) Values() []interface{} { cm.l.Lock() - items := []interface{}{} + items := make([]interface{}, 0, len(cm.m)) for _, v := range cm.m { items = append(items, v) } From 3cb7013c3886f8144e8791908c9c07e3072b357c Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 16 Apr 2019 13:33:51 +0400 Subject: [PATCH 166/259] update changelog --- CHANGELOG.md | 32 ++++++++++++++++++++++++++++---- CHANGELOG_PENDING.md | 3 +-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 057b2e7be..161514778 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,28 @@ # Changelog +## v0.31.5 + +*April 16th, 2019* + +This release fixes a regression from v0.31.4 where, in existing chains that +were upgraded, `/validators` could return an empty validator set. This is true +for almost all heights given the validator set remains the same. + +Special thanks to external contributors on this release: +@brapse, @guagualvcha, @dongsam, @phucc + +### IMPROVEMENTS: + +- [libs/common] CMap: slight optimization in Keys() and Values() (@phucc) +- [gitignore] gitignore: add .vendor-new (@dongsam) + +### BUG FIXES: + +- [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) + LoadValidators: do not return an empty validator set +- [blockchain] [\#3457](https://github.com/tendermint/tendermint/issues/3457) + Fix "peer did not send us anything" in `fast_sync` mode when under high pressure + ## v0.31.4 *April 12th, 2019* @@ -9,13 +32,14 @@ the address book. This swallowed the peer's self-reported port which is importan It brings back `NetAddress()` to `NodeInfo` and uses it instead of `SocketAddr` for adding peers. Additionally, it improves response time on the `/validators` or `/status` RPC endpoints. As a side-effect it makes these RPC endpoint more difficult to DoS and fixes a performance degradation in `ExecCommitBlock`. -Also, it contains an [ADR](https://github.com/tendermint/tendermint/pull/3539) that proposes decoupling the -responsibility for peer behaviour from the `p2p.Switch` (by @brapse). +Also, it contains an [ADR](https://github.com/tendermint/tendermint/pull/3539) that proposes decoupling the +responsibility for peer behaviour from the `p2p.Switch` (by @brapse). Special thanks to external contributors on this release: @brapse, @guagualvcha, @mydring ### IMPROVEMENTS: + - [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer - [p2p] [\#3547](https://github.com/tendermint/tendermint/pull/3547) Fix a couple of annoying typos (@mdyring) @@ -43,8 +67,8 @@ panic if the lookup failed. ### BREAKING CHANGES: * Go API - - [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) - The `secp256k1.GenPrivKeySecp256k1` function has changed to guarantee that it returns a valid key, which means it + - [crypto/secp256k1] [\#3439](https://github.com/tendermint/tendermint/issues/3439) + The `secp256k1.GenPrivKeySecp256k1` function has changed to guarantee that it returns a valid key, which means it will return a different private key than in previous versions for the same secret. ### BUG FIXES: diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 36c64be5e..fae9e6568 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.31.5 +## v0.31.6 ** @@ -19,4 +19,3 @@ ### IMPROVEMENTS: ### BUG FIXES: -- [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set From 4474a5ec70fad39d78b28a543d72c25715bfca84 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 16 Apr 2019 13:34:01 +0400 Subject: [PATCH 167/259] bump version --- version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version/version.go b/version/version.go index 9ba38de6c..a7559d499 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.4" + TMCoreSemVer = "0.31.5" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From 18bd5b627a99927877a92b50bee705aacf913b63 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Tue, 16 Apr 2019 15:13:30 +0400 Subject: [PATCH 168/259] Apply suggestions from code review Co-Authored-By: melekes --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 161514778..9d498060b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,20 +6,20 @@ This release fixes a regression from v0.31.4 where, in existing chains that were upgraded, `/validators` could return an empty validator set. This is true -for almost all heights given the validator set remains the same. +for almost all heights, given the validator set remains the same. Special thanks to external contributors on this release: @brapse, @guagualvcha, @dongsam, @phucc ### IMPROVEMENTS: -- [libs/common] CMap: slight optimization in Keys() and Values() (@phucc) +- [libs/common] `CMap`: slight optimization in `Keys()` and `Values()` (@phucc) - [gitignore] gitignore: add .vendor-new (@dongsam) ### BUG FIXES: - [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) - LoadValidators: do not return an empty validator set + `LoadValidators`: do not return an empty validator set - [blockchain] [\#3457](https://github.com/tendermint/tendermint/issues/3457) Fix "peer did not send us anything" in `fast_sync` mode when under high pressure From c0e8fb5085c6f137681616688808881530c9ec86 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 17 Apr 2019 16:44:26 +0400 Subject: [PATCH 169/259] p2p: (seed mode) limit the number of attempts to connect to a peer (#3573) * use dialPeer function in a seed mode Fixes #3532 by storing a number of attempts we've tried to connect in-memory and removing the address from addrbook when number of attempts > 16 --- CHANGELOG_PENDING.md | 2 + p2p/pex/pex_reactor.go | 79 +++++++++++++++++++++++++------------ p2p/pex/pex_reactor_test.go | 28 +++++++++++++ 3 files changed, 84 insertions(+), 25 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 36c64be5e..d628bc598 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,3 +20,5 @@ ### BUG FIXES: - [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set +- [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode + to 16 (as a result, the node will stop retrying after a 35 hours time window) diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index c24ee983c..38befe29a 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -48,6 +48,24 @@ const ( biasToSelectNewPeers = 30 // 70 to select good peers ) +type errMaxAttemptsToDial struct { +} + +func (e errMaxAttemptsToDial) Error() string { + return fmt.Sprintf("reached max attempts %d to dial", maxAttemptsToDial) +} + +type errTooEarlyToDial struct { + backoffDuration time.Duration + lastDialed time.Time +} + +func (e errTooEarlyToDial) Error() string { + return fmt.Sprintf( + "too early to dial (backoff duration: %d, last dialed: %v, time since: %v)", + e.backoffDuration, e.lastDialed, time.Since(e.lastDialed)) +} + // PEXReactor handles PEX (peer exchange) and ensures that an // adequate number of peers are connected to the switch. // @@ -449,7 +467,19 @@ func (r *PEXReactor) ensurePeers() { // Dial picked addresses for _, addr := range toDial { - go r.dialPeer(addr) + go func(addr *p2p.NetAddress) { + err := r.dialPeer(addr) + if err != nil { + switch err.(type) { + case errMaxAttemptsToDial: + r.Logger.Debug(err.Error(), "addr", addr) + case errTooEarlyToDial: + r.Logger.Debug(err.Error(), "addr", addr) + default: + r.Logger.Error(err.Error(), "addr", addr) + } + } + }(addr) } // If we need more addresses, pick a random peer and ask for more. @@ -479,17 +509,16 @@ func (r *PEXReactor) dialAttemptsInfo(addr *p2p.NetAddress) (attempts int, lastD return atd.number, atd.lastDialed } -func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { +func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) error { attempts, lastDialed := r.dialAttemptsInfo(addr) if attempts > maxAttemptsToDial { - // Do not log the message if the addr gets readded. - if attempts+1 == maxAttemptsToDial { - r.Logger.Info("Reached max attempts to dial", "addr", addr, "attempts", attempts) - r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) - } + // TODO(melekes): have a blacklist in the addrbook with peers whom we've + // failed to connect to. Then we can clean up attemptsToDial, which acts as + // a blacklist currently. + // https://github.com/tendermint/tendermint/issues/3572 r.book.MarkBad(addr) - return + return errMaxAttemptsToDial{} } // exponential backoff if it's not our first attempt to dial given address @@ -498,33 +527,31 @@ func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) { backoffDuration := jitterSeconds + ((1 << uint(attempts)) * time.Second) sinceLastDialed := time.Since(lastDialed) if sinceLastDialed < backoffDuration { - r.Logger.Debug("Too early to dial", "addr", addr, "backoff_duration", backoffDuration, "last_dialed", lastDialed, "time_since", sinceLastDialed) - return + return errTooEarlyToDial{backoffDuration, lastDialed} } } err := r.Switch.DialPeerWithAddress(addr, false) + if err != nil { if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { - return + return err } - r.Logger.Error("Dialing failed", "addr", addr, "err", err, "attempts", attempts) markAddrInBookBasedOnErr(addr, r.book, err) - if _, ok := err.(p2p.ErrSwitchAuthenticationFailure); ok { + switch err.(type) { + case p2p.ErrSwitchAuthenticationFailure: + // NOTE: addr is removed from addrbook in markAddrInBookBasedOnErr r.attemptsToDial.Delete(addr.DialString()) - } else { - // FIXME: if the addr is going to be removed from the addrbook (hard to - // tell at this point), we need to Delete it from attemptsToDial, not - // record another attempt. - // record attempt + default: r.attemptsToDial.Store(addr.DialString(), _attemptsToDial{attempts + 1, time.Now()}) } - return + return errors.Wrapf(err, "dialing failed (attempts: %d)", attempts+1) } // cleanup any history r.attemptsToDial.Delete(addr.DialString()) + return nil } // checkSeeds checks that addresses are well formed. @@ -633,14 +660,16 @@ func (r *PEXReactor) crawlPeers(addrs []*p2p.NetAddress) { LastCrawled: now, } - err := r.Switch.DialPeerWithAddress(addr, false) + err := r.dialPeer(addr) if err != nil { - if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { - continue + switch err.(type) { + case errMaxAttemptsToDial: + r.Logger.Debug(err.Error(), "addr", addr) + case errTooEarlyToDial: + r.Logger.Debug(err.Error(), "addr", addr) + default: + r.Logger.Error(err.Error(), "addr", addr) } - - r.Logger.Error("Dialing failed", "addr", addr, "err", err) - markAddrInBookBasedOnErr(addr, r.book, err) continue } diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 077f07a60..6572a5f61 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -322,6 +322,34 @@ func TestPEXReactorSeedMode(t *testing.T) { assert.Equal(t, 0, sw.Peers().Size()) } +func TestPEXReactorDialsPeerUpToMaxAttemptsInSeedMode(t *testing.T) { + // directory to store address books + dir, err := ioutil.TempDir("", "pex_reactor") + require.Nil(t, err) + defer os.RemoveAll(dir) // nolint: errcheck + + pexR, book := createReactor(&PEXReactorConfig{SeedMode: true}) + defer teardownReactor(book) + + sw := createSwitchAndAddReactors(pexR) + sw.SetAddrBook(book) + err = sw.Start() + require.NoError(t, err) + defer sw.Stop() + + peer := mock.NewPeer(nil) + addr := peer.SocketAddr() + + err = book.AddAddress(addr, addr) + require.NoError(t, err) + + assert.True(t, book.HasAddress(addr)) + // imitate maxAttemptsToDial reached + pexR.attemptsToDial.Store(addr.DialString(), _attemptsToDial{maxAttemptsToDial + 1, time.Now()}) + pexR.crawlPeers([]*p2p.NetAddress{addr}) + assert.False(t, book.HasAddress(addr)) +} + // connect a peer to a seed, wait a bit, then stop it. // this should give it time to request addrs and for the seed // to call FlushStop, and allows us to test calling Stop concurrently From 621c0e629d3aa80ac45ffc92c20709bea6702b32 Mon Sep 17 00:00:00 2001 From: kevlubkcm <36485490+kevlubkcm@users.noreply.github.com> Date: Wed, 17 Apr 2019 11:09:17 -0400 Subject: [PATCH 170/259] docs: fix typo in clist readme (#3574) --- libs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/README.md b/libs/README.md index 9ea618dbd..b1bb0396c 100644 --- a/libs/README.md +++ b/libs/README.md @@ -13,7 +13,7 @@ CLI wraps the `cobra` and `viper` packages and handles some common elements of b ## clist -Clist provides a linekd list that is safe for concurrent access by many readers. +Clist provides a linked list that is safe for concurrent access by many readers. ## common From 90465f727f122367f4bf1f808e3e352fd1b5d11c Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Wed, 17 Apr 2019 11:10:12 -0400 Subject: [PATCH 171/259] rpc: add support for batched requests/responses (#3534) Continues from #3280 in building support for batched requests/responses in the JSON RPC (as per issue #3213). * Add JSON RPC batching for client and server As per #3213, this adds support for [JSON RPC batch requests and responses](https://www.jsonrpc.org/specification#batch). * Add additional checks to ensure client responses are the same as results * Fix case where a notification is sent and no response is expected * Add test to check that JSON RPC notifications in a batch are left out in responses * Update CHANGELOG_PENDING.md * Update PR number now that PR has been created * Make errors start with lowercase letter * Refactor batch functionality to be standalone This refactors the batching functionality to rather act in a standalone way. In light of supporting concurrent goroutines making use of the same client, it would make sense to have batching functionality where one could create a batch of requests per goroutine and send that batch without interfering with a batch from another goroutine. * Add examples for simple and batch HTTP client usage * Check errors from writer and remove nolinter directives * Make error strings start with lowercase letter * Refactor examples to make them testable * Use safer deferred shutdown for example Tendermint test node * Recompose rpcClient interface from pre-existing interface components * Rename WaitGroup for brevity * Replace empty ID string with request ID * Remove extraneous test case * Convert first letter of errors.Wrap() messages to lowercase * Remove extraneous function parameter * Make variable declaration terse * Reorder WaitGroup.Done call to help prevent race conditions in the face of failure * Swap mutex to value representation and remove initialization * Restore empty JSONRPC string ID in response to prevent nil * Make JSONRPCBufferedRequest private * Revert PR hard link in CHANGELOG_PENDING * Add client ID for JSONRPCClient This adds code to automatically generate a randomized client ID for the JSONRPCClient, and adds a check of the IDs in the responses (if one was set in the requests). * Extract response ID validation into separate function * Remove extraneous comments * Reorder fields to indicate clearly which are protected by the mutex * Refactor for loop to remove indexing * Restructure and combine loop * Flatten conditional block for better readability * Make multi-variable declaration slightly more readable * Change for loop style * Compress error check statements * Make function description more generic to show that we support different protocols * Preallocate memory for request and result objects --- CHANGELOG_PENDING.md | 1 + rpc/client/examples_test.go | 126 +++++++++++++++++++++ rpc/client/helpers.go | 2 +- rpc/client/httpclient.go | 187 +++++++++++++++++++++++--------- rpc/client/rpc_test.go | 100 +++++++++++++++++ rpc/lib/client/http_client.go | 186 +++++++++++++++++++++++++++++-- rpc/lib/server/handlers.go | 100 +++++++++-------- rpc/lib/server/handlers_test.go | 66 +++++++++++ rpc/lib/server/http_params.go | 2 +- rpc/lib/server/http_server.go | 33 +++++- rpc/test/helpers.go | 77 +++++++++---- 11 files changed, 747 insertions(+), 133 deletions(-) create mode 100644 rpc/client/examples_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d628bc598..3f151f71a 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -17,6 +17,7 @@ ### FEATURES: ### IMPROVEMENTS: +- [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC ### BUG FIXES: - [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set diff --git a/rpc/client/examples_test.go b/rpc/client/examples_test.go new file mode 100644 index 000000000..720e48492 --- /dev/null +++ b/rpc/client/examples_test.go @@ -0,0 +1,126 @@ +package client_test + +import ( + "bytes" + "fmt" + + "github.com/tendermint/tendermint/abci/example/kvstore" + "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" + rpctest "github.com/tendermint/tendermint/rpc/test" +) + +func ExampleHTTP_simple() { + // Start a tendermint node (and kvstore) in the background to test against + app := kvstore.NewKVStoreApplication() + node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig) + defer rpctest.StopTendermint(node) + + // Create our RPC client + rpcAddr := rpctest.GetConfig().RPC.ListenAddress + c := client.NewHTTP(rpcAddr, "/websocket") + + // Create a transaction + k := []byte("name") + v := []byte("satoshi") + tx := append(k, append([]byte("="), v...)...) + + // Broadcast the transaction and wait for it to commit (rather use + // c.BroadcastTxSync though in production) + bres, err := c.BroadcastTxCommit(tx) + if err != nil { + panic(err) + } + if bres.CheckTx.IsErr() || bres.DeliverTx.IsErr() { + panic("BroadcastTxCommit transaction failed") + } + + // Now try to fetch the value for the key + qres, err := c.ABCIQuery("/key", k) + if err != nil { + panic(err) + } + if qres.Response.IsErr() { + panic("ABCIQuery failed") + } + if !bytes.Equal(qres.Response.Key, k) { + panic("returned key does not match queried key") + } + if !bytes.Equal(qres.Response.Value, v) { + panic("returned value does not match sent value") + } + + fmt.Println("Sent tx :", string(tx)) + fmt.Println("Queried for :", string(qres.Response.Key)) + fmt.Println("Got value :", string(qres.Response.Value)) + + // Output: + // Sent tx : name=satoshi + // Queried for : name + // Got value : satoshi +} + +func ExampleHTTP_batching() { + // Start a tendermint node (and kvstore) in the background to test against + app := kvstore.NewKVStoreApplication() + node := rpctest.StartTendermint(app, rpctest.SuppressStdout, rpctest.RecreateConfig) + defer rpctest.StopTendermint(node) + + // Create our RPC client + rpcAddr := rpctest.GetConfig().RPC.ListenAddress + c := client.NewHTTP(rpcAddr, "/websocket") + + // Create our two transactions + k1 := []byte("firstName") + v1 := []byte("satoshi") + tx1 := append(k1, append([]byte("="), v1...)...) + + k2 := []byte("lastName") + v2 := []byte("nakamoto") + tx2 := append(k2, append([]byte("="), v2...)...) + + txs := [][]byte{tx1, tx2} + + // Create a new batch + batch := c.NewBatch() + + // Queue up our transactions + for _, tx := range txs { + if _, err := batch.BroadcastTxCommit(tx); err != nil { + panic(err) + } + } + + // Send the batch of 2 transactions + if _, err := batch.Send(); err != nil { + panic(err) + } + + // Now let's query for the original results as a batch + keys := [][]byte{k1, k2} + for _, key := range keys { + if _, err := batch.ABCIQuery("/key", key); err != nil { + panic(err) + } + } + + // Send the 2 queries and keep the results + results, err := batch.Send() + if err != nil { + panic(err) + } + + // Each result in the returned list is the deserialized result of each + // respective ABCIQuery response + for _, result := range results { + qr, ok := result.(*ctypes.ResultABCIQuery) + if !ok { + panic("invalid result type from ABCIQuery request") + } + fmt.Println(string(qr.Response.Key), "=", string(qr.Response.Value)) + } + + // Output: + // firstName = satoshi + // lastName = nakamoto +} diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go index 4889b0740..756ba2818 100644 --- a/rpc/client/helpers.go +++ b/rpc/client/helpers.go @@ -15,7 +15,7 @@ type Waiter func(delta int64) (abort error) // but you can plug in another one func DefaultWaitStrategy(delta int64) (abort error) { if delta > 10 { - return errors.Errorf("Waiting for %d blocks... aborting", delta) + return errors.Errorf("waiting for %d blocks... aborting", delta) } else if delta > 0 { // estimate of wait time.... // wait half a second for the next block (in progress) diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go index 55c7b4f17..3fd13da37 100644 --- a/rpc/client/httpclient.go +++ b/rpc/client/httpclient.go @@ -18,27 +18,72 @@ import ( ) /* -HTTP is a Client implementation that communicates with a tendermint node over -json rpc and websockets. +HTTP is a Client implementation that communicates with a Tendermint node over +JSON RPC and WebSockets. This is the main implementation you probably want to use in production code. -There are other implementations when calling the tendermint node in-process +There are other implementations when calling the Tendermint node in-process (Local), or when you want to mock out the server for test code (mock). You can subscribe for any event published by Tendermint using Subscribe method. -Note delivery is best-effort. If you don't read events fast enough or network -is slow, Tendermint might cancel the subscription. The client will attempt to +Note delivery is best-effort. If you don't read events fast enough or network is +slow, Tendermint might cancel the subscription. The client will attempt to resubscribe (you don't need to do anything). It will keep trying every second indefinitely until successful. + +Request batching is available for JSON RPC requests over HTTP, which conforms to +the JSON RPC specification (https://www.jsonrpc.org/specification#batch). See +the example for more details. */ type HTTP struct { remote string rpc *rpcclient.JSONRPCClient + + *baseRPCClient *WSEvents } -// NewHTTP takes a remote endpoint in the form tcp://: -// and the websocket path (which always seems to be "/websocket") +// BatchHTTP provides the same interface as `HTTP`, but allows for batching of +// requests (as per https://www.jsonrpc.org/specification#batch). Do not +// instantiate directly - rather use the HTTP.NewBatch() method to create an +// instance of this struct. +// +// Batching of HTTP requests is thread-safe in the sense that multiple +// goroutines can each create their own batches and send them using the same +// HTTP client. Multiple goroutines could also enqueue transactions in a single +// batch, but ordering of transactions in the batch cannot be guaranteed in such +// an example. +type BatchHTTP struct { + rpcBatch *rpcclient.JSONRPCRequestBatch + *baseRPCClient +} + +// rpcClient is an internal interface to which our RPC clients (batch and +// non-batch) must conform. Acts as an additional code-level sanity check to +// make sure the implementations stay coherent. +type rpcClient interface { + ABCIClient + HistoryClient + NetworkClient + SignClient + StatusClient +} + +// baseRPCClient implements the basic RPC method logic without the actual +// underlying RPC call functionality, which is provided by `caller`. +type baseRPCClient struct { + caller rpcclient.JSONRPCCaller +} + +var _ rpcClient = (*HTTP)(nil) +var _ rpcClient = (*BatchHTTP)(nil) +var _ rpcClient = (*baseRPCClient)(nil) + +//----------------------------------------------------------------------------- +// HTTP + +// NewHTTP takes a remote endpoint in the form ://: and +// the websocket path (which always seems to be "/websocket") func NewHTTP(remote, wsEndpoint string) *HTTP { rc := rpcclient.NewJSONRPCClient(remote) cdc := rc.Codec() @@ -46,39 +91,76 @@ func NewHTTP(remote, wsEndpoint string) *HTTP { rc.SetCodec(cdc) return &HTTP{ - rpc: rc, - remote: remote, - WSEvents: newWSEvents(cdc, remote, wsEndpoint), + rpc: rc, + remote: remote, + baseRPCClient: &baseRPCClient{caller: rc}, + WSEvents: newWSEvents(cdc, remote, wsEndpoint), } } var _ Client = (*HTTP)(nil) -func (c *HTTP) Status() (*ctypes.ResultStatus, error) { +// NewBatch creates a new batch client for this HTTP client. +func (c *HTTP) NewBatch() *BatchHTTP { + rpcBatch := c.rpc.NewRequestBatch() + return &BatchHTTP{ + rpcBatch: rpcBatch, + baseRPCClient: &baseRPCClient{ + caller: rpcBatch, + }, + } +} + +//----------------------------------------------------------------------------- +// BatchHTTP + +// Send is a convenience function for an HTTP batch that will trigger the +// compilation of the batched requests and send them off using the client as a +// single request. On success, this returns a list of the deserialized results +// from each request in the sent batch. +func (b *BatchHTTP) Send() ([]interface{}, error) { + return b.rpcBatch.Send() +} + +// Clear will empty out this batch of requests and return the number of requests +// that were cleared out. +func (b *BatchHTTP) Clear() int { + return b.rpcBatch.Clear() +} + +// Count returns the number of enqueued requests waiting to be sent. +func (b *BatchHTTP) Count() int { + return b.rpcBatch.Count() +} + +//----------------------------------------------------------------------------- +// baseRPCClient + +func (c *baseRPCClient) Status() (*ctypes.ResultStatus, error) { result := new(ctypes.ResultStatus) - _, err := c.rpc.Call("status", map[string]interface{}{}, result) + _, err := c.caller.Call("status", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "Status") } return result, nil } -func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) { +func (c *baseRPCClient) ABCIInfo() (*ctypes.ResultABCIInfo, error) { result := new(ctypes.ResultABCIInfo) - _, err := c.rpc.Call("abci_info", map[string]interface{}{}, result) + _, err := c.caller.Call("abci_info", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "ABCIInfo") } return result, nil } -func (c *HTTP) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { +func (c *baseRPCClient) ABCIQuery(path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { return c.ABCIQueryWithOptions(path, data, DefaultABCIQueryOptions) } -func (c *HTTP) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { +func (c *baseRPCClient) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQueryOptions) (*ctypes.ResultABCIQuery, error) { result := new(ctypes.ResultABCIQuery) - _, err := c.rpc.Call("abci_query", + _, err := c.caller.Call("abci_query", map[string]interface{}{"path": path, "data": data, "height": opts.Height, "prove": opts.Prove}, result) if err != nil { @@ -87,89 +169,89 @@ func (c *HTTP) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts ABCIQue return result, nil } -func (c *HTTP) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { +func (c *baseRPCClient) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { result := new(ctypes.ResultBroadcastTxCommit) - _, err := c.rpc.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) + _, err := c.caller.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result) if err != nil { return nil, errors.Wrap(err, "broadcast_tx_commit") } return result, nil } -func (c *HTTP) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { +func (c *baseRPCClient) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { return c.broadcastTX("broadcast_tx_async", tx) } -func (c *HTTP) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { +func (c *baseRPCClient) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { return c.broadcastTX("broadcast_tx_sync", tx) } -func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { +func (c *baseRPCClient) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { result := new(ctypes.ResultBroadcastTx) - _, err := c.rpc.Call(route, map[string]interface{}{"tx": tx}, result) + _, err := c.caller.Call(route, map[string]interface{}{"tx": tx}, result) if err != nil { return nil, errors.Wrap(err, route) } return result, nil } -func (c *HTTP) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { +func (c *baseRPCClient) UnconfirmedTxs(limit int) (*ctypes.ResultUnconfirmedTxs, error) { result := new(ctypes.ResultUnconfirmedTxs) - _, err := c.rpc.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result) + _, err := c.caller.Call("unconfirmed_txs", map[string]interface{}{"limit": limit}, result) if err != nil { return nil, errors.Wrap(err, "unconfirmed_txs") } return result, nil } -func (c *HTTP) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { +func (c *baseRPCClient) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) { result := new(ctypes.ResultUnconfirmedTxs) - _, err := c.rpc.Call("num_unconfirmed_txs", map[string]interface{}{}, result) + _, err := c.caller.Call("num_unconfirmed_txs", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "num_unconfirmed_txs") } return result, nil } -func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) { +func (c *baseRPCClient) NetInfo() (*ctypes.ResultNetInfo, error) { result := new(ctypes.ResultNetInfo) - _, err := c.rpc.Call("net_info", map[string]interface{}{}, result) + _, err := c.caller.Call("net_info", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "NetInfo") } return result, nil } -func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { +func (c *baseRPCClient) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) { result := new(ctypes.ResultDumpConsensusState) - _, err := c.rpc.Call("dump_consensus_state", map[string]interface{}{}, result) + _, err := c.caller.Call("dump_consensus_state", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "DumpConsensusState") } return result, nil } -func (c *HTTP) ConsensusState() (*ctypes.ResultConsensusState, error) { +func (c *baseRPCClient) ConsensusState() (*ctypes.ResultConsensusState, error) { result := new(ctypes.ResultConsensusState) - _, err := c.rpc.Call("consensus_state", map[string]interface{}{}, result) + _, err := c.caller.Call("consensus_state", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "ConsensusState") } return result, nil } -func (c *HTTP) Health() (*ctypes.ResultHealth, error) { +func (c *baseRPCClient) Health() (*ctypes.ResultHealth, error) { result := new(ctypes.ResultHealth) - _, err := c.rpc.Call("health", map[string]interface{}{}, result) + _, err := c.caller.Call("health", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "Health") } return result, nil } -func (c *HTTP) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { +func (c *baseRPCClient) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { result := new(ctypes.ResultBlockchainInfo) - _, err := c.rpc.Call("blockchain", + _, err := c.caller.Call("blockchain", map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight}, result) if err != nil { @@ -178,56 +260,56 @@ func (c *HTTP) BlockchainInfo(minHeight, maxHeight int64) (*ctypes.ResultBlockch return result, nil } -func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) { +func (c *baseRPCClient) Genesis() (*ctypes.ResultGenesis, error) { result := new(ctypes.ResultGenesis) - _, err := c.rpc.Call("genesis", map[string]interface{}{}, result) + _, err := c.caller.Call("genesis", map[string]interface{}{}, result) if err != nil { return nil, errors.Wrap(err, "Genesis") } return result, nil } -func (c *HTTP) Block(height *int64) (*ctypes.ResultBlock, error) { +func (c *baseRPCClient) Block(height *int64) (*ctypes.ResultBlock, error) { result := new(ctypes.ResultBlock) - _, err := c.rpc.Call("block", map[string]interface{}{"height": height}, result) + _, err := c.caller.Call("block", map[string]interface{}{"height": height}, result) if err != nil { return nil, errors.Wrap(err, "Block") } return result, nil } -func (c *HTTP) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { +func (c *baseRPCClient) BlockResults(height *int64) (*ctypes.ResultBlockResults, error) { result := new(ctypes.ResultBlockResults) - _, err := c.rpc.Call("block_results", map[string]interface{}{"height": height}, result) + _, err := c.caller.Call("block_results", map[string]interface{}{"height": height}, result) if err != nil { return nil, errors.Wrap(err, "Block Result") } return result, nil } -func (c *HTTP) Commit(height *int64) (*ctypes.ResultCommit, error) { +func (c *baseRPCClient) Commit(height *int64) (*ctypes.ResultCommit, error) { result := new(ctypes.ResultCommit) - _, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, result) + _, err := c.caller.Call("commit", map[string]interface{}{"height": height}, result) if err != nil { return nil, errors.Wrap(err, "Commit") } return result, nil } -func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { +func (c *baseRPCClient) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) { result := new(ctypes.ResultTx) params := map[string]interface{}{ "hash": hash, "prove": prove, } - _, err := c.rpc.Call("tx", params, result) + _, err := c.caller.Call("tx", params, result) if err != nil { return nil, errors.Wrap(err, "Tx") } return result, nil } -func (c *HTTP) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { +func (c *baseRPCClient) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) { result := new(ctypes.ResultTxSearch) params := map[string]interface{}{ "query": query, @@ -235,23 +317,24 @@ func (c *HTTP) TxSearch(query string, prove bool, page, perPage int) (*ctypes.Re "page": page, "per_page": perPage, } - _, err := c.rpc.Call("tx_search", params, result) + _, err := c.caller.Call("tx_search", params, result) if err != nil { return nil, errors.Wrap(err, "TxSearch") } return result, nil } -func (c *HTTP) Validators(height *int64) (*ctypes.ResultValidators, error) { +func (c *baseRPCClient) Validators(height *int64) (*ctypes.ResultValidators, error) { result := new(ctypes.ResultValidators) - _, err := c.rpc.Call("validators", map[string]interface{}{"height": height}, result) + _, err := c.caller.Call("validators", map[string]interface{}{"height": height}, result) if err != nil { return nil, errors.Wrap(err, "Validators") } return result, nil } -/** websocket event stuff here... **/ +//----------------------------------------------------------------------------- +// WSEvents type WSEvents struct { cmn.BaseService diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index ba9bc3af7..1544a3d95 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -4,6 +4,7 @@ import ( "fmt" "net/http" "strings" + "sync" "testing" "github.com/stretchr/testify/assert" @@ -11,7 +12,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/rpc/client" + ctypes "github.com/tendermint/tendermint/rpc/core/types" rpctest "github.com/tendermint/tendermint/rpc/test" "github.com/tendermint/tendermint/types" ) @@ -441,3 +444,100 @@ func TestTxSearch(t *testing.T) { require.Len(t, result.Txs, 0) } } + +func TestBatchedJSONRPCCalls(t *testing.T) { + c := getHTTPClient() + testBatchedJSONRPCCalls(t, c) +} + +func testBatchedJSONRPCCalls(t *testing.T, c *client.HTTP) { + k1, v1, tx1 := MakeTxKV() + k2, v2, tx2 := MakeTxKV() + + batch := c.NewBatch() + r1, err := batch.BroadcastTxCommit(tx1) + require.NoError(t, err) + r2, err := batch.BroadcastTxCommit(tx2) + require.NoError(t, err) + require.Equal(t, 2, batch.Count()) + bresults, err := batch.Send() + require.NoError(t, err) + require.Len(t, bresults, 2) + require.Equal(t, 0, batch.Count()) + + bresult1, ok := bresults[0].(*ctypes.ResultBroadcastTxCommit) + require.True(t, ok) + require.Equal(t, *bresult1, *r1) + bresult2, ok := bresults[1].(*ctypes.ResultBroadcastTxCommit) + require.True(t, ok) + require.Equal(t, *bresult2, *r2) + apph := cmn.MaxInt64(bresult1.Height, bresult2.Height) + 1 + + client.WaitForHeight(c, apph, nil) + + q1, err := batch.ABCIQuery("/key", k1) + require.NoError(t, err) + q2, err := batch.ABCIQuery("/key", k2) + require.NoError(t, err) + require.Equal(t, 2, batch.Count()) + qresults, err := batch.Send() + require.NoError(t, err) + require.Len(t, qresults, 2) + require.Equal(t, 0, batch.Count()) + + qresult1, ok := qresults[0].(*ctypes.ResultABCIQuery) + require.True(t, ok) + require.Equal(t, *qresult1, *q1) + qresult2, ok := qresults[1].(*ctypes.ResultABCIQuery) + require.True(t, ok) + require.Equal(t, *qresult2, *q2) + + require.Equal(t, qresult1.Response.Key, k1) + require.Equal(t, qresult2.Response.Key, k2) + require.Equal(t, qresult1.Response.Value, v1) + require.Equal(t, qresult2.Response.Value, v2) +} + +func TestBatchedJSONRPCCallsCancellation(t *testing.T) { + c := getHTTPClient() + _, _, tx1 := MakeTxKV() + _, _, tx2 := MakeTxKV() + + batch := c.NewBatch() + _, err := batch.BroadcastTxCommit(tx1) + require.NoError(t, err) + _, err = batch.BroadcastTxCommit(tx2) + require.NoError(t, err) + // we should have 2 requests waiting + require.Equal(t, 2, batch.Count()) + // we want to make sure we cleared 2 pending requests + require.Equal(t, 2, batch.Clear()) + // now there should be no batched requests + require.Equal(t, 0, batch.Count()) +} + +func TestSendingEmptyJSONRPCRequestBatch(t *testing.T) { + c := getHTTPClient() + batch := c.NewBatch() + _, err := batch.Send() + require.Error(t, err, "sending an empty batch of JSON RPC requests should result in an error") +} + +func TestClearingEmptyJSONRPCRequestBatch(t *testing.T) { + c := getHTTPClient() + batch := c.NewBatch() + require.Zero(t, batch.Clear(), "clearing an empty batch of JSON RPC requests should result in a 0 result") +} + +func TestConcurrentJSONRPCBatching(t *testing.T) { + var wg sync.WaitGroup + c := getHTTPClient() + for i := 0; i < 50; i++ { + wg.Add(1) + go func() { + defer wg.Done() + testBatchedJSONRPCCalls(t, c) + }() + } + wg.Wait() +} diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index cfa26e89c..824820fab 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -10,9 +10,11 @@ import ( "net/url" "reflect" "strings" + "sync" "github.com/pkg/errors" amino "github.com/tendermint/go-amino" + cmn "github.com/tendermint/tendermint/libs/common" types "github.com/tendermint/tendermint/rpc/lib/types" ) @@ -83,25 +85,56 @@ func makeHTTPClient(remoteAddr string) (string, *http.Client) { //------------------------------------------------------------------------------------ +// jsonRPCBufferedRequest encapsulates a single buffered request, as well as its +// anticipated response structure. +type jsonRPCBufferedRequest struct { + request types.RPCRequest + result interface{} // The result will be deserialized into this object. +} + +// JSONRPCRequestBatch allows us to buffer multiple request/response structures +// into a single batch request. Note that this batch acts like a FIFO queue, and +// is thread-safe. +type JSONRPCRequestBatch struct { + client *JSONRPCClient + + mtx sync.Mutex + requests []*jsonRPCBufferedRequest +} + // JSONRPCClient takes params as a slice type JSONRPCClient struct { address string client *http.Client + id types.JSONRPCStringID cdc *amino.Codec } +// JSONRPCCaller implementers can facilitate calling the JSON RPC endpoint. +type JSONRPCCaller interface { + Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) +} + +// Both JSONRPCClient and JSONRPCRequestBatch can facilitate calls to the JSON +// RPC endpoint. +var _ JSONRPCCaller = (*JSONRPCClient)(nil) +var _ JSONRPCCaller = (*JSONRPCRequestBatch)(nil) + // NewJSONRPCClient returns a JSONRPCClient pointed at the given address. func NewJSONRPCClient(remote string) *JSONRPCClient { address, client := makeHTTPClient(remote) return &JSONRPCClient{ address: address, client: client, + id: types.JSONRPCStringID("jsonrpc-client-" + cmn.RandStr(8)), cdc: amino.NewCodec(), } } +// Call will send the request for the given method through to the RPC endpoint +// immediately, without buffering of requests. func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) { - request, err := types.MapToRequest(c.cdc, types.JSONRPCStringID("jsonrpc-client"), method, params) + request, err := types.MapToRequest(c.cdc, c.id, method, params) if err != nil { return nil, err } @@ -109,9 +142,7 @@ func (c *JSONRPCClient) Call(method string, params map[string]interface{}, resul if err != nil { return nil, err } - // log.Info(string(requestBytes)) requestBuf := bytes.NewBuffer(requestBytes) - // log.Info(Fmt("RPC request to %v (%v): %v", c.remote, method, string(requestBytes))) httpResponse, err := c.client.Post(c.address, "text/json", requestBuf) if err != nil { return nil, err @@ -122,8 +153,40 @@ func (c *JSONRPCClient) Call(method string, params map[string]interface{}, resul if err != nil { return nil, err } - // log.Info(Fmt("RPC response: %v", string(responseBytes))) - return unmarshalResponseBytes(c.cdc, responseBytes, result) + return unmarshalResponseBytes(c.cdc, responseBytes, c.id, result) +} + +// NewRequestBatch starts a batch of requests for this client. +func (c *JSONRPCClient) NewRequestBatch() *JSONRPCRequestBatch { + return &JSONRPCRequestBatch{ + requests: make([]*jsonRPCBufferedRequest, 0), + client: c, + } +} + +func (c *JSONRPCClient) sendBatch(requests []*jsonRPCBufferedRequest) ([]interface{}, error) { + reqs := make([]types.RPCRequest, 0, len(requests)) + results := make([]interface{}, 0, len(requests)) + for _, req := range requests { + reqs = append(reqs, req.request) + results = append(results, req.result) + } + // serialize the array of requests into a single JSON object + requestBytes, err := json.Marshal(reqs) + if err != nil { + return nil, err + } + httpResponse, err := c.client.Post(c.address, "text/json", bytes.NewBuffer(requestBytes)) + if err != nil { + return nil, err + } + defer httpResponse.Body.Close() // nolint: errcheck + + responseBytes, err := ioutil.ReadAll(httpResponse.Body) + if err != nil { + return nil, err + } + return unmarshalResponseBytesArray(c.cdc, responseBytes, c.id, results) } func (c *JSONRPCClient) Codec() *amino.Codec { @@ -136,6 +199,57 @@ func (c *JSONRPCClient) SetCodec(cdc *amino.Codec) { //------------------------------------------------------------- +// Count returns the number of enqueued requests waiting to be sent. +func (b *JSONRPCRequestBatch) Count() int { + b.mtx.Lock() + defer b.mtx.Unlock() + return len(b.requests) +} + +func (b *JSONRPCRequestBatch) enqueue(req *jsonRPCBufferedRequest) { + b.mtx.Lock() + defer b.mtx.Unlock() + b.requests = append(b.requests, req) +} + +// Clear empties out the request batch. +func (b *JSONRPCRequestBatch) Clear() int { + b.mtx.Lock() + defer b.mtx.Unlock() + return b.clear() +} + +func (b *JSONRPCRequestBatch) clear() int { + count := len(b.requests) + b.requests = make([]*jsonRPCBufferedRequest, 0) + return count +} + +// Send will attempt to send the current batch of enqueued requests, and then +// will clear out the requests once done. On success, this returns the +// deserialized list of results from each of the enqueued requests. +func (b *JSONRPCRequestBatch) Send() ([]interface{}, error) { + b.mtx.Lock() + defer func() { + b.clear() + b.mtx.Unlock() + }() + return b.client.sendBatch(b.requests) +} + +// Call enqueues a request to call the given RPC method with the specified +// parameters, in the same way that the `JSONRPCClient.Call` function would. +func (b *JSONRPCRequestBatch) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) { + request, err := types.MapToRequest(b.client.cdc, b.client.id, method, params) + if err != nil { + return nil, err + } + b.enqueue(&jsonRPCBufferedRequest{request: request, result: result}) + return result, nil +} + +//------------------------------------------------------------- + // URI takes params as a map type URIClient struct { address string @@ -168,7 +282,7 @@ func (c *URIClient) Call(method string, params map[string]interface{}, result in if err != nil { return nil, err } - return unmarshalResponseBytes(c.cdc, responseBytes, result) + return unmarshalResponseBytes(c.cdc, responseBytes, "", result) } func (c *URIClient) Codec() *amino.Codec { @@ -181,7 +295,7 @@ func (c *URIClient) SetCodec(cdc *amino.Codec) { //------------------------------------------------ -func unmarshalResponseBytes(cdc *amino.Codec, responseBytes []byte, result interface{}) (interface{}, error) { +func unmarshalResponseBytes(cdc *amino.Codec, responseBytes []byte, expectedID types.JSONRPCStringID, result interface{}) (interface{}, error) { // Read response. If rpc/core/types is imported, the result will unmarshal // into the correct type. // log.Notice("response", "response", string(responseBytes)) @@ -189,19 +303,71 @@ func unmarshalResponseBytes(cdc *amino.Codec, responseBytes []byte, result inter response := &types.RPCResponse{} err = json.Unmarshal(responseBytes, response) if err != nil { - return nil, errors.Errorf("Error unmarshalling rpc response: %v", err) + return nil, errors.Errorf("error unmarshalling rpc response: %v", err) } if response.Error != nil { - return nil, errors.Errorf("Response error: %v", response.Error) + return nil, errors.Errorf("response error: %v", response.Error) + } + // From the JSON-RPC 2.0 spec: + // id: It MUST be the same as the value of the id member in the Request Object. + if err := validateResponseID(response, expectedID); err != nil { + return nil, err } // Unmarshal the RawMessage into the result. err = cdc.UnmarshalJSON(response.Result, result) if err != nil { - return nil, errors.Errorf("Error unmarshalling rpc response result: %v", err) + return nil, errors.Errorf("error unmarshalling rpc response result: %v", err) } return result, nil } +func unmarshalResponseBytesArray(cdc *amino.Codec, responseBytes []byte, expectedID types.JSONRPCStringID, results []interface{}) ([]interface{}, error) { + var ( + err error + responses []types.RPCResponse + ) + err = json.Unmarshal(responseBytes, &responses) + if err != nil { + return nil, errors.Errorf("error unmarshalling rpc response: %v", err) + } + // No response error checking here as there may be a mixture of successful + // and unsuccessful responses. + + if len(results) != len(responses) { + return nil, errors.Errorf("expected %d result objects into which to inject responses, but got %d", len(responses), len(results)) + } + + for i, response := range responses { + // From the JSON-RPC 2.0 spec: + // id: It MUST be the same as the value of the id member in the Request Object. + if err := validateResponseID(&response, expectedID); err != nil { + return nil, errors.Errorf("failed to validate response ID in response %d: %v", i, err) + } + if err := cdc.UnmarshalJSON(responses[i].Result, results[i]); err != nil { + return nil, errors.Errorf("error unmarshalling rpc response result: %v", err) + } + } + return results, nil +} + +func validateResponseID(res *types.RPCResponse, expectedID types.JSONRPCStringID) error { + // we only validate a response ID if the expected ID is non-empty + if len(expectedID) == 0 { + return nil + } + if res.ID == nil { + return errors.Errorf("missing ID in response") + } + id, ok := res.ID.(types.JSONRPCStringID) + if !ok { + return errors.Errorf("expected ID string in response but got: %v", id) + } + if expectedID != id { + return errors.Errorf("response ID (%s) does not match request ID (%s)", id, expectedID) + } + return nil +} + func argsToURLValues(cdc *amino.Codec, args map[string]interface{}) (url.Values, error) { values := make(url.Values) if len(args) == 0 { diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go index 6391b0090..c1c1ebf1a 100644 --- a/rpc/lib/server/handlers.go +++ b/rpc/lib/server/handlers.go @@ -103,7 +103,7 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, cdc *amino.Codec, logger lo return func(w http.ResponseWriter, r *http.Request) { b, err := ioutil.ReadAll(r.Body) if err != nil { - WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(types.JSONRPCStringID(""), errors.Wrap(err, "Error reading request body"))) + WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(types.JSONRPCStringID(""), errors.Wrap(err, "error reading request body"))) return } // if its an empty request (like from a browser), @@ -113,49 +113,59 @@ func makeJSONRPCHandler(funcMap map[string]*RPCFunc, cdc *amino.Codec, logger lo return } - var request types.RPCRequest - err = json.Unmarshal(b, &request) - if err != nil { - WriteRPCResponseHTTP(w, types.RPCParseError(types.JSONRPCStringID(""), errors.Wrap(err, "Error unmarshalling request"))) - return - } - // A Notification is a Request object without an "id" member. - // The Server MUST NOT reply to a Notification, including those that are within a batch request. - if request.ID == types.JSONRPCStringID("") { - logger.Debug("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)") - return - } - if len(r.URL.Path) > 1 { - WriteRPCResponseHTTP(w, types.RPCInvalidRequestError(request.ID, errors.Errorf("Path %s is invalid", r.URL.Path))) - return - } - - rpcFunc := funcMap[request.Method] - if rpcFunc == nil || rpcFunc.ws { - WriteRPCResponseHTTP(w, types.RPCMethodNotFoundError(request.ID)) - return - } - - ctx := &types.Context{JSONReq: &request, HTTPReq: r} - args := []reflect.Value{reflect.ValueOf(ctx)} - if len(request.Params) > 0 { - fnArgs, err := jsonParamsToArgs(rpcFunc, cdc, request.Params) - if err != nil { - WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) + // first try to unmarshal the incoming request as an array of RPC requests + var ( + requests []types.RPCRequest + responses []types.RPCResponse + ) + if err := json.Unmarshal(b, &requests); err != nil { + // next, try to unmarshal as a single request + var request types.RPCRequest + if err := json.Unmarshal(b, &request); err != nil { + WriteRPCResponseHTTP(w, types.RPCParseError(types.JSONRPCStringID(""), errors.Wrap(err, "error unmarshalling request"))) return } - args = append(args, fnArgs...) + requests = []types.RPCRequest{request} } - returns := rpcFunc.f.Call(args) - - logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) - result, err := unreflectResult(returns) - if err != nil { - WriteRPCResponseHTTP(w, types.RPCInternalError(request.ID, err)) - return + for _, request := range requests { + // A Notification is a Request object without an "id" member. + // The Server MUST NOT reply to a Notification, including those that are within a batch request. + if request.ID == types.JSONRPCStringID("") { + logger.Debug("HTTPJSONRPC received a notification, skipping... (please send a non-empty ID if you want to call a method)") + continue + } + if len(r.URL.Path) > 1 { + responses = append(responses, types.RPCInvalidRequestError(request.ID, errors.Errorf("path %s is invalid", r.URL.Path))) + continue + } + rpcFunc, ok := funcMap[request.Method] + if !ok || rpcFunc.ws { + responses = append(responses, types.RPCMethodNotFoundError(request.ID)) + continue + } + ctx := &types.Context{JSONReq: &request, HTTPReq: r} + args := []reflect.Value{reflect.ValueOf(ctx)} + if len(request.Params) > 0 { + fnArgs, err := jsonParamsToArgs(rpcFunc, cdc, request.Params) + if err != nil { + responses = append(responses, types.RPCInvalidParamsError(request.ID, errors.Wrap(err, "error converting json params to arguments"))) + continue + } + args = append(args, fnArgs...) + } + returns := rpcFunc.f.Call(args) + logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns) + result, err := unreflectResult(returns) + if err != nil { + responses = append(responses, types.RPCInternalError(request.ID, err)) + continue + } + responses = append(responses, types.NewRPCSuccessResponse(cdc, request.ID, result)) + } + if len(responses) > 0 { + WriteRPCResponseArrayHTTP(w, responses) } - WriteRPCResponseHTTP(w, types.NewRPCSuccessResponse(cdc, request.ID, result)) } } @@ -194,7 +204,7 @@ func mapParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, params map[string]json. func arrayParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, params []json.RawMessage, argsOffset int) ([]reflect.Value, error) { if len(rpcFunc.argNames) != len(params) { - return nil, errors.Errorf("Expected %v parameters (%v), got %v (%v)", + return nil, errors.Errorf("expected %v parameters (%v), got %v (%v)", len(rpcFunc.argNames), rpcFunc.argNames, len(params), params) } @@ -236,7 +246,7 @@ func jsonParamsToArgs(rpcFunc *RPCFunc, cdc *amino.Codec, raw []byte) ([]reflect } // Otherwise, bad format, we cannot parse - return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err) + return nil, errors.Errorf("unknown type for JSON params: %v. Expected map or array", err) } // rpc.json @@ -261,7 +271,7 @@ func makeHTTPHandler(rpcFunc *RPCFunc, cdc *amino.Codec, logger log.Logger) func fnArgs, err := httpParamsToArgs(rpcFunc, cdc, r) if err != nil { - WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(types.JSONRPCStringID(""), errors.Wrap(err, "Error converting http params to arguments"))) + WriteRPCResponseHTTP(w, types.RPCInvalidParamsError(types.JSONRPCStringID(""), errors.Wrap(err, "error converting http params to arguments"))) return } args = append(args, fnArgs...) @@ -372,7 +382,7 @@ func _nonJSONStringToArg(cdc *amino.Codec, rt reflect.Type, arg string) (reflect if isHexString { if !expectingString && !expectingByteSlice { - err := errors.Errorf("Got a hex string arg, but expected '%s'", + err := errors.Errorf("got a hex string arg, but expected '%s'", rt.Kind().String()) return reflect.ValueOf(nil), err, false } @@ -631,7 +641,7 @@ func (wsc *wsConnection) readRoutine() { var request types.RPCRequest err = json.Unmarshal(in, &request) if err != nil { - wsc.WriteRPCResponse(types.RPCParseError(types.JSONRPCStringID(""), errors.Wrap(err, "Error unmarshaling request"))) + wsc.WriteRPCResponse(types.RPCParseError(types.JSONRPCStringID(""), errors.Wrap(err, "error unmarshaling request"))) continue } @@ -654,7 +664,7 @@ func (wsc *wsConnection) readRoutine() { if len(request.Params) > 0 { fnArgs, err := jsonParamsToArgs(rpcFunc, wsc.cdc, request.Params) if err != nil { - wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "Error converting json params to arguments"))) + wsc.WriteRPCResponse(types.RPCInternalError(request.ID, errors.Wrap(err, "error converting json params to arguments"))) continue } args = append(args, fnArgs...) diff --git a/rpc/lib/server/handlers_test.go b/rpc/lib/server/handlers_test.go index f8ad06107..9cded2953 100644 --- a/rpc/lib/server/handlers_test.go +++ b/rpc/lib/server/handlers_test.go @@ -154,6 +154,72 @@ func TestRPCNotification(t *testing.T) { require.Equal(t, len(blob), 0, "a notification SHOULD NOT be responded to by the server") } +func TestRPCNotificationInBatch(t *testing.T) { + mux := testMux() + tests := []struct { + payload string + expectCount int + }{ + { + `[ + {"jsonrpc": "2.0","id": ""}, + {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]} + ]`, + 1, + }, + { + `[ + {"jsonrpc": "2.0","id": ""}, + {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]}, + {"jsonrpc": "2.0","id": ""}, + {"jsonrpc": "2.0","method":"c","id":"abc","params":["a","10"]} + ]`, + 2, + }, + } + for i, tt := range tests { + req, _ := http.NewRequest("POST", "http://localhost/", strings.NewReader(tt.payload)) + rec := httptest.NewRecorder() + mux.ServeHTTP(rec, req) + res := rec.Result() + // Always expecting back a JSONRPCResponse + assert.True(t, statusOK(res.StatusCode), "#%d: should always return 2XX", i) + blob, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Errorf("#%d: err reading body: %v", i, err) + continue + } + + var responses []types.RPCResponse + // try to unmarshal an array first + err = json.Unmarshal(blob, &responses) + if err != nil { + // if we were actually expecting an array, but got an error + if tt.expectCount > 1 { + t.Errorf("#%d: expected an array, couldn't unmarshal it\nblob: %s", i, blob) + continue + } else { + // we were expecting an error here, so let's unmarshal a single response + var response types.RPCResponse + err = json.Unmarshal(blob, &response) + if err != nil { + t.Errorf("#%d: expected successful parsing of an RPCResponse\nblob: %s", i, blob) + continue + } + // have a single-element result + responses = []types.RPCResponse{response} + } + } + if tt.expectCount != len(responses) { + t.Errorf("#%d: expected %d response(s), but got %d\nblob: %s", i, tt.expectCount, len(responses), blob) + continue + } + for _, response := range responses { + assert.NotEqual(t, response, new(types.RPCResponse), "#%d: not expecting a blank RPCResponse", i) + } + } +} + func TestUnknownRPCPath(t *testing.T) { mux := testMux() req, _ := http.NewRequest("GET", "http://localhost/unknownrpcpath", nil) diff --git a/rpc/lib/server/http_params.go b/rpc/lib/server/http_params.go index 3c948c0ba..8ade41c79 100644 --- a/rpc/lib/server/http_params.go +++ b/rpc/lib/server/http_params.go @@ -76,7 +76,7 @@ func GetParamUint(r *http.Request, param string) (uint, error) { func GetParamRegexp(r *http.Request, param string, re *regexp.Regexp) (string, error) { s := GetParam(r, param) if !re.MatchString(s) { - return "", errors.Errorf(param, "Did not match regular expression %v", re.String()) + return "", errors.Errorf(param, "did not match regular expression %v", re.String()) } return s, nil } diff --git a/rpc/lib/server/http_server.go b/rpc/lib/server/http_server.go index c4bb6fa17..7825605eb 100644 --- a/rpc/lib/server/http_server.go +++ b/rpc/lib/server/http_server.go @@ -98,7 +98,9 @@ func WriteRPCResponseHTTPError( w.Header().Set("Content-Type", "application/json") w.WriteHeader(httpCode) - w.Write(jsonBytes) // nolint: errcheck, gas + if _, err := w.Write(jsonBytes); err != nil { + panic(err) + } } func WriteRPCResponseHTTP(w http.ResponseWriter, res types.RPCResponse) { @@ -108,12 +110,33 @@ func WriteRPCResponseHTTP(w http.ResponseWriter, res types.RPCResponse) { } w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) - w.Write(jsonBytes) // nolint: errcheck, gas + if _, err := w.Write(jsonBytes); err != nil { + panic(err) + } +} + +// WriteRPCResponseArrayHTTP will do the same as WriteRPCResponseHTTP, except it +// can write arrays of responses for batched request/response interactions via +// the JSON RPC. +func WriteRPCResponseArrayHTTP(w http.ResponseWriter, res []types.RPCResponse) { + if len(res) == 1 { + WriteRPCResponseHTTP(w, res[0]) + } else { + jsonBytes, err := json.MarshalIndent(res, "", " ") + if err != nil { + panic(err) + } + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + if _, err := w.Write(jsonBytes); err != nil { + panic(err) + } + } } //----------------------------------------------------------------------------- -// Wraps an HTTP handler, adding error logging. +// RecoverAndLogHandler wraps an HTTP handler, adding error logging. // If the inner function panics, the outer function recovers, logs, sends an // HTTP 500 error response. func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler { @@ -191,14 +214,14 @@ func Listen(addr string, config *Config) (listener net.Listener, err error) { parts := strings.SplitN(addr, "://", 2) if len(parts) != 2 { return nil, errors.Errorf( - "Invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", + "invalid listening address %s (use fully formed addresses, including the tcp:// or unix:// prefix)", addr, ) } proto, addr := parts[0], parts[1] listener, err = net.Listen(proto, addr) if err != nil { - return nil, errors.Errorf("Failed to listen on %v: %v", addr, err) + return nil, errors.Errorf("failed to listen on %v: %v", addr, err) } if config.MaxOpenConnections > 0 { listener = netutil.LimitListener(listener, config.MaxOpenConnections) diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index 10d165625..cddc80b8e 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -23,7 +23,18 @@ import ( rpcclient "github.com/tendermint/tendermint/rpc/lib/client" ) +// Options helps with specifying some parameters for our RPC testing for greater +// control. +type Options struct { + suppressStdout bool + recreateConfig bool +} + var globalConfig *cfg.Config +var defaultOptions = Options{ + suppressStdout: false, + recreateConfig: false, +} func waitForRPC() { laddr := GetConfig().RPC.ListenAddress @@ -77,19 +88,24 @@ func makeAddrs() (string, string, string) { fmt.Sprintf("tcp://0.0.0.0:%d", randPort()) } -// GetConfig returns a config for the test cases as a singleton -func GetConfig() *cfg.Config { - if globalConfig == nil { - pathname := makePathname() - globalConfig = cfg.ResetTestRoot(pathname) +func createConfig() *cfg.Config { + pathname := makePathname() + c := cfg.ResetTestRoot(pathname) - // and we use random ports to run in parallel - tm, rpc, grpc := makeAddrs() - globalConfig.P2P.ListenAddress = tm - globalConfig.RPC.ListenAddress = rpc - globalConfig.RPC.CORSAllowedOrigins = []string{"https://tendermint.com/"} - globalConfig.RPC.GRPCListenAddress = grpc - globalConfig.TxIndex.IndexTags = "app.creator,tx.height" // see kvstore application + // and we use random ports to run in parallel + tm, rpc, grpc := makeAddrs() + c.P2P.ListenAddress = tm + c.RPC.ListenAddress = rpc + c.RPC.CORSAllowedOrigins = []string{"https://tendermint.com/"} + c.RPC.GRPCListenAddress = grpc + c.TxIndex.IndexTags = "app.creator,tx.height" // see kvstore application + return c +} + +// GetConfig returns a config for the test cases as a singleton +func GetConfig(forceCreate ...bool) *cfg.Config { + if globalConfig == nil || (len(forceCreate) > 0 && forceCreate[0]) { + globalConfig = createConfig() } return globalConfig } @@ -100,8 +116,12 @@ func GetGRPCClient() core_grpc.BroadcastAPIClient { } // StartTendermint starts a test tendermint server in a go routine and returns when it is initialized -func StartTendermint(app abci.Application) *nm.Node { - node := NewTendermint(app) +func StartTendermint(app abci.Application, opts ...func(*Options)) *nm.Node { + nodeOpts := defaultOptions + for _, opt := range opts { + opt(&nodeOpts) + } + node := NewTendermint(app, &nodeOpts) err := node.Start() if err != nil { panic(err) @@ -111,7 +131,9 @@ func StartTendermint(app abci.Application) *nm.Node { waitForRPC() waitForGRPC() - fmt.Println("Tendermint running!") + if !nodeOpts.suppressStdout { + fmt.Println("Tendermint running!") + } return node } @@ -125,11 +147,16 @@ func StopTendermint(node *nm.Node) { } // NewTendermint creates a new tendermint server and sleeps forever -func NewTendermint(app abci.Application) *nm.Node { +func NewTendermint(app abci.Application, opts *Options) *nm.Node { // Create & start node - config := GetConfig() - logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)) - logger = log.NewFilter(logger, log.AllowError()) + config := GetConfig(opts.recreateConfig) + var logger log.Logger + if opts.suppressStdout { + logger = log.NewNopLogger() + } else { + logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) + logger = log.NewFilter(logger, log.AllowError()) + } pvKeyFile := config.PrivValidatorKeyFile() pvKeyStateFile := config.PrivValidatorStateFile() pv := privval.LoadOrGenFilePV(pvKeyFile, pvKeyStateFile) @@ -148,3 +175,15 @@ func NewTendermint(app abci.Application) *nm.Node { } return node } + +// SuppressStdout is an option that tries to make sure the RPC test Tendermint +// node doesn't log anything to stdout. +func SuppressStdout(o *Options) { + o.suppressStdout = true +} + +// RecreateConfig instructs the RPC test to recreate the configuration each +// time, instead of treating it as a global singleton. +func RecreateConfig(o *Options) { + o.recreateConfig = true +} From f2aa1bf50e3eb61f9cf27ee4e867fbd1daf9069f Mon Sep 17 00:00:00 2001 From: kevlubkcm <36485490+kevlubkcm@users.noreply.github.com> Date: Wed, 17 Apr 2019 12:14:01 -0400 Subject: [PATCH 172/259] bandaid for non-deterministic clist test (#3575) * add a deterministic timeout Co-Authored-By: kevlubkcm <36485490+kevlubkcm@users.noreply.github.com> --- libs/clist/clist_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/clist/clist_test.go b/libs/clist/clist_test.go index 13aca3577..1784f8218 100644 --- a/libs/clist/clist_test.go +++ b/libs/clist/clist_test.go @@ -261,6 +261,8 @@ func TestWaitChan(t *testing.T) { pushed++ time.Sleep(time.Duration(cmn.RandIntn(25)) * time.Millisecond) } + // apply a deterministic pause so the counter has time to catch up + time.Sleep(25 * time.Millisecond) close(done) }() @@ -273,7 +275,7 @@ FOR_LOOP: next = next.Next() seen++ if next == nil { - continue + t.Fatal("Next should not be nil when waiting on NextWaitChan") } case <-done: break FOR_LOOP From 671c5c9b84644b4b83b00fbf23f8d3dd7ff5c199 Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Thu, 18 Apr 2019 17:31:36 +0200 Subject: [PATCH 173/259] crypto: Proof of Concept for iterative version of SimpleHashFromByteSlices (#2611) (#3530) (#2611) had suggested that an iterative version of SimpleHashFromByteSlice would be faster, presumably because we can envision some overhead accumulating from stack frames and function calls. Additionally, a recursive algorithm risks hitting the stack limit and causing a stack overflow should the tree be too large. Provided here is an iterative alternative, a simple test to assert correctness and a benchmark. On the performance side, there appears to be no overall difference: ``` BenchmarkSimpleHashAlternatives/recursive-4 20000 77677 ns/op BenchmarkSimpleHashAlternatives/iterative-4 20000 76802 ns/op ``` On the surface it might seem that the additional overhead is due to the different allocation patterns of the implementations. The recursive version uses a single `[][]byte` slices which it then re-slices at each level of the tree. The iterative version reproduces `[][]byte` once within the function and then rewrites sub-slices of that array at each level of the tree. Eexperimenting by modifying the code to simply calculate the hash and not store the result show little to no difference in performance. These preliminary results suggest: 1. The performance of the current implementation is pretty good 2. Go has low overhead for recursive functions 3. The performance of the SimpleHashFromByteSlice routine is dominated by the actual hashing of data Although this work is in no way exhaustive, point #3 suggests that optimizations of this routine would need to take an alternative approach to make significant improvements on the current performance. Finally, considering that the recursive implementation is easier to read, it might not be worthwhile to switch to a less intuitive implementation for so little benefit. * re-add slice re-writing * [crypto] Document SimpleHashFromByteSlicesIterative --- crypto/merkle/simple_tree.go | 71 +++++++++++++++++++++++++++++++ crypto/merkle/simple_tree_test.go | 36 ++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/crypto/merkle/simple_tree.go b/crypto/merkle/simple_tree.go index 5de514b51..03dc9d9d1 100644 --- a/crypto/merkle/simple_tree.go +++ b/crypto/merkle/simple_tree.go @@ -20,6 +20,77 @@ func SimpleHashFromByteSlices(items [][]byte) []byte { } } +// SimpleHashFromByteSliceIterative is an iterative alternative to +// SimpleHashFromByteSlice motivated by potential performance improvements. +// (#2611) had suggested that an iterative version of +// SimpleHashFromByteSlice would be faster, presumably because +// we can envision some overhead accumulating from stack +// frames and function calls. Additionally, a recursive algorithm risks +// hitting the stack limit and causing a stack overflow should the tree +// be too large. +// +// Provided here is an iterative alternative, a simple test to assert +// correctness and a benchmark. On the performance side, there appears to +// be no overall difference: +// +// BenchmarkSimpleHashAlternatives/recursive-4 20000 77677 ns/op +// BenchmarkSimpleHashAlternatives/iterative-4 20000 76802 ns/op +// +// On the surface it might seem that the additional overhead is due to +// the different allocation patterns of the implementations. The recursive +// version uses a single [][]byte slices which it then re-slices at each level of the tree. +// The iterative version reproduces [][]byte once within the function and +// then rewrites sub-slices of that array at each level of the tree. +// +// Experimenting by modifying the code to simply calculate the +// hash and not store the result show little to no difference in performance. +// +// These preliminary results suggest: +// +// 1. The performance of the SimpleHashFromByteSlice is pretty good +// 2. Go has low overhead for recursive functions +// 3. The performance of the SimpleHashFromByteSlice routine is dominated +// by the actual hashing of data +// +// Although this work is in no way exhaustive, point #3 suggests that +// optimization of this routine would need to take an alternative +// approach to make significant improvements on the current performance. +// +// Finally, considering that the recursive implementation is easier to +// read, it might not be worthwhile to switch to a less intuitive +// implementation for so little benefit. +func SimpleHashFromByteSlicesIterative(input [][]byte) []byte { + items := make([][]byte, len(input)) + + for i, leaf := range input { + items[i] = leafHash(leaf) + } + + size := len(items) + for { + switch size { + case 0: + return nil + case 1: + return items[0] + default: + rp := 0 // read position + wp := 0 // write position + for rp < size { + if rp+1 < size { + items[wp] = innerHash(items[rp], items[rp+1]) + rp += 2 + } else { + items[wp] = items[rp] + rp += 1 + } + wp += 1 + } + size = wp + } + } +} + // SimpleHashFromMap computes a Merkle tree from sorted map. // Like calling SimpleHashFromHashers with // `item = []byte(Hash(key) | Hash(value))`, diff --git a/crypto/merkle/simple_tree_test.go b/crypto/merkle/simple_tree_test.go index 9abe321c3..5bbe294af 100644 --- a/crypto/merkle/simple_tree_test.go +++ b/crypto/merkle/simple_tree_test.go @@ -70,6 +70,42 @@ func TestSimpleProof(t *testing.T) { } } +func TestSimpleHashAlternatives(t *testing.T) { + + total := 100 + + items := make([][]byte, total) + for i := 0; i < total; i++ { + items[i] = testItem(cmn.RandBytes(tmhash.Size)) + } + + rootHash1 := SimpleHashFromByteSlicesIterative(items) + rootHash2 := SimpleHashFromByteSlices(items) + require.Equal(t, rootHash1, rootHash2, "Unmatched root hashes: %X vs %X", rootHash1, rootHash2) +} + +func BenchmarkSimpleHashAlternatives(b *testing.B) { + total := 100 + + items := make([][]byte, total) + for i := 0; i < total; i++ { + items[i] = testItem(cmn.RandBytes(tmhash.Size)) + } + + b.ResetTimer() + b.Run("recursive", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = SimpleHashFromByteSlices(items) + } + }) + + b.Run("iterative", func(b *testing.B) { + for i := 0; i < b.N; i++ { + _ = SimpleHashFromByteSlicesIterative(items) + } + }) +} + func Test_getSplitPoint(t *testing.T) { tests := []struct { length int From 8db7e74b873712b4bb3c72616a798d3fb82094e9 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Mon, 22 Apr 2019 10:04:04 +0200 Subject: [PATCH 174/259] privval: increase timeout to mitigate non-deterministic test failure (#3580) This should fix #3576 (ran it many times locally but only time will tell). The test actually only checked for the opcode of the error. From the name of the test we actually want to test if we see a timeout after a pre-defined time. ## Commits: * increase readWrite timeout as it is also used in the `Accept` of the tcp listener: - before this caused the readWriteTimeout to kick in (rarely) while Accept - as a side-effect: remove obsolete time.Sleep: in both listener cases the Accept will only return after successfully accepting and the timeout that is supposed to be tested here will be triggered because there is a read without a write - see if we actually run into a timeout error (the whole purpose of this test AFAIU) Signed-off-by: Ismail Khoffi * makee local test-vars `const` Signed-off-by: Ismail Khoffi ## Additional comments: @xla: Confusing how an accept could take longer than that, but assuming a noisy environment full of little docker whales will be slower than what 50 years of silicon are capable of. The only thing I'd be vary of is that we mask structural issues with the code by just bumping the timeout, if we are sensitive towards that it warrants invesigation, but again this might only be true in the environment our CI runs in. --- privval/socket_listeners_test.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/privval/socket_listeners_test.go b/privval/socket_listeners_test.go index 498ef79c0..1cda2b6ec 100644 --- a/privval/socket_listeners_test.go +++ b/privval/socket_listeners_test.go @@ -97,7 +97,15 @@ func TestListenerTimeoutAccept(t *testing.T) { } func TestListenerTimeoutReadWrite(t *testing.T) { - for _, tc := range listenerTestCases(t, time.Second, time.Millisecond) { + const ( + // This needs to be long enough s.t. the Accept will definitely succeed: + timeoutAccept = time.Second + // This can be really short but in the TCP case, the accept can + // also trigger a timeoutReadWrite. Hence, we need to give it some time. + // Note: this controls how long this test actually runs. + timeoutReadWrite = 10 * time.Millisecond + ) + for _, tc := range listenerTestCases(t, timeoutAccept, timeoutReadWrite) { go func(dialer SocketDialer) { _, err := dialer() if err != nil { @@ -110,8 +118,7 @@ func TestListenerTimeoutReadWrite(t *testing.T) { t.Fatal(err) } - time.Sleep(2 * time.Millisecond) - + // this will timeout because we don't write anything: msg := make([]byte, 200) _, err = c.Read(msg) opErr, ok := err.(*net.OpError) @@ -122,5 +129,9 @@ func TestListenerTimeoutReadWrite(t *testing.T) { if have, want := opErr.Op, "read"; have != want { t.Errorf("for %s listener, have %v, want %v", tc.description, have, want) } + + if have, want := opErr.Timeout(), true; have != want { + t.Errorf("for %s listener, got unexpected error: have %v, want %v", tc.description, have, want) + } } } From ebf815ee572ab47ee0945a5f5f8276c9f242b2fa Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 23 Apr 2019 12:22:40 +0400 Subject: [PATCH 175/259] cs/replay: check appHash for each block (#3579) * check every block appHash Fixes #3460 Not really needed, but it would detect if the app changed in a way it shouldn't have. * add a changelog entry * no need to return an error if we panic * rename methods * rename methods once again * add a test * correct error msg plus fix a few go-lint warnings * better panic msg --- CHANGELOG_PENDING.md | 1 + consensus/replay.go | 58 ++++++++--- consensus/replay_test.go | 219 ++++++++++++++++++++++++++++++++------- 3 files changed, 223 insertions(+), 55 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 3f151f71a..e05430218 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,6 +18,7 @@ ### IMPROVEMENTS: - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC +- [cs/replay] \#3460 check appHash for each block ### BUG FIXES: - [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set diff --git a/consensus/replay.go b/consensus/replay.go index e47d4892a..38ed79fcf 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -13,7 +13,7 @@ import ( abci "github.com/tendermint/tendermint/abci/types" //auto "github.com/tendermint/tendermint/libs/autofile" - cmn "github.com/tendermint/tendermint/libs/common" + dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" @@ -230,6 +230,7 @@ func (h *Handshaker) SetEventBus(eventBus types.BlockEventPublisher) { h.eventBus = eventBus } +// NBlocks returns the number of blocks applied to the state. func (h *Handshaker) NBlocks() int { return h.nBlocks } @@ -263,7 +264,7 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { // Replay blocks up to the latest in the blockstore. _, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp) if err != nil { - return fmt.Errorf("Error on replay: %v", err) + return fmt.Errorf("error on replay: %v", err) } h.logger.Info("Completed ABCI Handshake - Tendermint and App are synced", @@ -274,7 +275,8 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { return nil } -// Replay all blocks since appBlockHeight and ensure the result matches the current state. +// ReplayBlocks replays all blocks since appBlockHeight and ensures the result +// matches the current state. // Returns the final AppHash or an error. func (h *Handshaker) ReplayBlocks( state sm.State, @@ -319,7 +321,7 @@ func (h *Handshaker) ReplayBlocks( } else { // If validator set is not set in genesis and still empty after InitChain, exit. if len(h.genDoc.Validators) == 0 { - return nil, fmt.Errorf("Validator set is nil in genesis and still empty after InitChain") + return nil, fmt.Errorf("validator set is nil in genesis and still empty after InitChain") } } @@ -332,7 +334,8 @@ func (h *Handshaker) ReplayBlocks( // First handle edge cases and constraints on the storeBlockHeight. if storeBlockHeight == 0 { - return appHash, checkAppHash(state, appHash) + assertAppHashEqualsOneFromState(appHash, state) + return appHash, nil } else if storeBlockHeight < appBlockHeight { // the app should never be ahead of the store (but this is under app's control) @@ -340,11 +343,11 @@ func (h *Handshaker) ReplayBlocks( } else if storeBlockHeight < stateBlockHeight { // the state should never be ahead of the store (this is under tendermint's control) - cmn.PanicSanity(fmt.Sprintf("StateBlockHeight (%d) > StoreBlockHeight (%d)", stateBlockHeight, storeBlockHeight)) + panic(fmt.Sprintf("StateBlockHeight (%d) > StoreBlockHeight (%d)", stateBlockHeight, storeBlockHeight)) } else if storeBlockHeight > stateBlockHeight+1 { // store should be at most one ahead of the state (this is under tendermint's control) - cmn.PanicSanity(fmt.Sprintf("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1)) + panic(fmt.Sprintf("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1)) } var err error @@ -359,7 +362,8 @@ func (h *Handshaker) ReplayBlocks( } else if appBlockHeight == storeBlockHeight { // We're good! - return appHash, checkAppHash(state, appHash) + assertAppHashEqualsOneFromState(appHash, state) + return appHash, nil } } else if storeBlockHeight == stateBlockHeight+1 { @@ -380,7 +384,7 @@ func (h *Handshaker) ReplayBlocks( return state.AppHash, err } else if appBlockHeight == storeBlockHeight { - // We ran Commit, but didn't save the state, so replayBlock with mock app + // We ran Commit, but didn't save the state, so replayBlock with mock app. abciResponses, err := sm.LoadABCIResponses(h.stateDB, storeBlockHeight) if err != nil { return nil, err @@ -393,8 +397,8 @@ func (h *Handshaker) ReplayBlocks( } - cmn.PanicSanity("Should never happen") - return nil, nil + panic(fmt.Sprintf("uncovered case! appHeight: %d, storeHeight: %d, stateHeight: %d", + appBlockHeight, storeBlockHeight, stateBlockHeight)) } func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBlockHeight, storeBlockHeight int64, mutateState bool) ([]byte, error) { @@ -417,6 +421,12 @@ 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) + + // Extra check to ensure the app was not changed in a way it shouldn't have. + if len(appHash) > 0 { + assertAppHashEqualsOneFromBlock(appHash, block) + } + appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, state.LastValidators, h.stateDB) if err != nil { return nil, err @@ -434,7 +444,8 @@ func (h *Handshaker) replayBlocks(state sm.State, proxyApp proxy.AppConns, appBl appHash = state.AppHash } - return appHash, checkAppHash(state, appHash) + assertAppHashEqualsOneFromState(appHash, state) + return appHash, nil } // ApplyBlock on the proxyApp with the last block. @@ -456,11 +467,26 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap return state, nil } -func checkAppHash(state sm.State, appHash []byte) error { - if !bytes.Equal(state.AppHash, appHash) { - panic(fmt.Errorf("Tendermint state.AppHash does not match AppHash after replay. Got %X, expected %X", appHash, state.AppHash).Error()) +func assertAppHashEqualsOneFromBlock(appHash []byte, block *types.Block) { + if !bytes.Equal(appHash, block.AppHash) { + panic(fmt.Sprintf(`block.AppHash does not match AppHash after replay. Got %X, expected %X. + +Block: %v +`, + appHash, block.AppHash, block)) + } +} + +func assertAppHashEqualsOneFromState(appHash []byte, state sm.State) { + if !bytes.Equal(appHash, state.AppHash) { + panic(fmt.Sprintf(`state.AppHash does not match AppHash after replay. Got +%X, expected %X. + +State: %v + +Did you reset Tendermint without resetting your application's data?`, + appHash, state.AppHash, state)) } - return nil } //-------------------------------------------------------------------------------- diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 86dca7657..b084cef4e 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -7,7 +7,7 @@ import ( "io" "io/ioutil" "os" - "path" + "path/filepath" "runtime" "testing" "time" @@ -19,12 +19,14 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto" + cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" "github.com/tendermint/tendermint/version" ) @@ -88,7 +90,7 @@ func startNewConsensusStateAndWaitForBlock(t *testing.T, consensusReplayConfig * } } -func sendTxs(cs *ConsensusState, ctx context.Context) { +func sendTxs(ctx context.Context, cs *ConsensusState) { for i := 0; i < 256; i++ { select { case <-ctx.Done(): @@ -113,7 +115,7 @@ func TestWALCrash(t *testing.T) { 1}, {"many non-empty blocks", func(stateDB dbm.DB, cs *ConsensusState, ctx context.Context) { - go sendTxs(cs, ctx) + go sendTxs(ctx, cs) }, 3}, } @@ -263,7 +265,7 @@ func (w *crashingWAL) Wait() { w.next.Wait() } // Handshake Tests const ( - NUM_BLOCKS = 6 + numBlocks = 6 ) var ( @@ -284,6 +286,7 @@ func TestHandshakeReplayAll(t *testing.T) { for i, m := range modes { config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) defer os.RemoveAll(config.RootDir) + testHandshakeReplay(t, config, 0, m) } } @@ -293,6 +296,7 @@ func TestHandshakeReplaySome(t *testing.T) { for i, m := range modes { config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) defer os.RemoveAll(config.RootDir) + testHandshakeReplay(t, config, 1, m) } } @@ -302,7 +306,8 @@ func TestHandshakeReplayOne(t *testing.T) { for i, m := range modes { config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) defer os.RemoveAll(config.RootDir) - testHandshakeReplay(t, config, NUM_BLOCKS-1, m) + + testHandshakeReplay(t, config, numBlocks-1, m) } } @@ -311,28 +316,30 @@ func TestHandshakeReplayNone(t *testing.T) { for i, m := range modes { config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) defer os.RemoveAll(config.RootDir) - testHandshakeReplay(t, config, NUM_BLOCKS, m) + + testHandshakeReplay(t, config, numBlocks, m) } } func tempWALWithData(data []byte) string { walFile, err := ioutil.TempFile("", "wal") if err != nil { - panic(fmt.Errorf("failed to create temp WAL file: %v", err)) + panic(fmt.Sprintf("failed to create temp WAL file: %v", err)) } _, err = walFile.Write(data) if err != nil { - panic(fmt.Errorf("failed to write to temp WAL file: %v", err)) + panic(fmt.Sprintf("failed to write to temp WAL file: %v", err)) } if err := walFile.Close(); err != nil { - panic(fmt.Errorf("failed to close temp WAL file: %v", err)) + panic(fmt.Sprintf("failed to close temp WAL file: %v", err)) } return walFile.Name() } -// Make some blocks. Start a fresh app and apply nBlocks blocks. Then restart the app and sync it up with the remaining blocks +// Make some blocks. Start a fresh app and apply nBlocks blocks. Then restart +// the app and sync it up with the remaining blocks. func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uint) { - walBody, err := WALWithNBlocks(t, NUM_BLOCKS) + walBody, err := WALWithNBlocks(t, numBlocks) require.NoError(t, err) walFile := tempWALWithData(walBody) config.Consensus.SetWalFile(walFile) @@ -354,11 +361,16 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin store.commits = commits // run the chain through state.ApplyBlock to build up the tendermint state - state = buildTMStateFromChain(config, stateDB, state, chain, mode) + clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "1"))) + proxyApp := proxy.NewAppConns(clientCreator) + err = proxyApp.Start() + require.NoError(t, err) + state = buildTMStateFromChain(config, stateDB, state, chain, proxyApp, mode) + proxyApp.Stop() latestAppHash := state.AppHash // make a new client creator - kvstoreApp := kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "2")) + kvstoreApp := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "2")) clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp) if nBlocks > 0 { // run nBlocks against a new client to build up the app state. @@ -371,7 +383,7 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin // now start the app using the handshake - it should sync genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) handshaker := NewHandshaker(stateDB, state, store, genDoc) - proxyApp := proxy.NewAppConns(clientCreator2) + proxyApp = proxy.NewAppConns(clientCreator2) if err := proxyApp.Start(); err != nil { t.Fatalf("Error starting proxy app connections: %v", err) } @@ -391,8 +403,8 @@ func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uin t.Fatalf("Expected app hashes to match after handshake/replay. got %X, expected %X", res.LastBlockAppHash, latestAppHash) } - expectedBlocksToSync := NUM_BLOCKS - nBlocks - if nBlocks == NUM_BLOCKS && mode > 0 { + expectedBlocksToSync := numBlocks - nBlocks + if nBlocks == numBlocks && mode > 0 { expectedBlocksToSync++ } else if nBlocks > 0 && mode == 1 { expectedBlocksToSync++ @@ -407,7 +419,7 @@ func applyBlock(stateDB dbm.DB, st sm.State, blk *types.Block, proxyApp proxy.Ap testPartSize := types.BlockPartSizeBytes blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) - blkID := types.BlockID{blk.Hash(), blk.MakePartSet(testPartSize).Header()} + blkID := types.BlockID{Hash: blk.Hash(), PartsHeader: blk.MakePartSet(testPartSize).Header()} newState, err := blockExec.ApplyBlock(st, blkID, blk) if err != nil { panic(err) @@ -451,14 +463,8 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB, } -func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, mode uint) sm.State { - // run the whole chain against this client to build up the tendermint state - clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(path.Join(config.DBDir(), "1"))) - proxyApp := proxy.NewAppConns(clientCreator) - if err := proxyApp.Start(); err != nil { - panic(err) - } - defer proxyApp.Stop() +func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, + chain []*types.Block, proxyApp proxy.AppConns, mode uint) sm.State { validators := types.TM2PB.ValidatorUpdates(state.Validators) if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{ @@ -489,28 +495,162 @@ func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, c return state } +func TestHandshakePanicsIfAppReturnsWrongAppHash(t *testing.T) { + // 1. Initialize tendermint and commit 3 blocks with the following app hashes: + // - 0x01 + // - 0x02 + // - 0x03 + config := ResetConfig("handshake_test_") + defer os.RemoveAll(config.RootDir) + privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) + const appVersion = 0x0 + stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), appVersion) + genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) + state.LastValidators = state.Validators.Copy() + // mode = 0 for committing all the blocks + blocks := makeBlocks(3, &state, privVal) + store.chain = blocks + + // 2. Tendermint must panic if app returns wrong hash for the first block + // - RANDOM HASH + // - 0x02 + // - 0x03 + { + app := &badApp{numBlocks: 3, allHashesAreWrong: true} + clientCreator := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(clientCreator) + err := proxyApp.Start() + require.NoError(t, err) + defer proxyApp.Stop() + + assert.Panics(t, func() { + h := NewHandshaker(stateDB, state, store, genDoc) + h.Handshake(proxyApp) + }) + } + + // 3. Tendermint must panic if app returns wrong hash for the last block + // - 0x01 + // - 0x02 + // - RANDOM HASH + { + app := &badApp{numBlocks: 3, onlyLastHashIsWrong: true} + clientCreator := proxy.NewLocalClientCreator(app) + proxyApp := proxy.NewAppConns(clientCreator) + err := proxyApp.Start() + require.NoError(t, err) + defer proxyApp.Stop() + + assert.Panics(t, func() { + h := NewHandshaker(stateDB, state, store, genDoc) + h.Handshake(proxyApp) + }) + } +} + +func makeBlocks(n int, state *sm.State, privVal types.PrivValidator) []*types.Block { + blocks := make([]*types.Block, 0) + + var ( + prevBlock *types.Block + prevBlockMeta *types.BlockMeta + ) + + appHeight := byte(0x01) + for i := 0; i < n; i++ { + height := int64(i + 1) + + block, parts := makeBlock(*state, prevBlock, prevBlockMeta, privVal, height) + blocks = append(blocks, block) + + prevBlock = block + prevBlockMeta = types.NewBlockMeta(block, parts) + + // update state + state.AppHash = []byte{appHeight} + appHeight++ + state.LastBlockHeight = height + } + + return blocks +} + +func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote { + addr := privVal.GetPubKey().Address() + idx, _ := valset.GetByAddress(addr) + vote := &types.Vote{ + ValidatorAddress: addr, + ValidatorIndex: idx, + Height: header.Height, + Round: 1, + Timestamp: tmtime.Now(), + Type: types.PrecommitType, + BlockID: blockID, + } + + privVal.SignVote(header.ChainID, vote) + + return vote +} + +func makeBlock(state sm.State, lastBlock *types.Block, lastBlockMeta *types.BlockMeta, + privVal types.PrivValidator, height int64) (*types.Block, *types.PartSet) { + + lastCommit := types.NewCommit(types.BlockID{}, nil) + if height > 1 { + vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVal).CommitSig() + lastCommit = types.NewCommit(lastBlockMeta.BlockID, []*types.CommitSig{vote}) + } + + return state.MakeBlock(height, []types.Tx{}, lastCommit, nil, state.Validators.GetProposer().Address) +} + +type badApp struct { + abci.BaseApplication + numBlocks byte + height byte + allHashesAreWrong bool + onlyLastHashIsWrong bool +} + +func (app *badApp) Commit() abci.ResponseCommit { + app.height++ + if app.onlyLastHashIsWrong { + if app.height == app.numBlocks { + return abci.ResponseCommit{Data: cmn.RandBytes(8)} + } + return abci.ResponseCommit{Data: []byte{app.height}} + } else if app.allHashesAreWrong { + return abci.ResponseCommit{Data: cmn.RandBytes(8)} + } + + panic("either allHashesAreWrong or onlyLastHashIsWrong must be set") +} + //-------------------------- // utils for making blocks func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) { + var height int64 + // Search for height marker - gr, found, err := wal.SearchForEndHeight(0, &WALSearchOptions{}) + gr, found, err := wal.SearchForEndHeight(height, &WALSearchOptions{}) if err != nil { return nil, nil, err } if !found { - return nil, nil, fmt.Errorf("WAL does not contain height %d.", 1) + return nil, nil, fmt.Errorf("WAL does not contain height %d", height) } defer gr.Close() // nolint: errcheck // log.Notice("Build a blockchain by reading from the WAL") - var blocks []*types.Block - var commits []*types.Commit - - var thisBlockParts *types.PartSet - var thisBlockCommit *types.Commit - var height int64 + var ( + blocks []*types.Block + commits []*types.Commit + thisBlockParts *types.PartSet + thisBlockCommit *types.Commit + ) dec := NewWALDecoder(gr) for { @@ -602,7 +742,7 @@ func stateAndStore(config *cfg.Config, pubKey crypto.PubKey, appVersion version. stateDB := dbm.NewMemDB() state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile()) state.Version.Consensus.App = appVersion - store := NewMockBlockStore(config, state.ConsensusParams) + store := newMockBlockStore(config, state.ConsensusParams) return stateDB, state, store } @@ -617,7 +757,7 @@ type mockBlockStore struct { } // TODO: NewBlockStore(db.NewMemDB) ... -func NewMockBlockStore(config *cfg.Config, params types.ConsensusParams) *mockBlockStore { +func newMockBlockStore(config *cfg.Config, params types.ConsensusParams) *mockBlockStore { return &mockBlockStore{config, params, nil, nil} } @@ -626,7 +766,7 @@ func (bs *mockBlockStore) LoadBlock(height int64) *types.Block { return bs.chain func (bs *mockBlockStore) LoadBlockMeta(height int64) *types.BlockMeta { block := bs.chain[height-1] return &types.BlockMeta{ - BlockID: types.BlockID{block.Hash(), block.MakePartSet(types.BlockPartSizeBytes).Header()}, + BlockID: types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(types.BlockPartSizeBytes).Header()}, Header: block.Header, } } @@ -640,15 +780,16 @@ func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit { return bs.commits[height-1] } -//---------------------------------------- +//--------------------------------------- +// Test handshake/init chain -func TestInitChainUpdateValidators(t *testing.T) { +func TestHandshakeUpdatesValidators(t *testing.T) { val, _ := types.RandValidator(true, 10) vals := types.NewValidatorSet([]*types.Validator{val}) app := &initChainApp{vals: types.TM2PB.ValidatorUpdates(vals)} clientCreator := proxy.NewLocalClientCreator(app) - config := ResetConfig("proxy_test_") + config := ResetConfig("handshake_test_") defer os.RemoveAll(config.RootDir) privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0) From 968e955c46e08a58e4319f9e3dedbe2e91317e8f Mon Sep 17 00:00:00 2001 From: Greg Hill Date: Tue, 23 Apr 2019 09:52:46 +0100 Subject: [PATCH 176/259] testnet cmd: add config option (#3559) Option to explicitly provide the -config in the testnet command, closing #3160. --- CHANGELOG_PENDING.md | 1 + cmd/tendermint/commands/testnet.go | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index e05430218..bfad14700 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -18,6 +18,7 @@ ### IMPROVEMENTS: - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC +- [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `-config=` option to `testnet` cmd (@gregdhill) - [cs/replay] \#3460 check appHash for each block ### BUG FIXES: diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index e34b8d305..b4b33e655 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/spf13/cobra" + "github.com/spf13/viper" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" @@ -20,6 +21,7 @@ import ( var ( nValidators int nNonValidators int + configFile string outputDir string nodeDirPrefix string @@ -36,6 +38,8 @@ const ( func init() { TestnetFilesCmd.Flags().IntVar(&nValidators, "v", 4, "Number of validators to initialize the testnet with") + TestnetFilesCmd.Flags().StringVar(&configFile, "config", "", + "Config file to use (note some options may be overwritten)") TestnetFilesCmd.Flags().IntVar(&nNonValidators, "n", 0, "Number of non-validators to initialize the testnet with") TestnetFilesCmd.Flags().StringVar(&outputDir, "o", "./mytestnet", @@ -73,6 +77,21 @@ Example: func testnetFiles(cmd *cobra.Command, args []string) error { config := cfg.DefaultConfig() + + // overwrite default config if set and valid + if configFile != "" { + viper.SetConfigFile(configFile) + if err := viper.ReadInConfig(); err != nil { + return err + } + if err := viper.Unmarshal(config); err != nil { + return err + } + if err := config.ValidateBasic(); err != nil { + return err + } + } + genVals := make([]types.GenesisValidator, nValidators) for i := 0; i < nValidators; i++ { From b738add80cb9f7e7e91cbad38ec6db297f3535e6 Mon Sep 17 00:00:00 2001 From: Zarko Milosevic Date: Tue, 23 Apr 2019 11:19:16 +0200 Subject: [PATCH 177/259] cs: fix nondeterministic tests (#3582) Should fix #3451, #2723 and #3317. Test TestResetTimeoutPrecommitUponNewHeight is simplified so it reduces a risk of timeout failure. Furthermore, timeout we wait for TimeoutEvents is increased, and the timeout value is more precisely computed. This should hopefully decrease a chance of non-deterministic test failures. This assertion is problematic to ensure consistently due to dependency on scheduler. On the other hand, if I am not wrong, order in which messages are read from the channel respects order in which messages are written. Therefore, process will receive 2f+1 precommits that are not all for v (one is for nil) so TriggeredTimeoutPrecommit would be set to true. So we don't need to assert it. I know that it would be better to still assert to it but I don't know how to do it without sleep and that is ugly and is causing us nondeterministic failures. --- CHANGELOG_PENDING.md | 1 + consensus/common_test.go | 4 +-- consensus/state_test.go | 54 +++++++++++++++++++--------------------- 3 files changed, 28 insertions(+), 31 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index bfad14700..2d2830094 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -25,3 +25,4 @@ - [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) +- [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests \ No newline at end of file diff --git a/consensus/common_test.go b/consensus/common_test.go index 5706f2317..8f305139e 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -351,7 +351,7 @@ func ensureNoNewUnlock(unlockCh <-chan tmpubsub.Message) { } func ensureNoNewTimeout(stepCh <-chan tmpubsub.Message, timeout int64) { - timeoutDuration := time.Duration(timeout*5) * time.Nanosecond + timeoutDuration := time.Duration(timeout*10) * time.Nanosecond ensureNoNewEvent( stepCh, timeoutDuration, @@ -398,7 +398,7 @@ func ensureNewRound(roundCh <-chan tmpubsub.Message, height int64, round int) { } func ensureNewTimeout(timeoutCh <-chan tmpubsub.Message, height int64, round int, timeout int64) { - timeoutDuration := time.Duration(timeout*5) * time.Nanosecond + timeoutDuration := time.Duration(timeout*10) * time.Nanosecond ensureNewEvent(timeoutCh, height, round, timeoutDuration, "Timeout expired while waiting for NewTimeout event") } diff --git a/consensus/state_test.go b/consensus/state_test.go index fc1e3e949..67b5cfbdc 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -370,7 +370,7 @@ func TestStateLockNoPOL(t *testing.T) { // (note we're entering precommit for a second time this round) // but with invalid args. then we enterPrecommitWait, and the timeout to new round - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) /// @@ -384,7 +384,7 @@ func TestStateLockNoPOL(t *testing.T) { incrementRound(vs2) // now we're on a new round and not the proposer, so wait for timeout - ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) rs := cs1.GetRoundState() @@ -403,7 +403,7 @@ func TestStateLockNoPOL(t *testing.T) { // now we're going to enter prevote again, but with invalid args // and then prevote wait, which should timeout. then wait for precommit - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) ensurePrecommit(voteCh, height, round) // precommit // the proposed block should still be locked and our precommit added @@ -416,7 +416,7 @@ func TestStateLockNoPOL(t *testing.T) { // (note we're entering precommit for a second time this round, but with invalid args // then we enterPrecommitWait and timeout into NewRound - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) round = round + 1 // entering new round ensureNewRound(newRoundCh, height, round) @@ -441,7 +441,7 @@ func TestStateLockNoPOL(t *testing.T) { signAddVotes(cs1, types.PrevoteType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) ensurePrevote(voteCh, height, round) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) ensurePrecommit(voteCh, height, round) // precommit validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but be locked on proposal @@ -449,7 +449,7 @@ func TestStateLockNoPOL(t *testing.T) { signAddVotes(cs1, types.PrecommitType, hash, rs.ProposalBlock.MakePartSet(partSize).Header(), vs2) // NOTE: conflicting precommits at same height ensurePrecommit(voteCh, height, round) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) cs2, _ := randConsensusState(2) // needed so generated block is different than locked block // before we time out into new round, set next proposal block @@ -482,7 +482,7 @@ func TestStateLockNoPOL(t *testing.T) { signAddVotes(cs1, types.PrevoteType, propBlock.Hash(), propBlock.MakePartSet(partSize).Header(), vs2) ensurePrevote(voteCh, height, round) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) ensurePrecommit(voteCh, height, round) validatePrecommit(t, cs1, round, 0, vss[0], nil, theBlockHash) // precommit nil but locked on proposal @@ -542,7 +542,7 @@ func TestStateLockPOLRelock(t *testing.T) { incrementRound(vs2, vs3, vs4) // timeout to new round - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) round = round + 1 // moving to the next round //XXX: this isnt guaranteed to get there before the timeoutPropose ... @@ -632,7 +632,7 @@ func TestStateLockPOLUnlock(t *testing.T) { propBlockParts := propBlock.MakePartSet(partSize) // timeout to new round - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) rs = cs1.GetRoundState() lockedBlockHash := rs.LockedBlock.Hash() @@ -710,7 +710,7 @@ func TestStateLockPOLSafety1(t *testing.T) { // cs1 precommit nil ensurePrecommit(voteCh, height, round) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) t.Log("### ONTO ROUND 1") @@ -754,7 +754,7 @@ func TestStateLockPOLSafety1(t *testing.T) { signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) incrementRound(vs2, vs3, vs4) round = round + 1 // moving to the next round @@ -767,7 +767,7 @@ func TestStateLockPOLSafety1(t *testing.T) { */ // timeout of propose - ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) // finish prevote ensurePrevote(voteCh, height, round) @@ -850,7 +850,7 @@ func TestStateLockPOLSafety2(t *testing.T) { incrementRound(vs2, vs3, vs4) // timeout of precommit wait to new round - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) round = round + 1 // moving to the next round // in round 2 we see the polkad block from round 0 @@ -919,7 +919,7 @@ func TestProposeValidBlock(t *testing.T) { signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) incrementRound(vs2, vs3, vs4) round = round + 1 // moving to the next round @@ -929,7 +929,7 @@ func TestProposeValidBlock(t *testing.T) { t.Log("### ONTO ROUND 2") // timeout of propose - ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) ensurePrevote(voteCh, height, round) validatePrevote(t, cs1, round, vss[0], propBlockHash) @@ -952,7 +952,7 @@ func TestProposeValidBlock(t *testing.T) { ensureNewRound(newRoundCh, height, round) t.Log("### ONTO ROUND 3") - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) round = round + 1 // moving to the next round @@ -1004,7 +1004,7 @@ func TestSetValidBlockOnDelayedPrevote(t *testing.T) { // vs3 send prevote nil signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs3) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) ensurePrecommit(voteCh, height, round) // we should have precommitted @@ -1052,7 +1052,7 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) { startTestRound(cs1, cs1.Height, round) ensureNewRound(newRoundCh, height, round) - ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) ensurePrevote(voteCh, height, round) validatePrevote(t, cs1, round, vss[0], nil) @@ -1065,7 +1065,7 @@ func TestSetValidBlockOnDelayedProposal(t *testing.T) { signAddVotes(cs1, types.PrevoteType, propBlockHash, propBlockParts.Header(), vs2, vs3, vs4) ensureNewValidBlock(validBlockCh, height, round) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrevote.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Prevote(round).Nanoseconds()) ensurePrecommit(voteCh, height, round) validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) @@ -1099,7 +1099,7 @@ func TestWaitingTimeoutOnNilPolka(t *testing.T) { signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2, vs3, vs4) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) ensureNewRound(newRoundCh, height, round+1) } @@ -1131,7 +1131,7 @@ func TestWaitingTimeoutProposeOnNewRound(t *testing.T) { rs := cs1.GetRoundState() assert.True(t, rs.Step == cstypes.RoundStepPropose) // P0 does not prevote before timeoutPropose expires - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Propose(round).Nanoseconds()) ensurePrevote(voteCh, height, round) validatePrevote(t, cs1, round, vss[0], nil) @@ -1165,7 +1165,7 @@ func TestRoundSkipOnNilPolkaFromHigherRound(t *testing.T) { ensurePrecommit(voteCh, height, round) validatePrecommit(t, cs1, round, -1, vss[0], nil, nil) - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) round = round + 1 // moving to the next round ensureNewRound(newRoundCh, height, round) @@ -1191,7 +1191,7 @@ func TestWaitTimeoutProposeOnNilPolkaForTheCurrentRound(t *testing.T) { incrementRound(vss[1:]...) signAddVotes(cs1, types.PrevoteType, nil, types.PartSetHeader{}, vs2, vs3, vs4) - ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutProposeCh, height, round, cs1.config.Propose(round).Nanoseconds()) ensurePrevote(voteCh, height, round) validatePrevote(t, cs1, round, vss[0], nil) @@ -1332,7 +1332,7 @@ func TestStartNextHeightCorrectly(t *testing.T) { cs1.txNotifier.(*fakeTxNotifier).Notify() - ensureNewTimeout(timeoutProposeCh, height+1, round, cs1.config.TimeoutPropose.Nanoseconds()) + ensureNewTimeout(timeoutProposeCh, height+1, round, cs1.config.Propose(round).Nanoseconds()) rs = cs1.GetRoundState() assert.False(t, rs.TriggeredTimeoutPrecommit, "triggeredTimeoutPrecommit should be false at the beginning of each round") } @@ -1375,12 +1375,8 @@ func TestResetTimeoutPrecommitUponNewHeight(t *testing.T) { // add precommits signAddVotes(cs1, types.PrecommitType, nil, types.PartSetHeader{}, vs2) signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs3) - time.Sleep(5 * time.Millisecond) signAddVotes(cs1, types.PrecommitType, theBlockHash, theBlockParts, vs4) - rs = cs1.GetRoundState() - assert.True(t, rs.TriggeredTimeoutPrecommit) - ensureNewBlockHeader(newBlockHeader, height, theBlockHash) prop, propBlock := decideProposal(cs1, vs2, height+1, 0) @@ -1519,7 +1515,7 @@ func TestStateHalt1(t *testing.T) { incrementRound(vs2, vs3, vs4) // timeout to new round - ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.TimeoutPrecommit.Nanoseconds()) + ensureNewTimeout(timeoutWaitCh, height, round, cs1.config.Precommit(round).Nanoseconds()) round = round + 1 // moving to the next round From 90997ab1b56d2acbd022d7f1d7923873d6bc2e3a Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 23 Apr 2019 05:34:14 -0400 Subject: [PATCH 178/259] docs: update contributing.md (#3503) Minor updates to reflect squash merging and how to prepare releases --- CONTRIBUTING.md | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3dab3b8ab..f2320fb4c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,10 +105,14 @@ removed from the header in rpc responses as well. ## Branching Model and Release -All repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/. +We follow a variant of [git flow](http://nvie.com/posts/a-successful-git-branching-model/). This means that all pull-requests should be made against develop. Any merge to master constitutes a tagged release. +Note all pull requests should be squash merged except for merging to master and +merging master back to develop. This keeps the commit history clean and makes it +easy to reference the pull request where a change was introduced. + ### Development Procedure: - the latest state of development is on `develop` - `develop` must never fail `make test` @@ -120,13 +124,13 @@ master constitutes a tagged release. ### Pull Merge Procedure: - ensure pull branch is based on a recent develop - run `make test` to ensure that all tests pass -- merge pull request +- squash merge pull request - the `unstable` branch may be used to aggregate pull merges before fixing tests ### Release Procedure: - start on `develop` - run integration tests (see `test_integrations` in Makefile) -- prepare changelog: +- prepare release in a pull request against develop (to be squash merged): - copy `CHANGELOG_PENDING.md` to top of `CHANGELOG.md` - run `python ./scripts/linkify_changelog.py CHANGELOG.md` to add links for all issues @@ -135,23 +139,15 @@ master constitutes a tagged release. the changelog. To lookup an alias from an email, try `bash ./scripts/authors.sh ` - reset the `CHANGELOG_PENDING.md` -- bump versions -- push to release/vX.X.X to run the extended integration tests on the CI -- merge to master -- merge master back to develop + - bump versions +- push latest develop with prepared release details to release/vX.X.X to run the extended integration tests on the CI +- if necessary, make pull requests against release/vX.X.X and squash merge them +- merge to master (don't squash merge!) +- merge master back to develop (don't squash merge!) ### Hotfix Procedure: -- start on `master` -- checkout a new branch named hotfix-vX.X.X -- make the required changes - - these changes should be small and an absolute necessity - - add a note to CHANGELOG.md -- bump versions -- push to hotfix-vX.X.X to run the extended integration tests on the CI -- merge hotfix-vX.X.X to master -- merge hotfix-vX.X.X to develop -- delete the hotfix-vX.X.X branch +- follow the normal development and release procedure without any differences ## Testing From 70592cc4d87af013db4358186c484e64ee974bb6 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Fri, 26 Apr 2019 06:23:43 -0400 Subject: [PATCH 179/259] libs/common: remove deprecated PanicXXX functions (#3595) * Remove deprecated PanicXXX functions from codebase As per discussion over [here](https://github.com/tendermint/tendermint/pull/3456#discussion_r278423492), we need to remove these `PanicXXX` functions and eliminate our dependence on them. In this PR, each and every `PanicXXX` function call is replaced with a simple `panic` call. * add a changelog entry --- CHANGELOG_PENDING.md | 3 ++- blockchain/store.go | 10 ++++----- config/toml.go | 6 ++--- consensus/reactor.go | 4 ++-- consensus/state.go | 30 ++++++++++++------------- consensus/types/height_vote_set.go | 8 +++---- crypto/xsalsa20symmetric/symmetric.go | 5 ++--- libs/common/errors.go | 32 --------------------------- libs/common/random.go | 2 +- libs/common/service.go | 3 +-- libs/db/go_level_db.go | 10 ++++----- node/node.go | 4 ++-- p2p/conn/connection.go | 2 +- p2p/netaddress.go | 4 +--- p2p/pex/file.go | 4 ++-- p2p/switch.go | 2 +- p2p/trust/store.go | 2 +- types/part_set.go | 2 +- types/validator.go | 3 +-- types/vote.go | 2 +- types/vote_set.go | 16 +++++++------- 21 files changed, 57 insertions(+), 97 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 2d2830094..6065ee9b8 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,6 +9,7 @@ * Apps * Go API +- [libs/common] Removed PanicSanity, PanicCrisis, PanicConsensus and PanicQ * Blockchain Protocol @@ -25,4 +26,4 @@ - [state] [\#3537](https://github.com/tendermint/tendermint/pull/3537#issuecomment-482711833) LoadValidators: do not return an empty validator set - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) -- [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests \ No newline at end of file +- [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests diff --git a/blockchain/store.go b/blockchain/store.go index 498cca68d..b7f4e07c8 100644 --- a/blockchain/store.go +++ b/blockchain/store.go @@ -144,14 +144,14 @@ func (bs *BlockStore) LoadSeenCommit(height int64) *types.Commit { // most recent height. Otherwise they'd stall at H-1. func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, seenCommit *types.Commit) { if block == nil { - cmn.PanicSanity("BlockStore can only save a non-nil block") + panic("BlockStore can only save a non-nil block") } height := block.Height if g, w := height, bs.Height()+1; g != w { - cmn.PanicSanity(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g)) + panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g)) } if !blockParts.IsComplete() { - cmn.PanicSanity(fmt.Sprintf("BlockStore can only save complete block part sets")) + panic(fmt.Sprintf("BlockStore can only save complete block part sets")) } // Save block meta @@ -188,7 +188,7 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s func (bs *BlockStore) saveBlockPart(height int64, index int, part *types.Part) { if height != bs.Height()+1 { - cmn.PanicSanity(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height)) + panic(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height)) } partBytes := cdc.MustMarshalBinaryBare(part) bs.db.Set(calcBlockPartKey(height, index), partBytes) @@ -224,7 +224,7 @@ type BlockStoreStateJSON struct { func (bsj BlockStoreStateJSON) Save(db dbm.DB) { bytes, err := cdc.MarshalJSON(bsj) if err != nil { - cmn.PanicSanity(fmt.Sprintf("Could not marshal state bytes: %v", err)) + panic(fmt.Sprintf("Could not marshal state bytes: %v", err)) } db.SetSync(blockStoreKey, bytes) } diff --git a/config/toml.go b/config/toml.go index 978255aba..f80f525e4 100644 --- a/config/toml.go +++ b/config/toml.go @@ -28,13 +28,13 @@ func init() { // and panics if it fails. func EnsureRoot(rootDir string) { if err := cmn.EnsureDir(rootDir, DefaultDirPerm); err != nil { - cmn.PanicSanity(err.Error()) + panic(err.Error()) } if err := cmn.EnsureDir(filepath.Join(rootDir, defaultConfigDir), DefaultDirPerm); err != nil { - cmn.PanicSanity(err.Error()) + panic(err.Error()) } if err := cmn.EnsureDir(filepath.Join(rootDir, defaultDataDir), DefaultDirPerm); err != nil { - cmn.PanicSanity(err.Error()) + panic(err.Error()) } configFilePath := filepath.Join(rootDir, defaultConfigFilePath) diff --git a/consensus/reactor.go b/consensus/reactor.go index 604e54b49..937a12351 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -491,7 +491,7 @@ OUTER_LOOP: if prs.ProposalBlockParts == nil { blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height) if blockMeta == nil { - cmn.PanicCrisis(fmt.Sprintf("Failed to load block %d when blockStore is at %d", + panic(fmt.Sprintf("Failed to load block %d when blockStore is at %d", prs.Height, conR.conS.blockStore.Height())) } ps.InitProposalBlockParts(blockMeta.BlockID.PartsHeader) @@ -1110,7 +1110,7 @@ func (ps *PeerState) ensureCatchupCommitRound(height int64, round int, numValida NOTE: This is wrong, 'round' could change. e.g. if orig round is not the same as block LastCommit round. if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round { - cmn.PanicSanity(fmt.Sprintf("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round)) + panic(fmt.Sprintf("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round)) } */ if ps.PRS.CatchupCommitRound == round { diff --git a/consensus/state.go b/consensus/state.go index 74ec092ff..a6256f82d 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -491,11 +491,11 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) { } added, err := lastPrecommits.AddVote(seenCommit.ToVote(precommit)) if !added || err != nil { - cmn.PanicCrisis(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) + panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) } } if !lastPrecommits.HasTwoThirdsMajority() { - cmn.PanicSanity("Failed to reconstruct LastCommit: Does not have +2/3 maj") + panic("Failed to reconstruct LastCommit: Does not have +2/3 maj") } cs.LastCommit = lastPrecommits } @@ -504,13 +504,13 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) { // The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight. func (cs *ConsensusState) updateToState(state sm.State) { if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { - cmn.PanicSanity(fmt.Sprintf("updateToState() expected state height of %v but found %v", + panic(fmt.Sprintf("updateToState() expected state height of %v but found %v", cs.Height, state.LastBlockHeight)) } if !cs.state.IsEmpty() && cs.state.LastBlockHeight+1 != cs.Height { // This might happen when someone else is mutating cs.state. // Someone forgot to pass in state.Copy() somewhere?! - cmn.PanicSanity(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v", + panic(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v", cs.state.LastBlockHeight+1, cs.Height)) } @@ -530,7 +530,7 @@ func (cs *ConsensusState) updateToState(state sm.State) { lastPrecommits := (*types.VoteSet)(nil) if cs.CommitRound > -1 && cs.Votes != nil { if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() { - cmn.PanicSanity("updateToState(state) called but last Precommit round didn't have +2/3") + panic("updateToState(state) called but last Precommit round didn't have +2/3") } lastPrecommits = cs.Votes.Precommits(cs.CommitRound) } @@ -1047,7 +1047,7 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) { return } if !cs.Votes.Prevotes(round).HasTwoThirdsAny() { - cmn.PanicSanity(fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round)) + panic(fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round)) } logger.Info(fmt.Sprintf("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) @@ -1103,7 +1103,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) { // the latest POLRound should be this round. polRound, _ := cs.Votes.POLInfo() if polRound < round { - cmn.PanicSanity(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound)) + panic(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound)) } // +2/3 prevoted nil. Unlock and precommit nil. @@ -1137,7 +1137,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) { logger.Info("enterPrecommit: +2/3 prevoted proposal block. Locking", "hash", blockID.Hash) // Validate the block. if err := cs.blockExec.ValidateBlock(cs.state, cs.ProposalBlock); err != nil { - cmn.PanicConsensus(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)) + panic(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err)) } cs.LockedRound = round cs.LockedBlock = cs.ProposalBlock @@ -1175,7 +1175,7 @@ func (cs *ConsensusState) enterPrecommitWait(height int64, round int) { return } if !cs.Votes.Precommits(round).HasTwoThirdsAny() { - cmn.PanicSanity(fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round)) + panic(fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round)) } logger.Info(fmt.Sprintf("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) @@ -1214,7 +1214,7 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) { blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() if !ok { - cmn.PanicSanity("RunActionCommit() expects +2/3 precommits") + panic("RunActionCommit() expects +2/3 precommits") } // The Locked* fields no longer matter. @@ -1247,7 +1247,7 @@ func (cs *ConsensusState) tryFinalizeCommit(height int64) { logger := cs.Logger.With("height", height) if cs.Height != height { - cmn.PanicSanity(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) + panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height)) } blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority() @@ -1277,16 +1277,16 @@ func (cs *ConsensusState) finalizeCommit(height int64) { block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts if !ok { - cmn.PanicSanity(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority")) + panic(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority")) } if !blockParts.HasHeader(blockID.PartsHeader) { - cmn.PanicSanity(fmt.Sprintf("Expected ProposalBlockParts header to be commit header")) + panic(fmt.Sprintf("Expected ProposalBlockParts header to be commit header")) } if !block.HashesTo(blockID.Hash) { - cmn.PanicSanity(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")) + panic(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash")) } if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil { - cmn.PanicConsensus(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) + panic(fmt.Sprintf("+2/3 committed an invalid block: %v", err)) } cs.Logger.Info(fmt.Sprintf("Finalizing commit of block with %d txs", block.NumTxs), diff --git a/consensus/types/height_vote_set.go b/consensus/types/height_vote_set.go index eee013eea..35c9a486d 100644 --- a/consensus/types/height_vote_set.go +++ b/consensus/types/height_vote_set.go @@ -6,7 +6,6 @@ import ( "strings" "sync" - cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/types" ) @@ -83,7 +82,7 @@ func (hvs *HeightVoteSet) SetRound(round int) { hvs.mtx.Lock() defer hvs.mtx.Unlock() if hvs.round != 0 && (round < hvs.round+1) { - cmn.PanicSanity("SetRound() must increment hvs.round") + panic("SetRound() must increment hvs.round") } for r := hvs.round + 1; r <= round; r++ { if _, ok := hvs.roundVoteSets[r]; ok { @@ -96,7 +95,7 @@ func (hvs *HeightVoteSet) SetRound(round int) { func (hvs *HeightVoteSet) addRound(round int) { if _, ok := hvs.roundVoteSets[round]; ok { - cmn.PanicSanity("addRound() for an existing round") + panic("addRound() for an existing round") } // log.Debug("addRound(round)", "round", round) prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrevoteType, hvs.valSet) @@ -169,8 +168,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ types.SignedMsgType) *type case types.PrecommitType: return rvs.Precommits default: - cmn.PanicSanity(fmt.Sprintf("Unexpected vote type %X", type_)) - return nil + panic(fmt.Sprintf("Unexpected vote type %X", type_)) } } diff --git a/crypto/xsalsa20symmetric/symmetric.go b/crypto/xsalsa20symmetric/symmetric.go index 10a0f6f33..73dc9dec5 100644 --- a/crypto/xsalsa20symmetric/symmetric.go +++ b/crypto/xsalsa20symmetric/symmetric.go @@ -7,7 +7,6 @@ import ( "golang.org/x/crypto/nacl/secretbox" "github.com/tendermint/tendermint/crypto" - cmn "github.com/tendermint/tendermint/libs/common" ) // TODO, make this into a struct that implements crypto.Symmetric. @@ -19,7 +18,7 @@ const secretLen = 32 // The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { if len(secret) != secretLen { - cmn.PanicSanity(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) + panic(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) } nonce := crypto.CRandBytes(nonceLen) nonceArr := [nonceLen]byte{} @@ -36,7 +35,7 @@ func EncryptSymmetric(plaintext []byte, secret []byte) (ciphertext []byte) { // The ciphertext is (secretbox.Overhead + 24) bytes longer than the plaintext. func DecryptSymmetric(ciphertext []byte, secret []byte) (plaintext []byte, err error) { if len(secret) != secretLen { - cmn.PanicSanity(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) + panic(fmt.Sprintf("Secret must be 32 bytes long, got len %v", len(secret))) } if len(ciphertext) <= secretbox.Overhead+nonceLen { return nil, errors.New("Ciphertext is too short") diff --git a/libs/common/errors.go b/libs/common/errors.go index 10e40ebd2..24af84267 100644 --- a/libs/common/errors.go +++ b/libs/common/errors.go @@ -212,35 +212,3 @@ func (fe FmtError) String() string { func (fe FmtError) Format() string { return fe.format } - -//---------------------------------------- -// Panic wrappers -// XXX DEPRECATED - -// A panic resulting from a sanity check means there is a programmer error -// and some guarantee is not satisfied. -// XXX DEPRECATED -func PanicSanity(v interface{}) { - panic(fmt.Sprintf("Panicked on a Sanity Check: %v", v)) -} - -// A panic here means something has gone horribly wrong, in the form of data corruption or -// failure of the operating system. In a correct/healthy system, these should never fire. -// If they do, it's indicative of a much more serious problem. -// XXX DEPRECATED -func PanicCrisis(v interface{}) { - panic(fmt.Sprintf("Panicked on a Crisis: %v", v)) -} - -// Indicates a failure of consensus. Someone was malicious or something has -// gone horribly wrong. These should really boot us into an "emergency-recover" mode -// XXX DEPRECATED -func PanicConsensus(v interface{}) { - panic(fmt.Sprintf("Panicked on a Consensus Failure: %v", v)) -} - -// For those times when we're not sure if we should panic -// XXX DEPRECATED -func PanicQ(v interface{}) { - panic(fmt.Sprintf("Panicked questionably: %v", v)) -} diff --git a/libs/common/random.go b/libs/common/random.go index 2de65945c..47e44d1c0 100644 --- a/libs/common/random.go +++ b/libs/common/random.go @@ -300,7 +300,7 @@ func cRandBytes(numBytes int) []byte { b := make([]byte, numBytes) _, err := crand.Read(b) if err != nil { - PanicCrisis(err) + panic(err) } return b } diff --git a/libs/common/service.go b/libs/common/service.go index 21fb0df3e..8eee48138 100644 --- a/libs/common/service.go +++ b/libs/common/service.go @@ -194,8 +194,7 @@ func (bs *BaseService) Reset() error { // OnReset implements Service by panicking. func (bs *BaseService) OnReset() error { - PanicSanity("The service cannot be reset") - return nil + panic("The service cannot be reset") } // IsRunning implements Service by returning true or false depending on the diff --git a/libs/db/go_level_db.go b/libs/db/go_level_db.go index 9a4358f60..79ee5ccb9 100644 --- a/libs/db/go_level_db.go +++ b/libs/db/go_level_db.go @@ -9,8 +9,6 @@ import ( "github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/iterator" "github.com/syndtr/goleveldb/leveldb/opt" - - cmn "github.com/tendermint/tendermint/libs/common" ) func init() { @@ -67,7 +65,7 @@ func (db *GoLevelDB) Set(key []byte, value []byte) { value = nonNilBytes(value) err := db.db.Put(key, value, nil) if err != nil { - cmn.PanicCrisis(err) + panic(err) } } @@ -77,7 +75,7 @@ func (db *GoLevelDB) SetSync(key []byte, value []byte) { value = nonNilBytes(value) err := db.db.Put(key, value, &opt.WriteOptions{Sync: true}) if err != nil { - cmn.PanicCrisis(err) + panic(err) } } @@ -86,7 +84,7 @@ func (db *GoLevelDB) Delete(key []byte) { key = nonNilBytes(key) err := db.db.Delete(key, nil) if err != nil { - cmn.PanicCrisis(err) + panic(err) } } @@ -95,7 +93,7 @@ func (db *GoLevelDB) DeleteSync(key []byte) { key = nonNilBytes(key) err := db.db.Delete(key, &opt.WriteOptions{Sync: true}) if err != nil { - cmn.PanicCrisis(err) + panic(err) } } diff --git a/node/node.go b/node/node.go index c0a4d736e..9ad7172fb 100644 --- a/node/node.go +++ b/node/node.go @@ -912,7 +912,7 @@ func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { var genDoc *types.GenesisDoc err := cdc.UnmarshalJSON(bytes, &genDoc) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes)) + panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes)) } return genDoc, nil } @@ -921,7 +921,7 @@ func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { bytes, err := cdc.MarshalJSON(genDoc) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) + panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) } db.SetSync(genesisDocKey, bytes) } diff --git a/p2p/conn/connection.go b/p2p/conn/connection.go index e0ce062ab..ee29fc85c 100644 --- a/p2p/conn/connection.go +++ b/p2p/conn/connection.go @@ -707,7 +707,7 @@ type Channel struct { func newChannel(conn *MConnection, desc ChannelDescriptor) *Channel { desc = desc.FillDefaults() if desc.Priority <= 0 { - cmn.PanicSanity("Channel default priority must be a positive integer") + panic("Channel default priority must be a positive integer") } return &Channel{ conn: conn, diff --git a/p2p/netaddress.go b/p2p/netaddress.go index 5534ded98..73130ee5e 100644 --- a/p2p/netaddress.go +++ b/p2p/netaddress.go @@ -14,8 +14,6 @@ import ( "time" "errors" - - cmn "github.com/tendermint/tendermint/libs/common" ) // NetAddress defines information about a peer on the network @@ -48,7 +46,7 @@ func NewNetAddress(id ID, addr net.Addr) *NetAddress { tcpAddr, ok := addr.(*net.TCPAddr) if !ok { if flag.Lookup("test.v") == nil { // normal run - cmn.PanicSanity(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr)) + panic(fmt.Sprintf("Only TCPAddrs are supported. Got: %v", addr)) } else { // in testing netAddr := NewNetAddressIPPort(net.IP("0.0.0.0"), 0) netAddr.ID = id diff --git a/p2p/pex/file.go b/p2p/pex/file.go index d4a516850..a42eddaf9 100644 --- a/p2p/pex/file.go +++ b/p2p/pex/file.go @@ -53,14 +53,14 @@ func (a *addrBook) loadFromFile(filePath string) bool { // Load addrBookJSON{} r, err := os.Open(filePath) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Error opening file %s: %v", filePath, err)) + panic(fmt.Sprintf("Error opening file %s: %v", filePath, err)) } defer r.Close() // nolint: errcheck aJSON := &addrBookJSON{} dec := json.NewDecoder(r) err = dec.Decode(aJSON) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Error reading file %s: %v", filePath, err)) + panic(fmt.Sprintf("Error reading file %s: %v", filePath, err)) } // Restore all the fields... diff --git a/p2p/switch.go b/p2p/switch.go index afd7d9656..a566e6029 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -155,7 +155,7 @@ func (sw *Switch) AddReactor(name string, reactor Reactor) Reactor { for _, chDesc := range reactorChannels { chID := chDesc.ID if sw.reactorsByCh[chID] != nil { - cmn.PanicSanity(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chID, sw.reactorsByCh[chID], reactor)) + panic(fmt.Sprintf("Channel %X has multiple reactors %v & %v", chID, sw.reactorsByCh[chID], reactor)) } sw.chDescs = append(sw.chDescs, chDesc) sw.reactorsByCh[chID] = reactor diff --git a/p2p/trust/store.go b/p2p/trust/store.go index d6b4c049d..fc1ad399e 100644 --- a/p2p/trust/store.go +++ b/p2p/trust/store.go @@ -156,7 +156,7 @@ func (tms *TrustMetricStore) loadFromDB() bool { peers := make(map[string]MetricHistoryJSON) err := json.Unmarshal(bytes, &peers) if err != nil { - cmn.PanicCrisis(fmt.Sprintf("Could not unmarshal Trust Metric Store DB data: %v", err)) + panic(fmt.Sprintf("Could not unmarshal Trust Metric Store DB data: %v", err)) } // If history data exists in the file, diff --git a/types/part_set.go b/types/part_set.go index 4533fb759..389db7a0b 100644 --- a/types/part_set.go +++ b/types/part_set.go @@ -226,7 +226,7 @@ func (ps *PartSet) IsComplete() bool { func (ps *PartSet) GetReader() io.Reader { if !ps.IsComplete() { - cmn.PanicSanity("Cannot GetReader() on incomplete PartSet") + panic("Cannot GetReader() on incomplete PartSet") } return NewPartSetReader(ps.parts) } diff --git a/types/validator.go b/types/validator.go index 325d20f5c..a662eb6c0 100644 --- a/types/validator.go +++ b/types/validator.go @@ -52,8 +52,7 @@ func (v *Validator) CompareProposerPriority(other *Validator) *Validator { } else if result > 0 { return other } else { - cmn.PanicSanity("Cannot compare identical validators") - return nil + panic("Cannot compare identical validators") } } } diff --git a/types/vote.go b/types/vote.go index ad05d688d..6fcbd3ff6 100644 --- a/types/vote.go +++ b/types/vote.go @@ -93,7 +93,7 @@ func (vote *Vote) String() string { case PrecommitType: typeString = "Precommit" default: - cmn.PanicSanity("Unknown vote type") + panic("Unknown vote type") } return fmt.Sprintf("Vote{%v:%X %v/%02d/%v(%v) %X %X @ %s}", diff --git a/types/vote_set.go b/types/vote_set.go index 1cd0f2281..2c896b39c 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -70,7 +70,7 @@ type VoteSet struct { // Constructs a new VoteSet struct used to accumulate votes for given height/round. func NewVoteSet(chainID string, height int64, round int, type_ SignedMsgType, valSet *ValidatorSet) *VoteSet { if height == 0 { - cmn.PanicSanity("Cannot make VoteSet for height == 0, doesn't make sense.") + panic("Cannot make VoteSet for height == 0, doesn't make sense.") } return &VoteSet{ chainID: chainID, @@ -130,7 +130,7 @@ func (voteSet *VoteSet) Size() int { // NOTE: Vote must not be nil func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) { if voteSet == nil { - cmn.PanicSanity("AddVote() on nil VoteSet") + panic("AddVote() on nil VoteSet") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -196,7 +196,7 @@ func (voteSet *VoteSet) addVote(vote *Vote) (added bool, err error) { return added, NewConflictingVoteError(val, conflicting, vote) } if !added { - cmn.PanicSanity("Expected to add non-conflicting vote") + panic("Expected to add non-conflicting vote") } return added, nil } @@ -220,7 +220,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower // Already exists in voteSet.votes? if existing := voteSet.votes[valIndex]; existing != nil { if existing.BlockID.Equals(vote.BlockID) { - cmn.PanicSanity("addVerifiedVote does not expect duplicate votes") + panic("addVerifiedVote does not expect duplicate votes") } else { conflicting = existing } @@ -290,7 +290,7 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower // NOTE: VoteSet must not be nil func (voteSet *VoteSet) SetPeerMaj23(peerID P2PID, blockID BlockID) error { if voteSet == nil { - cmn.PanicSanity("SetPeerMaj23() on nil VoteSet") + panic("SetPeerMaj23() on nil VoteSet") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() @@ -363,7 +363,7 @@ func (voteSet *VoteSet) GetByAddress(address []byte) *Vote { defer voteSet.mtx.Unlock() valIndex, val := voteSet.valSet.GetByAddress(address) if val == nil { - cmn.PanicSanity("GetByAddress(address) returned nil") + panic("GetByAddress(address) returned nil") } return voteSet.votes[valIndex] } @@ -530,14 +530,14 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) { func (voteSet *VoteSet) MakeCommit() *Commit { if voteSet.type_ != PrecommitType { - cmn.PanicSanity("Cannot MakeCommit() unless VoteSet.Type is PrecommitType") + panic("Cannot MakeCommit() unless VoteSet.Type is PrecommitType") } voteSet.mtx.Lock() defer voteSet.mtx.Unlock() // Make sure we have a 2/3 majority if voteSet.maj23 == nil { - cmn.PanicSanity("Cannot MakeCommit() unless a blockhash has +2/3") + panic("Cannot MakeCommit() unless a blockhash has +2/3") } // For every validator, get the precommit From 7b162f5c54267d716ef2e66912d59a0bd993a84f Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Fri, 26 Apr 2019 08:05:39 -0400 Subject: [PATCH 180/259] node: refactor node.NewNode (#3456) The node.NewNode method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the node.TestCreateProposalBlock test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. See also this gist https://gist.github.com/thanethomson/56e1640d057a26186e38ad678a1d114c for some background work done when starting to refactor here. ## Commits: * [WIP] Refactor node.NewNode to simplify The `node.NewNode` method is pretty complex at the moment, an in order to address issues like #3156, we need to simplify the interface for partial node instantiation. In some places, we don't need to build up a full node (like in the `node.TestCreateProposalBlock` test), but the complexity of such partial instantiation needs to be reduced. This PR aims to eventually make this easier/simpler. * Refactor state loading and genesis doc provider into state package * Refactor for clarity of return parameters * Fix incorrect capitalization of error messages * Simplify extracted functions' names * Document optionally-prefixed functions * Refactor optionallyFastSync for clarity of separation of concerns * Restructure function for early return * Restructure function for early return * Remove dependence on deprecated panic functions * refactor code a bit more plus, expose PEXReactor on node * align logger names * add a changelog entry * align logger names 2 * add a note about PEXReactor returning nil --- CHANGELOG_PENDING.md | 3 +- node/node.go | 458 +++++++++++++++++++++++++------------------ rpc/test/helpers.go | 3 +- state/store.go | 65 ++++++ 4 files changed, 331 insertions(+), 198 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 6065ee9b8..1dc44e686 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,7 +9,8 @@ * Apps * Go API -- [libs/common] Removed PanicSanity, PanicCrisis, PanicConsensus and PanicQ +- [libs/common] Removed `PanicSanity`, `PanicCrisis`, `PanicConsensus` and `PanicQ` +- [node] Moved `GenesisDocProvider` and `DefaultGenesisDocProviderFunc` to state package * Blockchain Protocol diff --git a/node/node.go b/node/node.go index 9ad7172fb..36bdb9d57 100644 --- a/node/node.go +++ b/node/node.go @@ -18,8 +18,10 @@ import ( amino "github.com/tendermint/go-amino" abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/blockchain" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/consensus" cs "github.com/tendermint/tendermint/consensus" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/evidence" @@ -63,19 +65,6 @@ func DefaultDBProvider(ctx *DBContext) (dbm.DB, error) { return dbm.NewDB(ctx.ID, dbType, ctx.Config.DBDir()), nil } -// GenesisDocProvider returns a GenesisDoc. -// It allows the GenesisDoc to be pulled from sources other than the -// filesystem, for instance from a distributed key-value store cluster. -type GenesisDocProvider func() (*types.GenesisDoc, error) - -// DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads -// the GenesisDoc from the config.GenesisFile() on the filesystem. -func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { - return func() (*types.GenesisDoc, error) { - return types.GenesisDocFromFile(config.GenesisFile()) - } -} - // NodeProvider takes a config and a logger and returns a ready to go Node. type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) @@ -96,7 +85,7 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { if _, err := os.Stat(oldPrivVal); !os.IsNotExist(err) { oldPV, err := privval.LoadOldFilePV(oldPrivVal) if err != nil { - return nil, fmt.Errorf("Error reading OldPrivValidator from %v: %v\n", oldPrivVal, err) + return nil, fmt.Errorf("error reading OldPrivValidator from %v: %v\n", oldPrivVal, err) } logger.Info("Upgrading PrivValidator file", "old", oldPrivVal, @@ -110,7 +99,7 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { privval.LoadOrGenFilePV(newPrivValKey, newPrivValState), nodeKey, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), - DefaultGenesisDocProviderFunc(config), + sm.DefaultGenesisDocProviderFunc(config), DefaultDBProvider, DefaultMetricsProvider(config.Instrumentation), logger, @@ -162,6 +151,7 @@ type Node struct { mempoolReactor *mempl.MempoolReactor // for gossipping transactions consensusState *cs.ConsensusState // latest consensus state consensusReactor *cs.ConsensusReactor // for participating in the consensus + pexReactor *pex.PEXReactor // for exchanging peer addresses evidencePool *evidence.EvidencePool // tracking evidence proxyApp proxy.AppConns // connection to the application rpcListeners []net.Listener // rpc servers @@ -170,73 +160,49 @@ type Node struct { prometheusSrv *http.Server } -// NewNode returns a new, ready to go, Tendermint Node. -func NewNode(config *cfg.Config, - privValidator types.PrivValidator, - nodeKey *p2p.NodeKey, - clientCreator proxy.ClientCreator, - genesisDocProvider GenesisDocProvider, - dbProvider DBProvider, - metricsProvider MetricsProvider, - logger log.Logger) (*Node, error) { - - // Get BlockStore - blockStoreDB, err := dbProvider(&DBContext{"blockstore", config}) +func initDBs(config *cfg.Config, dbProvider DBProvider) (blockStore *bc.BlockStore, stateDB dbm.DB, err error) { + var blockStoreDB dbm.DB + blockStoreDB, err = dbProvider(&DBContext{"blockstore", config}) if err != nil { - return nil, err + return } - blockStore := bc.NewBlockStore(blockStoreDB) + blockStore = bc.NewBlockStore(blockStoreDB) - // Get State - stateDB, err := dbProvider(&DBContext{"state", config}) + stateDB, err = dbProvider(&DBContext{"state", config}) if err != nil { - return nil, err + return } - // Get genesis doc - // TODO: move to state package? - genDoc, err := loadGenesisDoc(stateDB) - if err != nil { - genDoc, err = genesisDocProvider() - if err != nil { - return nil, err - } - // save genesis doc to prevent a certain class of user errors (e.g. when it - // was changed, accidentally or not). Also good for audit trail. - saveGenesisDoc(stateDB, genDoc) - } + return +} - state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) - if err != nil { - return nil, err - } - - // Create the proxyApp and establish connections to the ABCI app (consensus, mempool, query). +func createAndStartProxyAppConns(clientCreator proxy.ClientCreator, logger log.Logger) (proxy.AppConns, error) { proxyApp := proxy.NewAppConns(clientCreator) proxyApp.SetLogger(logger.With("module", "proxy")) if err := proxyApp.Start(); err != nil { - return nil, fmt.Errorf("Error starting proxy app connections: %v", err) + return nil, fmt.Errorf("error starting proxy app connections: %v", err) } + return proxyApp, nil +} - // EventBus and IndexerService must be started before the handshake because - // we might need to index the txs of the replayed block as this might not have happened - // when the node stopped last time (i.e. the node stopped after it saved the block - // but before it indexed the txs, or, endblocker panicked) +func createAndStartEventBus(logger log.Logger) (*types.EventBus, error) { eventBus := types.NewEventBus() eventBus.SetLogger(logger.With("module", "events")) - - err = eventBus.Start() - if err != nil { + if err := eventBus.Start(); err != nil { return nil, err } + return eventBus, nil +} + +func createAndStartIndexerService(config *cfg.Config, dbProvider DBProvider, + eventBus *types.EventBus, logger log.Logger) (*txindex.IndexerService, txindex.TxIndexer, error) { - // Transaction indexing var txIndexer txindex.TxIndexer switch config.TxIndex.Indexer { case "kv": store, err := dbProvider(&DBContext{"tx_index", config}) if err != nil { - return nil, err + return nil, nil, err } if config.TxIndex.IndexTags != "" { txIndexer = kv.NewTxIndex(store, kv.IndexTags(splitAndTrimEmpty(config.TxIndex.IndexTags, ",", " "))) @@ -251,26 +217,26 @@ func NewNode(config *cfg.Config, indexerService := txindex.NewIndexerService(txIndexer, eventBus) indexerService.SetLogger(logger.With("module", "txindex")) - - err = indexerService.Start() - if err != nil { - return nil, err + if err := indexerService.Start(); err != nil { + return nil, nil, err } + return indexerService, txIndexer, nil +} + +func doHandshake(stateDB dbm.DB, state sm.State, blockStore sm.BlockStore, + genDoc *types.GenesisDoc, eventBus *types.EventBus, proxyApp proxy.AppConns, consensusLogger log.Logger) error { - // Create the handshaker, which calls RequestInfo, sets the AppVersion on the state, - // and replays any blocks as necessary to sync tendermint with the app. - consensusLogger := logger.With("module", "consensus") handshaker := cs.NewHandshaker(stateDB, state, blockStore, genDoc) handshaker.SetLogger(consensusLogger) handshaker.SetEventBus(eventBus) if err := handshaker.Handshake(proxyApp); err != nil { - return nil, fmt.Errorf("Error during handshake: %v", err) + return fmt.Errorf("error during handshake: %v", err) } + return nil +} - // Reload the state. It will have the Version.Consensus.App set by the - // Handshake, and may have other modifications as well (ie. depending on - // what happened during block replay). - state = sm.LoadState(stateDB) +func logNodeStartupInfo(state sm.State, privValidator types.PrivValidator, logger, + consensusLogger log.Logger) { // Log the version info. logger.Info("Version info", @@ -287,27 +253,6 @@ func NewNode(config *cfg.Config, ) } - if config.PrivValidatorListenAddr != "" { - // If an address is provided, listen on the socket for a connection from an - // external signing process. - // FIXME: we should start services inside OnStart - privValidator, err = createAndStartPrivValidatorSocketClient(config.PrivValidatorListenAddr, logger) - if err != nil { - return nil, errors.Wrap(err, "Error with private validator socket client") - } - } - - // Decide whether to fast-sync or not - // We don't fast-sync when the only validator is us. - fastSync := config.FastSync - if state.Validators.Size() == 1 { - addr, _ := state.Validators.GetByIndex(0) - privValAddr := privValidator.GetPubKey().Address() - if bytes.Equal(privValAddr, addr) { - fastSync = false - } - } - pubKey := privValidator.GetPubKey() addr := pubKey.Address() // Log whether this node is a validator or an observer @@ -316,10 +261,19 @@ func NewNode(config *cfg.Config, } else { consensusLogger.Info("This node is not a validator", "addr", addr, "pubKey", pubKey) } +} - csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider(genDoc.ChainID) +func onlyValidatorIsUs(state sm.State, privVal types.PrivValidator) bool { + if state.Validators.Size() > 1 { + return false + } + addr, _ := state.Validators.GetByIndex(0) + return bytes.Equal(privVal.GetPubKey().Address(), addr) +} + +func createMempoolAndMempoolReactor(config *cfg.Config, proxyApp proxy.AppConns, + state sm.State, memplMetrics *mempl.Metrics, logger log.Logger) (*mempl.MempoolReactor, *mempl.Mempool) { - // Make MempoolReactor mempool := mempl.NewMempool( config.Mempool, proxyApp.Mempool(), @@ -339,34 +293,36 @@ func NewNode(config *cfg.Config, if config.Consensus.WaitForTxs() { mempool.EnableTxsAvailable() } + return mempoolReactor, mempool +} + +func createEvidenceReactor(config *cfg.Config, dbProvider DBProvider, + stateDB dbm.DB, logger log.Logger) (*evidence.EvidenceReactor, *evidence.EvidencePool, error) { - // Make Evidence Reactor evidenceDB, err := dbProvider(&DBContext{"evidence", config}) if err != nil { - return nil, err + return nil, nil, err } evidenceLogger := logger.With("module", "evidence") evidencePool := evidence.NewEvidencePool(stateDB, evidenceDB) evidencePool.SetLogger(evidenceLogger) evidenceReactor := evidence.NewEvidenceReactor(evidencePool) evidenceReactor.SetLogger(evidenceLogger) + return evidenceReactor, evidencePool, nil +} - blockExecLogger := logger.With("module", "state") - // make block executor for consensus and blockchain reactors to execute blocks - blockExec := sm.NewBlockExecutor( - stateDB, - blockExecLogger, - proxyApp.Consensus(), - mempool, - evidencePool, - sm.BlockExecutorWithMetrics(smMetrics), - ) +func createConsensusReactor(config *cfg.Config, + state sm.State, + blockExec *sm.BlockExecutor, + blockStore sm.BlockStore, + mempool *mempl.Mempool, + evidencePool *evidence.EvidencePool, + privValidator types.PrivValidator, + csMetrics *cs.Metrics, + fastSync bool, + eventBus *types.EventBus, + consensusLogger log.Logger) (*consensus.ConsensusReactor, *consensus.ConsensusState) { - // Make BlockchainReactor - bcReactor := bc.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) - bcReactor.SetLogger(logger.With("module", "blockchain")) - - // Make ConsensusReactor consensusState := cs.NewConsensusState( config.Consensus, state.Copy(), @@ -382,28 +338,13 @@ func NewNode(config *cfg.Config, } consensusReactor := cs.NewConsensusReactor(consensusState, fastSync, cs.ReactorMetrics(csMetrics)) consensusReactor.SetLogger(consensusLogger) - // services which will be publishing and/or subscribing for messages (events) // consensusReactor will set it on consensusState and blockExecutor consensusReactor.SetEventBus(eventBus) + return consensusReactor, consensusState +} - p2pLogger := logger.With("module", "p2p") - nodeInfo, err := makeNodeInfo( - config, - nodeKey.ID(), - txIndexer, - genDoc.ChainID, - p2p.NewProtocolVersion( - version.P2PProtocol, // global - state.Version.Consensus.Block, - state.Version.Consensus.App, - ), - ) - if err != nil { - return nil, err - } - - // Setup Transport. +func createTransport(config *cfg.Config, nodeInfo p2p.NodeInfo, nodeKey *p2p.NodeKey, proxyApp proxy.AppConns) (*p2p.MultiplexTransport, []p2p.PeerFilterFunc) { var ( mConnConfig = p2p.MConnConfig(config.P2P) transport = p2p.NewMultiplexTransport(nodeInfo, *nodeKey, mConnConfig) @@ -429,7 +370,7 @@ func NewNode(config *cfg.Config, return err } if res.IsErr() { - return fmt.Errorf("Error querying abci app: %v", res) + return fmt.Errorf("error querying abci app: %v", res) } return nil @@ -447,7 +388,7 @@ func NewNode(config *cfg.Config, return err } if res.IsErr() { - return fmt.Errorf("Error querying abci app: %v", res) + return fmt.Errorf("error querying abci app: %v", res) } return nil @@ -456,8 +397,21 @@ func NewNode(config *cfg.Config, } p2p.MultiplexTransportConnFilters(connFilters...)(transport) + return transport, peerFilters +} + +func createSwitch(config *cfg.Config, + transport *p2p.MultiplexTransport, + p2pMetrics *p2p.Metrics, + peerFilters []p2p.PeerFilterFunc, + mempoolReactor *mempl.MempoolReactor, + bcReactor *blockchain.BlockchainReactor, + consensusReactor *consensus.ConsensusReactor, + evidenceReactor *evidence.EvidenceReactor, + nodeInfo p2p.NodeInfo, + nodeKey *p2p.NodeKey, + p2pLogger log.Logger) *p2p.Switch { - // Setup Switch. sw := p2p.NewSwitch( config.P2P, transport, @@ -473,6 +427,159 @@ func NewNode(config *cfg.Config, sw.SetNodeKey(nodeKey) p2pLogger.Info("P2P Node ID", "ID", nodeKey.ID(), "file", config.NodeKeyFile()) + return sw +} + +func createAddrBookAndSetOnSwitch(config *cfg.Config, sw *p2p.Switch, + p2pLogger log.Logger) pex.AddrBook { + + addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) + addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) + + // Add ourselves to addrbook to prevent dialing ourselves + addrBook.AddOurAddress(sw.NetAddress()) + + sw.SetAddrBook(addrBook) + + return addrBook +} + +func createPEXReactorAndAddToSwitch(addrBook pex.AddrBook, config *cfg.Config, + sw *p2p.Switch, logger log.Logger) *pex.PEXReactor { + + // TODO persistent peers ? so we can have their DNS addrs saved + pexReactor := pex.NewPEXReactor(addrBook, + &pex.PEXReactorConfig{ + Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "), + SeedMode: config.P2P.SeedMode, + // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000 + // blocks assuming 10s blocks ~ 28 hours. + // TODO (melekes): make it dynamic based on the actual block latencies + // from the live network. + // https://github.com/tendermint/tendermint/issues/3523 + SeedDisconnectWaitPeriod: 28 * time.Hour, + }) + pexReactor.SetLogger(logger.With("module", "pex")) + sw.AddReactor("PEX", pexReactor) + return pexReactor +} + +// NewNode returns a new, ready to go, Tendermint Node. +func NewNode(config *cfg.Config, + privValidator types.PrivValidator, + nodeKey *p2p.NodeKey, + clientCreator proxy.ClientCreator, + genesisDocProvider sm.GenesisDocProvider, + dbProvider DBProvider, + metricsProvider MetricsProvider, + logger log.Logger) (*Node, error) { + + blockStore, stateDB, err := initDBs(config, dbProvider) + if err != nil { + return nil, err + } + + state, genDoc, err := sm.LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider) + if err != nil { + return nil, err + } + + // Create the proxyApp and establish connections to the ABCI app (consensus, mempool, query). + proxyApp, err := createAndStartProxyAppConns(clientCreator, logger) + if err != nil { + return nil, err + } + + // EventBus and IndexerService must be started before the handshake because + // we might need to index the txs of the replayed block as this might not have happened + // when the node stopped last time (i.e. the node stopped after it saved the block + // but before it indexed the txs, or, endblocker panicked) + eventBus, err := createAndStartEventBus(logger) + if err != nil { + return nil, err + } + + // Transaction indexing + indexerService, txIndexer, err := createAndStartIndexerService(config, dbProvider, eventBus, logger) + if err != nil { + return nil, err + } + + // Create the handshaker, which calls RequestInfo, sets the AppVersion on the state, + // and replays any blocks as necessary to sync tendermint with the app. + consensusLogger := logger.With("module", "consensus") + if err := doHandshake(stateDB, state, blockStore, genDoc, eventBus, proxyApp, consensusLogger); err != nil { + return nil, err + } + + // Reload the state. It will have the Version.Consensus.App set by the + // Handshake, and may have other modifications as well (ie. depending on + // what happened during block replay). + state = sm.LoadState(stateDB) + + // If an address is provided, listen on the socket for a connection from an + // external signing process. + if config.PrivValidatorListenAddr != "" { + // FIXME: we should start services inside OnStart + privValidator, err = createAndStartPrivValidatorSocketClient(config.PrivValidatorListenAddr, logger) + if err != nil { + return nil, errors.Wrap(err, "error with private validator socket client") + } + } + + logNodeStartupInfo(state, privValidator, logger, consensusLogger) + + // Decide whether to fast-sync or not + // We don't fast-sync when the only validator is us. + fastSync := config.FastSync && !onlyValidatorIsUs(state, privValidator) + + csMetrics, p2pMetrics, memplMetrics, smMetrics := metricsProvider(genDoc.ChainID) + + // Make MempoolReactor + mempoolReactor, mempool := createMempoolAndMempoolReactor(config, proxyApp, state, memplMetrics, logger) + + // Make Evidence Reactor + evidenceReactor, evidencePool, err := createEvidenceReactor(config, dbProvider, stateDB, logger) + if err != nil { + return nil, err + } + + // make block executor for consensus and blockchain reactors to execute blocks + blockExec := sm.NewBlockExecutor( + stateDB, + logger.With("module", "state"), + proxyApp.Consensus(), + mempool, + evidencePool, + sm.BlockExecutorWithMetrics(smMetrics), + ) + + // Make BlockchainReactor + bcReactor := bc.NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync) + bcReactor.SetLogger(logger.With("module", "blockchain")) + + // Make ConsensusReactor + consensusReactor, consensusState := createConsensusReactor( + config, state, blockExec, blockStore, mempool, evidencePool, + privValidator, csMetrics, fastSync, eventBus, consensusLogger, + ) + + nodeInfo, err := makeNodeInfo(config, nodeKey, txIndexer, genDoc, state) + if err != nil { + return nil, err + } + + // Setup Transport. + transport, peerFilters := createTransport(config, nodeInfo, nodeKey, proxyApp) + + // Setup Switch. + p2pLogger := logger.With("module", "p2p") + sw := createSwitch( + config, transport, p2pMetrics, peerFilters, mempoolReactor, bcReactor, + consensusReactor, evidenceReactor, nodeInfo, nodeKey, p2pLogger, + ) + + addrBook := createAddrBookAndSetOnSwitch(config, sw, p2pLogger) // Optionally, start the pex reactor // @@ -486,37 +593,13 @@ func NewNode(config *cfg.Config, // // If PEX is on, it should handle dialing the seeds. Otherwise the switch does it. // Note we currently use the addrBook regardless at least for AddOurAddress - addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) - - // Add ourselves to addrbook to prevent dialing ourselves - addrBook.AddOurAddress(sw.NetAddress()) - - addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) + var pexReactor *pex.PEXReactor if config.P2P.PexReactor { - // TODO persistent peers ? so we can have their DNS addrs saved - pexReactor := pex.NewPEXReactor(addrBook, - &pex.PEXReactorConfig{ - Seeds: splitAndTrimEmpty(config.P2P.Seeds, ",", " "), - SeedMode: config.P2P.SeedMode, - // See consensus/reactor.go: blocksToContributeToBecomeGoodPeer 10000 - // blocks assuming 10s blocks ~ 28 hours. - // TODO (melekes): make it dynamic based on the actual block latencies - // from the live network. - // https://github.com/tendermint/tendermint/issues/3523 - SeedDisconnectWaitPeriod: 28 * time.Hour, - }) - pexReactor.SetLogger(logger.With("module", "pex")) - sw.AddReactor("PEX", pexReactor) + pexReactor = createPEXReactorAndAddToSwitch(addrBook, config, sw, logger) } - sw.SetAddrBook(addrBook) - - // run the profile server - profileHost := config.ProfListenAddress - if profileHost != "" { - go func() { - logger.Error("Profile server", "err", http.ListenAndServe(profileHost, nil)) - }() + if config.ProfListenAddress != "" { + go logger.Error("Profile server", "err", http.ListenAndServe(config.ProfListenAddress, nil)) } node := &Node{ @@ -536,6 +619,7 @@ func NewNode(config *cfg.Config, mempoolReactor: mempoolReactor, consensusState: consensusState, consensusReactor: consensusReactor, + pexReactor: pexReactor, evidencePool: evidencePool, proxyApp: proxyApp, txIndexer: txIndexer, @@ -804,6 +888,11 @@ func (n *Node) MempoolReactor() *mempl.MempoolReactor { return n.mempoolReactor } +// PEXReactor returns the Node's PEXReactor. It returns nil if PEX is disabled. +func (n *Node) PEXReactor() *pex.PEXReactor { + return n.pexReactor +} + // EvidencePool returns the Node's EvidencePool. func (n *Node) EvidencePool() *evidence.EvidencePool { return n.evidencePool @@ -854,20 +943,24 @@ func (n *Node) NodeInfo() p2p.NodeInfo { func makeNodeInfo( config *cfg.Config, - nodeID p2p.ID, + nodeKey *p2p.NodeKey, txIndexer txindex.TxIndexer, - chainID string, - protocolVersion p2p.ProtocolVersion, + genDoc *types.GenesisDoc, + state sm.State, ) (p2p.NodeInfo, error) { txIndexerStatus := "on" if _, ok := txIndexer.(*null.TxIndex); ok { txIndexerStatus = "off" } nodeInfo := p2p.DefaultNodeInfo{ - ProtocolVersion: protocolVersion, - ID_: nodeID, - Network: chainID, - Version: version.TMCoreSemVer, + ProtocolVersion: p2p.NewProtocolVersion( + version.P2PProtocol, // global + state.Version.Consensus.Block, + state.Version.Consensus.App, + ), + ID_: nodeKey.ID(), + Network: genDoc.ChainID, + Version: version.TMCoreSemVer, Channels: []byte{ bc.BlockchainChannel, cs.StateChannel, cs.DataChannel, cs.VoteChannel, cs.VoteSetBitsChannel, @@ -899,33 +992,6 @@ func makeNodeInfo( //------------------------------------------------------------------------------ -var ( - genesisDocKey = []byte("genesisDoc") -) - -// panics if failed to unmarshal bytes -func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { - bytes := db.Get(genesisDocKey) - if len(bytes) == 0 { - return nil, errors.New("Genesis doc not found") - } - var genDoc *types.GenesisDoc - err := cdc.UnmarshalJSON(bytes, &genDoc) - if err != nil { - panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes)) - } - return genDoc, nil -} - -// panics if failed to marshal the given genesis document -func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { - bytes, err := cdc.MarshalJSON(genDoc) - if err != nil { - panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) - } - db.SetSync(genesisDocKey, bytes) -} - func createAndStartPrivValidatorSocketClient( listenAddr string, logger log.Logger, @@ -946,7 +1012,7 @@ func createAndStartPrivValidatorSocketClient( listener = privval.NewTCPListener(ln, ed25519.GenPrivKey()) default: return nil, fmt.Errorf( - "Wrong listen address: expected either 'tcp' or 'unix' protocols, got %s", + "wrong listen address: expected either 'tcp' or 'unix' protocols, got %s", protocol, ) } diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index cddc80b8e..f5c923bf4 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -9,6 +9,7 @@ import ( "time" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/state" abci "github.com/tendermint/tendermint/abci/types" @@ -166,7 +167,7 @@ func NewTendermint(app abci.Application, opts *Options) *nm.Node { panic(err) } node, err := nm.NewNode(config, pv, nodeKey, papp, - nm.DefaultGenesisDocProviderFunc(config), + state.DefaultGenesisDocProviderFunc(config), nm.DefaultDBProvider, nm.DefaultMetricsProvider(config.Instrumentation), logger) diff --git a/state/store.go b/state/store.go index 0301bc7c3..e1fb5ba60 100644 --- a/state/store.go +++ b/state/store.go @@ -1,9 +1,11 @@ package state import ( + "errors" "fmt" abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" @@ -17,6 +19,23 @@ const ( valSetCheckpointInterval = 100000 ) +var ( + genesisDocKey = []byte("genesisDoc") +) + +// GenesisDocProvider returns a GenesisDoc. +// It allows the GenesisDoc to be pulled from sources other than the +// filesystem, for instance from a distributed key-value store cluster. +type GenesisDocProvider func() (*types.GenesisDoc, error) + +// DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads +// the GenesisDoc from the config.GenesisFile() on the filesystem. +func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { + return func() (*types.GenesisDoc, error) { + return types.GenesisDocFromFile(config.GenesisFile()) + } +} + //------------------------------------------------------------------------ func calcValidatorsKey(height int64) []byte { @@ -65,6 +84,52 @@ func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) ( return state, nil } +// LoadStateFromDBOrGenesisDocProvider attempts to load the state from the +// database, or creates one using the given genesisDocProvider and persists the +// result to the database. On success this also returns the genesis doc loaded +// through the given provider. +func LoadStateFromDBOrGenesisDocProvider(stateDB dbm.DB, genesisDocProvider GenesisDocProvider) (State, *types.GenesisDoc, error) { + // Get genesis doc + genDoc, err := loadGenesisDoc(stateDB) + if err != nil { + genDoc, err = genesisDocProvider() + if err != nil { + return State{}, nil, err + } + // save genesis doc to prevent a certain class of user errors (e.g. when it + // was changed, accidentally or not). Also good for audit trail. + saveGenesisDoc(stateDB, genDoc) + } + state, err := LoadStateFromDBOrGenesisDoc(stateDB, genDoc) + if err != nil { + return State{}, nil, err + } + return state, genDoc, nil +} + +// panics if failed to unmarshal bytes +func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { + bytes := db.Get(genesisDocKey) + if len(bytes) == 0 { + return nil, errors.New("Genesis doc not found") + } + var genDoc *types.GenesisDoc + err := cdc.UnmarshalJSON(bytes, &genDoc) + if err != nil { + panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes)) + } + return genDoc, nil +} + +// panics if failed to marshal the given genesis document +func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { + bytes, err := cdc.MarshalJSON(genDoc) + if err != nil { + panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) + } + db.SetSync(genesisDocKey, bytes) +} + // LoadState loads the State from the database. func LoadState(db dbm.DB) State { return loadState(db, stateKey) From 40dbad9915561434dad539eb681834e8fd126d65 Mon Sep 17 00:00:00 2001 From: Roman Shtylman Date: Mon, 29 Apr 2019 23:17:57 -0700 Subject: [PATCH 181/259] pex: dial seeds when address book needs more addresses (#3603) If we are low on addresses for peering, we need to discover more peers. The previous behavior would query existing peers; however, if an existing peer does not participate in peer exchange, then our node will not discover more peers. This change consults both existing peers as well as seeds when there is a deficit in address book addresses. This allows for discovering peers though existing channels as well as via seeds if existing peers do not share addresses. --- CHANGELOG_PENDING.md | 1 + p2p/pex/pex_reactor.go | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index ff2ff69e3..c4441776a 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -27,3 +27,4 @@ - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) - [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests +- [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) \ No newline at end of file diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 38befe29a..b63c5f815 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -493,9 +493,10 @@ func (r *PEXReactor) ensurePeers() { } } - // If we are not connected to nor dialing anybody, fallback to dialing a seed. - if out+in+dial+len(toDial) == 0 { - r.Logger.Info("No addresses to dial nor connected peers. Falling back to seeds") + // If we are not dialing anyone and need more addresses - dial a seed + // This is done in addition to asking a peer for addresses to work-around peers not participating in PEX + if r.book.NeedMoreAddrs() && len(toDial) == 0 { + r.Logger.Info("No addresses to dial. Falling back to seeds") r.dialSeeds() } } From 43348022d6904a0d19d97fc1e711facecdc2b9d5 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 1 May 2019 09:38:26 +0400 Subject: [PATCH 182/259] pex: various follow-ups (#3605) * p2p: merge switch cases also improve the error msg in privval * pex: refactor code plus update specification follow-up to https://github.com/tendermint/tendermint/pull/3603 * Update docs/spec/reactors/pex/pex.md Co-Authored-By: melekes --- docs/spec/reactors/pex/pex.md | 19 +++++++++++-------- p2p/pex/pex_reactor.go | 23 ++++++++++------------- privval/socket_listeners_test.go | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/docs/spec/reactors/pex/pex.md b/docs/spec/reactors/pex/pex.md index 26f1fa8bb..268b4a318 100644 --- a/docs/spec/reactors/pex/pex.md +++ b/docs/spec/reactors/pex/pex.md @@ -21,17 +21,20 @@ inbound (they dialed our public address) or outbound (we dialed them). ## Discovery Peer discovery begins with a list of seeds. -When we have no peers, or have been unable to find enough peers from existing ones, -we dial a randomly selected seed to get a list of peers to dial. + +When we don't have enough peers, we + +1. ask existing peers +2. dial seeds if we're not dialing anyone currently On startup, we will also immediately dial the given list of `persistent_peers`, -and will attempt to maintain persistent connections with them. If the connections die, or we fail to dial, -we will redial every 5s for a few minutes, then switch to an exponential backoff schedule, -and after about a day of trying, stop dialing the peer. +and will attempt to maintain persistent connections with them. If the +connections die, or we fail to dial, we will redial every 5s for a few minutes, +then switch to an exponential backoff schedule, and after about a day of +trying, stop dialing the peer. -So long as we have less than `MaxNumOutboundPeers`, we periodically request additional peers -from each of our own. If sufficient time goes by and we still can't find enough peers, -we try the seeds again. +As long as we have less than `MaxNumOutboundPeers`, we periodically request +additional peers from each of our own and try seeds. ## Listening diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index b63c5f815..f24f44dd5 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -471,9 +471,7 @@ func (r *PEXReactor) ensurePeers() { err := r.dialPeer(addr) if err != nil { switch err.(type) { - case errMaxAttemptsToDial: - r.Logger.Debug(err.Error(), "addr", addr) - case errTooEarlyToDial: + case errMaxAttemptsToDial, errTooEarlyToDial: r.Logger.Debug(err.Error(), "addr", addr) default: r.Logger.Error(err.Error(), "addr", addr) @@ -482,8 +480,8 @@ func (r *PEXReactor) ensurePeers() { }(addr) } - // If we need more addresses, pick a random peer and ask for more. if r.book.NeedMoreAddrs() { + // 1) Pick a random peer and ask for more. peers := r.Switch.Peers().List() peersCount := len(peers) if peersCount > 0 { @@ -491,13 +489,14 @@ func (r *PEXReactor) ensurePeers() { r.Logger.Info("We need more addresses. Sending pexRequest to random peer", "peer", peer) r.RequestAddrs(peer) } - } - // If we are not dialing anyone and need more addresses - dial a seed - // This is done in addition to asking a peer for addresses to work-around peers not participating in PEX - if r.book.NeedMoreAddrs() && len(toDial) == 0 { - r.Logger.Info("No addresses to dial. Falling back to seeds") - r.dialSeeds() + // 2) Dial seeds if we are not dialing anyone. + // This is done in addition to asking a peer for addresses to work-around + // peers not participating in PEX. + if len(toDial) == 0 { + r.Logger.Info("No addresses to dial. Falling back to seeds") + r.dialSeeds() + } } } @@ -664,9 +663,7 @@ func (r *PEXReactor) crawlPeers(addrs []*p2p.NetAddress) { err := r.dialPeer(addr) if err != nil { switch err.(type) { - case errMaxAttemptsToDial: - r.Logger.Debug(err.Error(), "addr", addr) - case errTooEarlyToDial: + case errMaxAttemptsToDial, errTooEarlyToDial: r.Logger.Debug(err.Error(), "addr", addr) default: r.Logger.Error(err.Error(), "addr", addr) diff --git a/privval/socket_listeners_test.go b/privval/socket_listeners_test.go index 1cda2b6ec..3c4cb8588 100644 --- a/privval/socket_listeners_test.go +++ b/privval/socket_listeners_test.go @@ -130,8 +130,8 @@ func TestListenerTimeoutReadWrite(t *testing.T) { t.Errorf("for %s listener, have %v, want %v", tc.description, have, want) } - if have, want := opErr.Timeout(), true; have != want { - t.Errorf("for %s listener, got unexpected error: have %v, want %v", tc.description, have, want) + if !opErr.Timeout() { + t.Errorf("for %s listener, got unexpected error: have %v, want Timeout error", tc.description, opErr) } } } From a2a68df5219c45c57bd41216df18a9a1b56c0d84 Mon Sep 17 00:00:00 2001 From: Ivan Kushmantsev Date: Wed, 1 May 2019 10:01:41 +0400 Subject: [PATCH 183/259] cli: add option to not clear address book with unsafe reset (#3606) Fixes #3585 --- CHANGELOG_PENDING.md | 1 + cmd/tendermint/commands/reset_priv_validator.go | 12 +++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c4441776a..0f1ee28f0 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,6 +20,7 @@ ### IMPROVEMENTS: - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC +- [cli] \#3606 (https://github.com/tendermint/tendermint/issues/3585) Add option to not clear address book with unsafe reset (@climber73) - [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `-config=` option to `testnet` cmd (@gregdhill) - [cs/replay] \#3460 check appHash for each block diff --git a/cmd/tendermint/commands/reset_priv_validator.go b/cmd/tendermint/commands/reset_priv_validator.go index 055a76c51..4e8bde8b4 100644 --- a/cmd/tendermint/commands/reset_priv_validator.go +++ b/cmd/tendermint/commands/reset_priv_validator.go @@ -18,6 +18,12 @@ var ResetAllCmd = &cobra.Command{ Run: resetAll, } +var keepAddrBook bool + +func init() { + ResetAllCmd.Flags().BoolVar(&keepAddrBook, "keep-addr-book", false, "Keep the address book intact") +} + // ResetPrivValidatorCmd resets the private validator files. var ResetPrivValidatorCmd = &cobra.Command{ Use: "unsafe_reset_priv_validator", @@ -41,7 +47,11 @@ func resetPrivValidator(cmd *cobra.Command, args []string) { // ResetAll removes address book files plus all data, and resets the privValdiator data. // Exported so other CLI tools can use it. func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) { - removeAddrBook(addrBookFile, logger) + if keepAddrBook { + logger.Info("The address book remains intact") + } else { + removeAddrBook(addrBookFile, logger) + } if err := os.RemoveAll(dbDir); err == nil { logger.Info("Removed all blockchain history", "dir", dbDir) } else { From 2c26d95ab914730ce70445f8aefeb18ff90f8159 Mon Sep 17 00:00:00 2001 From: JamesRay <66258875@qq.com> Date: Thu, 2 May 2019 05:15:53 +0800 Subject: [PATCH 184/259] cs/replay: execCommitBlock should not read from state.lastValidators (#3067) * execCommitBlock should not read from state.lastValidators * fix height 1 * fix blockchain/reactor_test * fix consensus/mempool_test * fix consensus/reactor_test * fix consensus/replay_test * add CHANGELOG * fix consensus/reactor_test * fix consensus/replay_test * add a test for replay validators change * fix mem_pool test * fix byzantine test * remove a redundant code * reduce validator change blocks to 6 * fix * return peer0 config * seperate testName * seperate testName 1 * seperate testName 2 * seperate app db path * seperate app db path 1 * add a lock before startNet * move the lock to reactor_test * simulate just once * try to find problem * handshake only saveState when app version changed * update gometalinter to 3.0.0 (#3233) in the attempt to fix https://circleci.com/gh/tendermint/tendermint/43165 also code is simplified by running gofmt -s . remove unused vars enable linters we're currently passing remove deprecated linters (cherry picked from commit d47094550315c094512a242445e0dde24b5a03f5) * gofmt code * goimport code * change the bool name to testValidatorsChange * adjust receive kvstore.ProtocolVersion * adjust receive kvstore.ProtocolVersion 1 * adjust receive kvstore.ProtocolVersion 3 * fix merge execution.go * fix merge develop * fix merge develop 1 * fix run cleanupFunc * adjust code according to reviewers' opinion * modify the func name match the convention * simplify simulate a chain containing some validator change txs 1 * test CI error * Merge remote-tracking branch 'upstream/develop' into fixReplay 1 * fix pubsub_test * subscribeUnbuffered vote channel --- CHANGELOG_PENDING.md | 3 +- blockchain/reactor_test.go | 4 +- consensus/byzantine_test.go | 4 +- consensus/common_test.go | 47 ++++- consensus/mempool_test.go | 9 +- consensus/reactor_test.go | 7 +- consensus/replay.go | 12 +- consensus/replay_test.go | 380 +++++++++++++++++++++++++++++++----- consensus/state_test.go | 17 +- consensus/wal_generator.go | 3 +- libs/pubsub/pubsub_test.go | 7 +- state/execution.go | 45 +++-- state/execution_test.go | 4 +- 13 files changed, 444 insertions(+), 98 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 0f1ee28f0..f777c6e06 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -28,4 +28,5 @@ - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) - [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests -- [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) \ No newline at end of file +- [consensus] \#3067 getBeginBlockValidatorInfo loads validators from stateDB instead of state +- [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 1b3aee56f..9cd3489c8 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -91,8 +91,10 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals // NOTE we have to create and commit the blocks first because // pool.height is determined from the store. fastSync := true - blockExec := sm.NewBlockExecutor(dbm.NewMemDB(), log.TestingLogger(), proxyApp.Consensus(), + db := dbm.NewMemDB() + blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(), sm.MockMempool{}, sm.MockEvidencePool{}) + sm.SaveState(db, state) // let's add some blocks in for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ { diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index 0c1b88565..d181fb1a6 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/p2p" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -69,7 +70,7 @@ func TestByzantine(t *testing.T) { blocksSubs[i], err = eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock) require.NoError(t, err) - conR := NewConsensusReactor(css[i], true) // so we dont start the consensus states + conR := NewConsensusReactor(css[i], true) // so we don't start the consensus states conR.SetLogger(logger.With("validator", i)) conR.SetEventBus(eventBus) @@ -81,6 +82,7 @@ func TestByzantine(t *testing.T) { } reactors[i] = conRI + sm.SaveState(css[i].blockExec.DB(), css[i].state) //for save height 1's validators info } defer func() { diff --git a/consensus/common_test.go b/consensus/common_test.go index 8f305139e..a0c179ae1 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -14,6 +14,8 @@ import ( "github.com/go-kit/kit/log/term" + "path" + abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/example/counter" "github.com/tendermint/tendermint/abci/example/kvstore" @@ -119,6 +121,24 @@ func incrementRound(vss ...*validatorStub) { } } +type ValidatorStubsByAddress []*validatorStub + +func (vss ValidatorStubsByAddress) Len() int { + return len(vss) +} + +func (vss ValidatorStubsByAddress) Less(i, j int) bool { + return bytes.Compare(vss[i].GetPubKey().Address(), vss[j].GetPubKey().Address()) == -1 +} + +func (vss ValidatorStubsByAddress) Swap(i, j int) { + it := vss[i] + vss[i] = vss[j] + vss[i].Index = i + vss[j] = it + vss[j].Index = j +} + //------------------------------------------------------------------------------- // Functions for transitioning the consensus state @@ -228,7 +248,7 @@ func validatePrevoteAndPrecommit(t *testing.T, cs *ConsensusState, thisRound, lo } func subscribeToVoter(cs *ConsensusState, addr []byte) <-chan tmpubsub.Message { - votesSub, err := cs.eventBus.Subscribe(context.Background(), testSubscriber, types.EventQueryVote) + votesSub, err := cs.eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, types.EventQueryVote) if err != nil { panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, types.EventQueryVote)) } @@ -278,7 +298,8 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S evpool := sm.MockEvidencePool{} // Make ConsensusState - stateDB := dbm.NewMemDB() + stateDB := blockDB + sm.SaveState(stateDB, state) //for save height 1's validators info blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool) cs := NewConsensusState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool) cs.SetLogger(log.TestingLogger().With("module", "consensus")) @@ -564,7 +585,7 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou vals := types.TM2PB.ValidatorUpdates(state.Validators) app.InitChain(abci.RequestInitChain{Validators: vals}) - css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], app) + css[i] = newConsensusStateWithConfigAndBlockStore(thisConfig, state, privVals[i], app, stateDB) css[i].SetTimeoutTicker(tickerFunc()) css[i].SetLogger(logger.With("validator", i, "module", "consensus")) } @@ -576,12 +597,11 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou } // nPeers = nValidators + nNotValidator -func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerFunc func() TimeoutTicker, - appFunc func() abci.Application) ([]*ConsensusState, cleanupFunc) { - +func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerFunc func() TimeoutTicker, appFunc func(string) abci.Application) ([]*ConsensusState, *types.GenesisDoc, *cfg.Config, cleanupFunc) { genDoc, privVals := randGenesisDoc(nValidators, false, testMinPower) css := make([]*ConsensusState, nPeers) logger := consensusLogger() + var peer0Config *cfg.Config configRootDirs := make([]string, 0, nPeers) for i := 0; i < nPeers; i++ { stateDB := dbm.NewMemDB() // each state needs its own db @@ -589,6 +609,9 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i)) configRootDirs = append(configRootDirs, thisConfig.RootDir) ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal + if i == 0 { + peer0Config = thisConfig + } var privVal types.PrivValidator if i < nValidators { privVal = privVals[i] @@ -605,15 +628,19 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) } - app := appFunc() + app := appFunc(path.Join(config.DBDir(), fmt.Sprintf("%s_%d", testName, i))) vals := types.TM2PB.ValidatorUpdates(state.Validators) + if _, ok := app.(*kvstore.PersistentKVStoreApplication); ok { + state.Version.Consensus.App = kvstore.ProtocolVersion //simulate handshake, receive app version. If don't do this, replay test will fail + } app.InitChain(abci.RequestInitChain{Validators: vals}) + //sm.SaveState(stateDB,state) //height 1's validatorsInfo already saved in LoadStateFromDBOrGenesisDoc above css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, app) css[i].SetTimeoutTicker(tickerFunc()) css[i].SetLogger(logger.With("validator", i, "module", "consensus")) } - return css, func() { + return css, genDoc, peer0Config, func() { for _, dir := range configRootDirs { os.RemoveAll(dir) } @@ -719,3 +746,7 @@ func newPersistentKVStore() abci.Application { } return kvstore.NewPersistentKVStoreApplication(dir) } + +func newPersistentKVStoreWithPath(dbDir string) abci.Application { + return kvstore.NewPersistentKVStoreApplication(dbDir) +} diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index e7669b177..1baa92b89 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -11,6 +11,7 @@ import ( "github.com/tendermint/tendermint/abci/example/code" abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -106,7 +107,9 @@ func deliverTxsRange(cs *ConsensusState, start, end int) { func TestMempoolTxConcurrentWithCommit(t *testing.T) { state, privVals := randGenesisState(1, false, 10) - cs := newConsensusState(state, privVals[0], NewCounterApplication()) + blockDB := dbm.NewMemDB() + cs := newConsensusStateWithConfigAndBlockStore(config, state, privVals[0], NewCounterApplication(), blockDB) + sm.SaveState(blockDB, state) height, round := cs.Height, cs.Round newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock) @@ -129,7 +132,9 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) { func TestMempoolRmBadTx(t *testing.T) { state, privVals := randGenesisState(1, false, 10) app := NewCounterApplication() - cs := newConsensusState(state, privVals[0], app) + blockDB := dbm.NewMemDB() + cs := newConsensusStateWithConfigAndBlockStore(config, state, privVals[0], app, blockDB) + sm.SaveState(blockDB, state) // increment the counter by 1 txBytes := make([]byte, 8) diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index dfd6f2508..bf58a8eeb 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -51,6 +51,10 @@ func startConsensusNet(t *testing.T, css []*ConsensusState, N int) ( blocksSub, err := eventBuses[i].Subscribe(context.Background(), testSubscriber, types.EventQueryNewBlock) require.NoError(t, err) blocksSubs = append(blocksSubs, blocksSub) + + if css[i].state.LastBlockHeight == 0 { //simulate handle initChain in handshake + sm.SaveState(css[i].blockExec.DB(), css[i].state) + } } // make connected switches and start all reactors p2p.MakeConnectedSwitches(config.P2P, N, func(i int, s *p2p.Switch) *p2p.Switch { @@ -329,7 +333,8 @@ func TestReactorVotingPowerChange(t *testing.T) { func TestReactorValidatorSetChanges(t *testing.T) { nPeers := 7 nVals := 4 - css, cleanup := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStore) + css, _, _, cleanup := randConsensusNetWithPeers(nVals, nPeers, "consensus_val_set_changes_test", newMockTickerFunc(true), newPersistentKVStoreWithPath) + defer cleanup() logger := log.TestingLogger() diff --git a/consensus/replay.go b/consensus/replay.go index 38ed79fcf..cc1344eba 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -258,8 +258,10 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error { ) // Set AppVersion on the state. - h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion) - sm.SaveState(h.stateDB, h.initialState) + if h.initialState.Version.Consensus.App != version.Protocol(res.AppVersion) { + h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion) + sm.SaveState(h.stateDB, h.initialState) + } // Replay blocks up to the latest in the blockstore. _, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp) @@ -421,13 +423,12 @@ 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) - // Extra check to ensure the app was not changed in a way it shouldn't have. if len(appHash) > 0 { assertAppHashEqualsOneFromBlock(appHash, block) } - appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, state.LastValidators, h.stateDB) + appHash, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, h.logger, h.stateDB) if err != nil { return nil, err } @@ -517,6 +518,9 @@ type mockProxyApp struct { func (mock *mockProxyApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { r := mock.abciResponses.DeliverTx[mock.txCount] mock.txCount++ + if r == nil { //it could be nil because of amino unMarshall, it will cause an empty ResponseDeliverTx to become nil + return abci.ResponseDeliverTx{} + } return *r } diff --git a/consensus/replay_test.go b/consensus/replay_test.go index b084cef4e..6ed361905 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -15,6 +15,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "sort" + "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" @@ -140,10 +142,10 @@ LOOP: // create consensus state from a clean slate logger := log.NewNopLogger() - stateDB := dbm.NewMemDB() + blockDB := dbm.NewMemDB() + stateDB := blockDB state, _ := sm.MakeGenesisStateFromFile(consensusReplayConfig.GenesisFile()) privValidator := loadPrivValidator(consensusReplayConfig) - blockDB := dbm.NewMemDB() cs := newConsensusStateWithConfigAndBlockStore(consensusReplayConfig, state, privValidator, kvstore.NewKVStoreApplication(), blockDB) cs.SetLogger(logger) @@ -262,7 +264,13 @@ func (w *crashingWAL) Stop() error { return w.next.Stop() } func (w *crashingWAL) Wait() { w.next.Wait() } //------------------------------------------------------------------------------------------ -// Handshake Tests +type testSim struct { + GenesisState sm.State + Config *cfg.Config + Chain []*types.Block + Commits []*types.Commit + CleanupFunc cleanupFunc +} const ( numBlocks = 6 @@ -271,6 +279,8 @@ const ( var ( mempool = sm.MockMempool{} evpool = sm.MockEvidencePool{} + + sim testSim ) //--------------------------------------- @@ -281,44 +291,283 @@ var ( // 2 - save block and committed but state is behind var modes = []uint{0, 1, 2} +// This is actually not a test, it's for storing validator change tx data for testHandshakeReplay +func TestSimulateValidatorsChange(t *testing.T) { + nPeers := 7 + nVals := 4 + css, genDoc, config, cleanup := randConsensusNetWithPeers(nVals, nPeers, "replay_test", newMockTickerFunc(true), newPersistentKVStoreWithPath) + sim.Config = config + sim.GenesisState, _ = sm.MakeGenesisState(genDoc) + sim.CleanupFunc = cleanup + + partSize := types.BlockPartSizeBytes + + newRoundCh := subscribe(css[0].eventBus, types.EventQueryNewRound) + proposalCh := subscribe(css[0].eventBus, types.EventQueryCompleteProposal) + + vss := make([]*validatorStub, nPeers) + for i := 0; i < nPeers; i++ { + vss[i] = NewValidatorStub(css[i].privValidator, i) + } + height, round := css[0].Height, css[0].Round + // start the machine + startTestRound(css[0], height, round) + incrementHeight(vss...) + ensureNewRound(newRoundCh, height, 0) + ensureNewProposal(proposalCh, height, round) + rs := css[0].GetRoundState() + signAddVotes(css[0], types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...) + ensureNewRound(newRoundCh, height+1, 0) + + //height 2 + height++ + incrementHeight(vss...) + newValidatorPubKey1 := css[nVals].privValidator.GetPubKey() + valPubKey1ABCI := types.TM2PB.PubKey(newValidatorPubKey1) + newValidatorTx1 := kvstore.MakeValSetChangeTx(valPubKey1ABCI, testMinPower) + err := assertMempool(css[0].txNotifier).CheckTx(newValidatorTx1, nil) + assert.Nil(t, err) + propBlock, _ := css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlockParts := propBlock.MakePartSet(partSize) + blockID := types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} + proposal := types.NewProposal(vss[1].Height, round, -1, blockID) + if err := vss[1].SignProposal(config.ChainID(), proposal); err != nil { + t.Fatal("failed to sign bad proposal", err) + } + + // set the proposal block + if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { + t.Fatal(err) + } + ensureNewProposal(proposalCh, height, round) + rs = css[0].GetRoundState() + signAddVotes(css[0], types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...) + ensureNewRound(newRoundCh, height+1, 0) + + //height 3 + height++ + incrementHeight(vss...) + updateValidatorPubKey1 := css[nVals].privValidator.GetPubKey() + updatePubKey1ABCI := types.TM2PB.PubKey(updateValidatorPubKey1) + updateValidatorTx1 := kvstore.MakeValSetChangeTx(updatePubKey1ABCI, 25) + err = assertMempool(css[0].txNotifier).CheckTx(updateValidatorTx1, nil) + assert.Nil(t, err) + propBlock, _ = css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlockParts = propBlock.MakePartSet(partSize) + blockID = types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} + proposal = types.NewProposal(vss[2].Height, round, -1, blockID) + if err := vss[2].SignProposal(config.ChainID(), proposal); err != nil { + t.Fatal("failed to sign bad proposal", err) + } + + // set the proposal block + if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { + t.Fatal(err) + } + ensureNewProposal(proposalCh, height, round) + rs = css[0].GetRoundState() + signAddVotes(css[0], types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), vss[1:nVals]...) + ensureNewRound(newRoundCh, height+1, 0) + + //height 4 + height++ + incrementHeight(vss...) + newValidatorPubKey2 := css[nVals+1].privValidator.GetPubKey() + newVal2ABCI := types.TM2PB.PubKey(newValidatorPubKey2) + newValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, testMinPower) + err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx2, nil) + assert.Nil(t, err) + newValidatorPubKey3 := css[nVals+2].privValidator.GetPubKey() + newVal3ABCI := types.TM2PB.PubKey(newValidatorPubKey3) + newValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, testMinPower) + err = assertMempool(css[0].txNotifier).CheckTx(newValidatorTx3, nil) + assert.Nil(t, err) + propBlock, _ = css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlockParts = propBlock.MakePartSet(partSize) + blockID = types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} + newVss := make([]*validatorStub, nVals+1) + copy(newVss, vss[:nVals+1]) + sort.Sort(ValidatorStubsByAddress(newVss)) + selfIndex := 0 + for i, vs := range newVss { + if vs.GetPubKey().Equals(css[0].privValidator.GetPubKey()) { + selfIndex = i + break + } + } + + proposal = types.NewProposal(vss[3].Height, round, -1, blockID) + if err := vss[3].SignProposal(config.ChainID(), proposal); err != nil { + t.Fatal("failed to sign bad proposal", err) + } + + // set the proposal block + if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { + t.Fatal(err) + } + ensureNewProposal(proposalCh, height, round) + + removeValidatorTx2 := kvstore.MakeValSetChangeTx(newVal2ABCI, 0) + err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx2, nil) + assert.Nil(t, err) + + rs = css[0].GetRoundState() + for i := 0; i < nVals+1; i++ { + if i == selfIndex { + continue + } + signAddVotes(css[0], types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i]) + } + + ensureNewRound(newRoundCh, height+1, 0) + + //height 5 + height++ + incrementHeight(vss...) + ensureNewProposal(proposalCh, height, round) + rs = css[0].GetRoundState() + for i := 0; i < nVals+1; i++ { + if i == selfIndex { + continue + } + signAddVotes(css[0], types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i]) + } + ensureNewRound(newRoundCh, height+1, 0) + + //height 6 + height++ + incrementHeight(vss...) + removeValidatorTx3 := kvstore.MakeValSetChangeTx(newVal3ABCI, 0) + err = assertMempool(css[0].txNotifier).CheckTx(removeValidatorTx3, nil) + assert.Nil(t, err) + propBlock, _ = css[0].createProposalBlock() //changeProposer(t, cs1, vs2) + propBlockParts = propBlock.MakePartSet(partSize) + blockID = types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} + newVss = make([]*validatorStub, nVals+3) + copy(newVss, vss[:nVals+3]) + sort.Sort(ValidatorStubsByAddress(newVss)) + for i, vs := range newVss { + if vs.GetPubKey().Equals(css[0].privValidator.GetPubKey()) { + selfIndex = i + break + } + } + proposal = types.NewProposal(vss[1].Height, round, -1, blockID) + if err := vss[1].SignProposal(config.ChainID(), proposal); err != nil { + t.Fatal("failed to sign bad proposal", err) + } + + // set the proposal block + if err := css[0].SetProposalAndBlock(proposal, propBlock, propBlockParts, "some peer"); err != nil { + t.Fatal(err) + } + ensureNewProposal(proposalCh, height, round) + rs = css[0].GetRoundState() + for i := 0; i < nVals+3; i++ { + if i == selfIndex { + continue + } + signAddVotes(css[0], types.PrecommitType, rs.ProposalBlock.Hash(), rs.ProposalBlockParts.Header(), newVss[i]) + } + ensureNewRound(newRoundCh, height+1, 0) + + sim.Chain = make([]*types.Block, 0) + sim.Commits = make([]*types.Commit, 0) + for i := 1; i <= numBlocks; i++ { + sim.Chain = append(sim.Chain, css[0].blockStore.LoadBlock(int64(i))) + sim.Commits = append(sim.Commits, css[0].blockStore.LoadBlockCommit(int64(i))) + } +} + // Sync from scratch func TestHandshakeReplayAll(t *testing.T) { - for i, m := range modes { - config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) - defer os.RemoveAll(config.RootDir) - - testHandshakeReplay(t, config, 0, m) + for _, m := range modes { + testHandshakeReplay(t, config, 0, m, false) + } + for _, m := range modes { + testHandshakeReplay(t, config, 0, m, true) } } // Sync many, not from scratch func TestHandshakeReplaySome(t *testing.T) { - for i, m := range modes { - config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) - defer os.RemoveAll(config.RootDir) - - testHandshakeReplay(t, config, 1, m) + for _, m := range modes { + testHandshakeReplay(t, config, 1, m, false) + } + for _, m := range modes { + testHandshakeReplay(t, config, 1, m, true) } } // Sync from lagging by one func TestHandshakeReplayOne(t *testing.T) { - for i, m := range modes { - config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) - defer os.RemoveAll(config.RootDir) - - testHandshakeReplay(t, config, numBlocks-1, m) + for _, m := range modes { + testHandshakeReplay(t, config, numBlocks-1, m, false) + } + for _, m := range modes { + testHandshakeReplay(t, config, numBlocks-1, m, true) } } // Sync from caught up func TestHandshakeReplayNone(t *testing.T) { - for i, m := range modes { - config := ResetConfig(fmt.Sprintf("%s_%v", t.Name(), i)) - defer os.RemoveAll(config.RootDir) - - testHandshakeReplay(t, config, numBlocks, m) + for _, m := range modes { + testHandshakeReplay(t, config, numBlocks, m, false) } + for _, m := range modes { + testHandshakeReplay(t, config, numBlocks, m, true) + } +} + +// Test mockProxyApp should not panic when app return ABCIResponses with some empty ResponseDeliverTx +func TestMockProxyApp(t *testing.T) { + sim.CleanupFunc() //clean the test env created in TestSimulateValidatorsChange + logger := log.TestingLogger() + var validTxs, invalidTxs = 0, 0 + txIndex := 0 + + assert.NotPanics(t, func() { + abciResWithEmptyDeliverTx := new(sm.ABCIResponses) + abciResWithEmptyDeliverTx.DeliverTx = make([]*abci.ResponseDeliverTx, 0) + abciResWithEmptyDeliverTx.DeliverTx = append(abciResWithEmptyDeliverTx.DeliverTx, &abci.ResponseDeliverTx{}) + + // called when saveABCIResponses: + bytes := cdc.MustMarshalBinaryBare(abciResWithEmptyDeliverTx) + loadedAbciRes := new(sm.ABCIResponses) + + // this also happens sm.LoadABCIResponses + err := cdc.UnmarshalBinaryBare(bytes, loadedAbciRes) + require.NoError(t, err) + + mock := newMockProxyApp([]byte("mock_hash"), loadedAbciRes) + + abciRes := new(sm.ABCIResponses) + abciRes.DeliverTx = make([]*abci.ResponseDeliverTx, len(loadedAbciRes.DeliverTx)) + // Execute transactions and get hash. + proxyCb := func(req *abci.Request, res *abci.Response) { + switch r := res.Value.(type) { + case *abci.Response_DeliverTx: + // TODO: make use of res.Log + // TODO: make use of this info + // Blocks may include invalid txs. + txRes := r.DeliverTx + if txRes.Code == abci.CodeTypeOK { + validTxs++ + } else { + logger.Debug("Invalid tx", "code", txRes.Code, "log", txRes.Log) + invalidTxs++ + } + abciRes.DeliverTx[txIndex] = txRes + txIndex++ + } + } + mock.SetResponseCallback(proxyCb) + + someTx := []byte("tx") + mock.DeliverTxAsync(someTx) + }) + assert.True(t, validTxs == 1) + assert.True(t, invalidTxs == 0) } func tempWALWithData(data []byte) string { @@ -336,54 +585,68 @@ func tempWALWithData(data []byte) string { return walFile.Name() } -// Make some blocks. Start a fresh app and apply nBlocks blocks. Then restart -// the app and sync it up with the remaining blocks. -func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uint) { - walBody, err := WALWithNBlocks(t, numBlocks) - require.NoError(t, err) - walFile := tempWALWithData(walBody) - config.Consensus.SetWalFile(walFile) +// Make some blocks. Start a fresh app and apply nBlocks blocks. Then restart the app and sync it up with the remaining blocks +func testHandshakeReplay(t *testing.T, config *cfg.Config, nBlocks int, mode uint, testValidatorsChange bool) { + var chain []*types.Block + var commits []*types.Commit + var store *mockBlockStore + var stateDB dbm.DB + var genisisState sm.State + if testValidatorsChange { + testConfig := ResetConfig(fmt.Sprintf("%s_%v_m", t.Name(), mode)) + defer os.RemoveAll(testConfig.RootDir) + stateDB = dbm.NewMemDB() + genisisState = sim.GenesisState + config = sim.Config + chain = sim.Chain + commits = sim.Commits + store = newMockBlockStore(config, genisisState.ConsensusParams) + } else { //test single node + testConfig := ResetConfig(fmt.Sprintf("%s_%v_s", t.Name(), mode)) + defer os.RemoveAll(testConfig.RootDir) + walBody, err := WALWithNBlocks(t, numBlocks) + require.NoError(t, err) + walFile := tempWALWithData(walBody) + config.Consensus.SetWalFile(walFile) - privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) + privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()) - wal, err := NewWAL(walFile) - require.NoError(t, err) - wal.SetLogger(log.TestingLogger()) - err = wal.Start() - require.NoError(t, err) - defer wal.Stop() + wal, err := NewWAL(walFile) + require.NoError(t, err) + wal.SetLogger(log.TestingLogger()) + err = wal.Start() + require.NoError(t, err) + defer wal.Stop() - chain, commits, err := makeBlockchainFromWAL(wal) - require.NoError(t, err) - - stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion) + chain, commits, err = makeBlockchainFromWAL(wal) + require.NoError(t, err) + stateDB, genisisState, store = stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion) + } store.chain = chain store.commits = commits + state := genisisState.Copy() // run the chain through state.ApplyBlock to build up the tendermint state - clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "1"))) - proxyApp := proxy.NewAppConns(clientCreator) - err = proxyApp.Start() - require.NoError(t, err) - state = buildTMStateFromChain(config, stateDB, state, chain, proxyApp, mode) - proxyApp.Stop() + state = buildTMStateFromChain(config, stateDB, state, chain, nBlocks, mode) latestAppHash := state.AppHash // make a new client creator - kvstoreApp := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "2")) + kvstoreApp := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_a", nBlocks, mode))) + clientCreator2 := proxy.NewLocalClientCreator(kvstoreApp) if nBlocks > 0 { // run nBlocks against a new client to build up the app state. // use a throwaway tendermint state proxyApp := proxy.NewAppConns(clientCreator2) - stateDB, state, _ := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion) - buildAppStateFromChain(proxyApp, stateDB, state, chain, nBlocks, mode) + stateDB1 := dbm.NewMemDB() + sm.SaveState(stateDB1, genisisState) + buildAppStateFromChain(proxyApp, stateDB1, genisisState, chain, nBlocks, mode) } // now start the app using the handshake - it should sync genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile()) handshaker := NewHandshaker(stateDB, state, store, genDoc) - proxyApp = proxy.NewAppConns(clientCreator2) + proxyApp := proxy.NewAppConns(clientCreator2) if err := proxyApp.Start(); err != nil { t.Fatalf("Error starting proxy app connections: %v", err) } @@ -435,12 +698,14 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB, } defer proxyApp.Stop() + state.Version.Consensus.App = kvstore.ProtocolVersion //simulate handshake, receive app version validators := types.TM2PB.ValidatorUpdates(state.Validators) if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{ Validators: validators, }); err != nil { panic(err) } + sm.SaveState(stateDB, state) //save height 1's validatorsInfo switch mode { case 0: @@ -463,15 +728,23 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB, } -func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, - chain []*types.Block, proxyApp proxy.AppConns, mode uint) sm.State { +func buildTMStateFromChain(config *cfg.Config, stateDB dbm.DB, state sm.State, chain []*types.Block, nBlocks int, mode uint) sm.State { + // run the whole chain against this client to build up the tendermint state + clientCreator := proxy.NewLocalClientCreator(kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), fmt.Sprintf("replay_test_%d_%d_t", nBlocks, mode)))) + proxyApp := proxy.NewAppConns(clientCreator) + if err := proxyApp.Start(); err != nil { + panic(err) + } + defer proxyApp.Stop() + state.Version.Consensus.App = kvstore.ProtocolVersion //simulate handshake, receive app version validators := types.TM2PB.ValidatorUpdates(state.Validators) if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{ Validators: validators, }); err != nil { panic(err) } + sm.SaveState(stateDB, state) //save height 1's validatorsInfo switch mode { case 0: @@ -743,6 +1016,7 @@ func stateAndStore(config *cfg.Config, pubKey crypto.PubKey, appVersion version. state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile()) state.Version.Consensus.App = appVersion store := newMockBlockStore(config, state.ConsensusParams) + sm.SaveState(stateDB, state) return stateDB, state, store } diff --git a/consensus/state_test.go b/consensus/state_test.go index 67b5cfbdc..87e351dc8 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -239,7 +239,7 @@ func TestStateFullRound1(t *testing.T) { cs.SetEventBus(eventBus) eventBus.Start() - voteCh := subscribe(cs.eventBus, types.EventQueryVote) + voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote) propCh := subscribe(cs.eventBus, types.EventQueryCompleteProposal) newRoundCh := subscribe(cs.eventBus, types.EventQueryNewRound) @@ -267,7 +267,7 @@ func TestStateFullRoundNil(t *testing.T) { cs, vss := randConsensusState(1) height, round := cs.Height, cs.Round - voteCh := subscribe(cs.eventBus, types.EventQueryVote) + voteCh := subscribeUnBuffered(cs.eventBus, types.EventQueryVote) cs.enterPrevote(height, round) cs.startRoutines(4) @@ -286,7 +286,7 @@ func TestStateFullRound2(t *testing.T) { vs2 := vss[1] height, round := cs1.Height, cs1.Round - voteCh := subscribe(cs1.eventBus, types.EventQueryVote) + voteCh := subscribeUnBuffered(cs1.eventBus, types.EventQueryVote) newBlockCh := subscribe(cs1.eventBus, types.EventQueryNewBlock) // start round and wait for propose and prevote @@ -330,7 +330,7 @@ func TestStateLockNoPOL(t *testing.T) { timeoutProposeCh := subscribe(cs1.eventBus, types.EventQueryTimeoutPropose) timeoutWaitCh := subscribe(cs1.eventBus, types.EventQueryTimeoutWait) - voteCh := subscribe(cs1.eventBus, types.EventQueryVote) + voteCh := subscribeUnBuffered(cs1.eventBus, types.EventQueryVote) proposalCh := subscribe(cs1.eventBus, types.EventQueryCompleteProposal) newRoundCh := subscribe(cs1.eventBus, types.EventQueryNewRound) @@ -1623,3 +1623,12 @@ func subscribe(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Messa } return sub.Out() } + +// subscribe subscribes test client to the given query and returns a channel with cap = 0. +func subscribeUnBuffered(eventBus *types.EventBus, q tmpubsub.Query) <-chan tmpubsub.Message { + sub, err := eventBus.SubscribeUnbuffered(context.Background(), testSubscriber, q) + if err != nil { + panic(fmt.Sprintf("failed to subscribe %s to %v", testSubscriber, q)) + } + return sub.Out() +} diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index 1a4cfb9ff..c007a21cf 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -45,13 +45,14 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { if err != nil { return errors.Wrap(err, "failed to read genesis file") } - stateDB := db.NewMemDB() blockStoreDB := db.NewMemDB() + stateDB := blockStoreDB state, err := sm.MakeGenesisState(genDoc) if err != nil { return errors.Wrap(err, "failed to make genesis state") } state.Version.Consensus.App = kvstore.ProtocolVersion + sm.SaveState(stateDB, state) blockStore := bc.NewBlockStore(blockStoreDB) proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app)) proxyApp.SetLogger(logger.With("module", "proxy")) diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index 884477563..74af431f8 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -46,13 +46,16 @@ func TestSubscribe(t *testing.T) { err = s.Publish(ctx, "Asylum") assert.NoError(t, err) + + err = s.Publish(ctx, "Ivan") + assert.NoError(t, err) }() select { case <-published: assertReceive(t, "Quicksilver", subscription.Out()) assertCancelled(t, subscription, pubsub.ErrOutOfCapacity) - case <-time.After(100 * time.Millisecond): + case <-time.After(3 * time.Second): t.Fatal("Expected Publish(Asylum) not to block") } } @@ -101,7 +104,7 @@ func TestSubscribeUnbuffered(t *testing.T) { select { case <-published: t.Fatal("Expected Publish(Darkhawk) to block") - case <-time.After(100 * time.Millisecond): + case <-time.After(3 * time.Second): assertReceive(t, "Ultron", subscription.Out()) assertReceive(t, "Darkhawk", subscription.Out()) } diff --git a/state/execution.go b/state/execution.go index 3a11ecca4..245325525 100644 --- a/state/execution.go +++ b/state/execution.go @@ -66,6 +66,10 @@ func NewBlockExecutor(db dbm.DB, logger log.Logger, proxyApp proxy.AppConnConsen return res } +func (blockExec *BlockExecutor) DB() dbm.DB { + return blockExec.db +} + // SetEventBus - sets the event bus for publishing block related events. // If not called, it defaults to types.NopEventBus. func (blockExec *BlockExecutor) SetEventBus(eventBus types.BlockEventPublisher) { @@ -116,7 +120,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b } startTime := time.Now().UnixNano() - abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, state.LastValidators, blockExec.db) + abciResponses, err := execBlockOnProxyApp(blockExec.logger, blockExec.proxyApp, block, blockExec.db) endTime := time.Now().UnixNano() blockExec.metrics.BlockProcessingTime.Observe(float64(endTime-startTime) / 1000000) if err != nil { @@ -233,7 +237,6 @@ func execBlockOnProxyApp( logger log.Logger, proxyAppConn proxy.AppConnConsensus, block *types.Block, - lastValSet *types.ValidatorSet, stateDB dbm.DB, ) (*ABCIResponses, error) { var validTxs, invalidTxs = 0, 0 @@ -261,7 +264,7 @@ func execBlockOnProxyApp( } proxyAppConn.SetResponseCallback(proxyCb) - commitInfo, byzVals := getBeginBlockValidatorInfo(block, lastValSet, stateDB) + commitInfo, byzVals := getBeginBlockValidatorInfo(block, stateDB) // Begin block var err error @@ -296,22 +299,31 @@ func execBlockOnProxyApp( return abciResponses, nil } -func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorSet, stateDB dbm.DB) (abci.LastCommitInfo, []abci.Evidence) { - - // Sanity check that commit length matches validator set size - - // only applies after first block +func getBeginBlockValidatorInfo(block *types.Block, stateDB dbm.DB) (abci.LastCommitInfo, []abci.Evidence) { + voteInfos := make([]abci.VoteInfo, block.LastCommit.Size()) + byzVals := make([]abci.Evidence, len(block.Evidence.Evidence)) + var lastValSet *types.ValidatorSet + var err error if block.Height > 1 { - precommitLen := len(block.LastCommit.Precommits) + lastValSet, err = LoadValidators(stateDB, block.Height-1) + if err != nil { + panic(err) // shouldn't happen + } + + // Sanity check that commit length matches validator set size - + // only applies after first block + + precommitLen := block.LastCommit.Size() valSetLen := len(lastValSet.Validators) if precommitLen != valSetLen { // sanity check panic(fmt.Sprintf("precommit length (%d) doesn't match valset length (%d) at height %d\n\n%v\n\n%v", precommitLen, valSetLen, block.Height, block.LastCommit.Precommits, lastValSet.Validators)) } + } else { + lastValSet = types.NewValidatorSet(nil) } - // Collect the vote info (list of validators and whether or not they signed). - voteInfos := make([]abci.VoteInfo, len(lastValSet.Validators)) for i, val := range lastValSet.Validators { var vote *types.CommitSig if i < len(block.LastCommit.Precommits) { @@ -324,12 +336,6 @@ func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorS voteInfos[i] = voteInfo } - commitInfo := abci.LastCommitInfo{ - Round: int32(block.LastCommit.Round()), - Votes: voteInfos, - } - - byzVals := make([]abci.Evidence, len(block.Evidence.Evidence)) for i, ev := range block.Evidence.Evidence { // We need the validator set. We already did this in validateBlock. // TODO: Should we instead cache the valset in the evidence itself and add @@ -341,6 +347,10 @@ func getBeginBlockValidatorInfo(block *types.Block, lastValSet *types.ValidatorS byzVals[i] = types.TM2PB.Evidence(ev, valset, block.Time) } + commitInfo := abci.LastCommitInfo{ + Round: int32(block.LastCommit.Round()), + Votes: voteInfos, + } return commitInfo, byzVals } @@ -469,10 +479,9 @@ func ExecCommitBlock( appConnConsensus proxy.AppConnConsensus, block *types.Block, logger log.Logger, - lastValSet *types.ValidatorSet, stateDB dbm.DB, ) ([]byte, error) { - _, err := execBlockOnProxyApp(logger, appConnConsensus, block, lastValSet, stateDB) + _, err := execBlockOnProxyApp(logger, appConnConsensus, block, stateDB) if err != nil { logger.Error("Error executing block on proxy app", "height", block.Height, "err", err) return nil, err diff --git a/state/execution_test.go b/state/execution_test.go index a9fdfe270..092fbd68f 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -85,7 +85,7 @@ func TestBeginBlockValidators(t *testing.T) { // block for height 2 block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) - _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators, stateDB) + _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) require.Nil(t, err, tc.desc) // -> app receives a list of validators with a bool indicating if they signed @@ -146,7 +146,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) block.Time = now block.Evidence.Evidence = tc.evidence - _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), state.Validators, stateDB) + _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) require.Nil(t, err, tc.desc) // -> app must receive an index of the byzantine validator From 5df6cf563ad201ac3d4214283efc3765777d0c5c Mon Sep 17 00:00:00 2001 From: Ivan Kushmantsev Date: Thu, 2 May 2019 10:09:56 +0400 Subject: [PATCH 185/259] p2p: session should terminate on nonce wrapping (#3531) (#3609) Refs #3531 --- CHANGELOG_PENDING.md | 5 +++-- p2p/conn/secret_connection.go | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f777c6e06..8f162e072 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -20,13 +20,14 @@ ### IMPROVEMENTS: - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC -- [cli] \#3606 (https://github.com/tendermint/tendermint/issues/3585) Add option to not clear address book with unsafe reset (@climber73) +- [cli] \#3585 Add option to not clear address book with unsafe reset (@climber73) - [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `-config=` option to `testnet` cmd (@gregdhill) - [cs/replay] \#3460 check appHash for each block +- [p2p] \#3531 Terminate session on nonce wrapping (@climber73) ### BUG FIXES: - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) - [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests -- [consensus] \#3067 getBeginBlockValidatorInfo loads validators from stateDB instead of state +- [consensus] \#3067 getBeginBlockValidatorInfo loads validators from stateDB instead of state (@james-ray) - [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) diff --git a/p2p/conn/secret_connection.go b/p2p/conn/secret_connection.go index 36d6ee1bb..7f76ac800 100644 --- a/p2p/conn/secret_connection.go +++ b/p2p/conn/secret_connection.go @@ -8,6 +8,7 @@ import ( "encoding/binary" "errors" "io" + "math" "net" "sync" "time" @@ -439,6 +440,11 @@ func shareAuthSignature(sc *SecretConnection, pubKey crypto.PubKey, signature [] // (little-endian in nonce[4:]). func incrNonce(nonce *[aeadNonceSize]byte) { counter := binary.LittleEndian.Uint64(nonce[4:]) + if counter == math.MaxUint64 { + // Terminates the session and makes sure the nonce would not re-used. + // See https://github.com/tendermint/tendermint/issues/3531 + panic("can't increase nonce without overflow") + } counter++ binary.LittleEndian.PutUint64(nonce[4:], counter) } From 9926ae768ee3e4ecb200540ac6a74c074516274c Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 2 May 2019 11:53:33 +0400 Subject: [PATCH 186/259] abci/types: update comment (#3612) Fixes #3607 --- abci/types/types.pb.go | 86 +++++++++++++++++++++--------------------- abci/types/types.proto | 2 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index b09213a5f..7bf50f330 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -61,7 +61,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{0} + return fileDescriptor_types_7e896a7c04915591, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -483,7 +483,7 @@ func (m *RequestEcho) Reset() { *m = RequestEcho{} } func (m *RequestEcho) String() string { return proto.CompactTextString(m) } func (*RequestEcho) ProtoMessage() {} func (*RequestEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{1} + return fileDescriptor_types_7e896a7c04915591, []int{1} } func (m *RequestEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -529,7 +529,7 @@ func (m *RequestFlush) Reset() { *m = RequestFlush{} } func (m *RequestFlush) String() string { return proto.CompactTextString(m) } func (*RequestFlush) ProtoMessage() {} func (*RequestFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{2} + return fileDescriptor_types_7e896a7c04915591, []int{2} } func (m *RequestFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -571,7 +571,7 @@ func (m *RequestInfo) Reset() { *m = RequestInfo{} } func (m *RequestInfo) String() string { return proto.CompactTextString(m) } func (*RequestInfo) ProtoMessage() {} func (*RequestInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{3} + return fileDescriptor_types_7e896a7c04915591, []int{3} } func (m *RequestInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,7 +634,7 @@ func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } func (*RequestSetOption) ProtoMessage() {} func (*RequestSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{4} + return fileDescriptor_types_7e896a7c04915591, []int{4} } func (m *RequestSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -692,7 +692,7 @@ func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } func (*RequestInitChain) ProtoMessage() {} func (*RequestInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{5} + return fileDescriptor_types_7e896a7c04915591, []int{5} } func (m *RequestInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -770,7 +770,7 @@ func (m *RequestQuery) Reset() { *m = RequestQuery{} } func (m *RequestQuery) String() string { return proto.CompactTextString(m) } func (*RequestQuery) ProtoMessage() {} func (*RequestQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{6} + return fileDescriptor_types_7e896a7c04915591, []int{6} } func (m *RequestQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -841,7 +841,7 @@ func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } func (*RequestBeginBlock) ProtoMessage() {} func (*RequestBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{7} + return fileDescriptor_types_7e896a7c04915591, []int{7} } func (m *RequestBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -909,7 +909,7 @@ func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } func (*RequestCheckTx) ProtoMessage() {} func (*RequestCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{8} + return fileDescriptor_types_7e896a7c04915591, []int{8} } func (m *RequestCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -956,7 +956,7 @@ func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } func (*RequestDeliverTx) ProtoMessage() {} func (*RequestDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{9} + return fileDescriptor_types_7e896a7c04915591, []int{9} } func (m *RequestDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1003,7 @@ func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } func (*RequestEndBlock) ProtoMessage() {} func (*RequestEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{10} + return fileDescriptor_types_7e896a7c04915591, []int{10} } func (m *RequestEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1049,7 @@ func (m *RequestCommit) Reset() { *m = RequestCommit{} } func (m *RequestCommit) String() string { return proto.CompactTextString(m) } func (*RequestCommit) ProtoMessage() {} func (*RequestCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{11} + return fileDescriptor_types_7e896a7c04915591, []int{11} } func (m *RequestCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1102,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{12} + return fileDescriptor_types_7e896a7c04915591, []int{12} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1555,7 +1555,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{13} + return fileDescriptor_types_7e896a7c04915591, []int{13} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1602,7 +1602,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{14} + return fileDescriptor_types_7e896a7c04915591, []int{14} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1648,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{15} + return fileDescriptor_types_7e896a7c04915591, []int{15} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1692,7 +1692,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{16} + return fileDescriptor_types_7e896a7c04915591, []int{16} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1771,7 +1771,7 @@ func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } func (*ResponseSetOption) ProtoMessage() {} func (*ResponseSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{17} + return fileDescriptor_types_7e896a7c04915591, []int{17} } func (m *ResponseSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1833,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{18} + return fileDescriptor_types_7e896a7c04915591, []int{18} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1896,7 +1896,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{19} + return fileDescriptor_types_7e896a7c04915591, []int{19} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1999,7 +1999,7 @@ func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } func (*ResponseBeginBlock) ProtoMessage() {} func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{20} + return fileDescriptor_types_7e896a7c04915591, []int{20} } func (m *ResponseBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2053,7 +2053,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{21} + return fileDescriptor_types_7e896a7c04915591, []int{21} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2156,7 +2156,7 @@ func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } func (*ResponseDeliverTx) ProtoMessage() {} func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{22} + return fileDescriptor_types_7e896a7c04915591, []int{22} } func (m *ResponseDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2254,7 +2254,7 @@ func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } func (*ResponseEndBlock) ProtoMessage() {} func (*ResponseEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{23} + return fileDescriptor_types_7e896a7c04915591, []int{23} } func (m *ResponseEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2316,7 +2316,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{24} + return fileDescriptor_types_7e896a7c04915591, []int{24} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2367,7 +2367,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } func (*ConsensusParams) ProtoMessage() {} func (*ConsensusParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{25} + return fileDescriptor_types_7e896a7c04915591, []int{25} } func (m *ConsensusParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2417,7 +2417,7 @@ func (m *ConsensusParams) GetValidator() *ValidatorParams { return nil } -// BlockParams contains limits on the block size and timestamp. +// BlockParams contains limits on the block size. type BlockParams struct { // Note: must be greater than 0 MaxBytes int64 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` @@ -2432,7 +2432,7 @@ func (m *BlockParams) Reset() { *m = BlockParams{} } func (m *BlockParams) String() string { return proto.CompactTextString(m) } func (*BlockParams) ProtoMessage() {} func (*BlockParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{26} + return fileDescriptor_types_7e896a7c04915591, []int{26} } func (m *BlockParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2488,7 +2488,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } func (m *EvidenceParams) String() string { return proto.CompactTextString(m) } func (*EvidenceParams) ProtoMessage() {} func (*EvidenceParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{27} + return fileDescriptor_types_7e896a7c04915591, []int{27} } func (m *EvidenceParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2536,7 +2536,7 @@ func (m *ValidatorParams) Reset() { *m = ValidatorParams{} } func (m *ValidatorParams) String() string { return proto.CompactTextString(m) } func (*ValidatorParams) ProtoMessage() {} func (*ValidatorParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{28} + return fileDescriptor_types_7e896a7c04915591, []int{28} } func (m *ValidatorParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2584,7 +2584,7 @@ func (m *LastCommitInfo) Reset() { *m = LastCommitInfo{} } func (m *LastCommitInfo) String() string { return proto.CompactTextString(m) } func (*LastCommitInfo) ProtoMessage() {} func (*LastCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{29} + return fileDescriptor_types_7e896a7c04915591, []int{29} } func (m *LastCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2658,7 +2658,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{30} + return fileDescriptor_types_7e896a7c04915591, []int{30} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2811,7 +2811,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{31} + return fileDescriptor_types_7e896a7c04915591, []int{31} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2866,7 +2866,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{32} + return fileDescriptor_types_7e896a7c04915591, []int{32} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2921,7 +2921,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{33} + return fileDescriptor_types_7e896a7c04915591, []int{33} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2978,7 +2978,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{34} + return fileDescriptor_types_7e896a7c04915591, []int{34} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3034,7 +3034,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{35} + return fileDescriptor_types_7e896a7c04915591, []int{35} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3090,7 +3090,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{36} + return fileDescriptor_types_7e896a7c04915591, []int{36} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3145,7 +3145,7 @@ func (m *PubKey) Reset() { *m = PubKey{} } func (m *PubKey) String() string { return proto.CompactTextString(m) } func (*PubKey) ProtoMessage() {} func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{37} + return fileDescriptor_types_7e896a7c04915591, []int{37} } func (m *PubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3203,7 +3203,7 @@ func (m *Evidence) Reset() { *m = Evidence{} } func (m *Evidence) String() string { return proto.CompactTextString(m) } func (*Evidence) ProtoMessage() {} func (*Evidence) Descriptor() ([]byte, []int) { - return fileDescriptor_types_a177e47fab90f91d, []int{38} + return fileDescriptor_types_7e896a7c04915591, []int{38} } func (m *Evidence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -15357,12 +15357,12 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_a177e47fab90f91d) } +func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_7e896a7c04915591) } func init() { - golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_a177e47fab90f91d) + golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_7e896a7c04915591) } -var fileDescriptor_types_a177e47fab90f91d = []byte{ +var fileDescriptor_types_7e896a7c04915591 = []byte{ // 2203 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcf, 0x73, 0x1c, 0x47, 0xf5, 0xd7, 0xec, 0xef, 0x79, 0xab, 0xfd, 0xe1, 0xb6, 0x6c, 0xaf, 0xf7, 0x9b, 0xaf, 0xe4, 0x1a, diff --git a/abci/types/types.proto b/abci/types/types.proto index 8eeecb392..4fdf575c6 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -212,7 +212,7 @@ message ConsensusParams { ValidatorParams validator = 3; } -// BlockParams contains limits on the block size and timestamp. +// BlockParams contains limits on the block size. message BlockParams { // Note: must be greater than 0 int64 max_bytes = 1; From 8711af608fff1cfe8d1de1ba8e91d6b0a25a9211 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 3 May 2019 17:21:56 +0400 Subject: [PATCH 187/259] p2p: make persistent prop independent of conn direction (#3593) ## Description Previously only outbound peers can be persistent. Now, even if the peer is inbound, if it's marked as persistent, when/if conn is lost, Tendermint will try to reconnect. This part is actually optional and can be reverted. Plus, seed won't disconnect from inbound peer if it's marked as persistent. Fixes #3362 ## Commits * make persistent prop independent of conn direction Previously only outbound peers can be persistent. Now, even if the peer is inbound, if it's marked as persistent, when/if conn is lost, Tendermint will try to reconnect. Plus, seed won't disconnect from inbound peer if it's marked as persistent. Fixes #3362 * fix TestPEXReactorDialPeer test * add a changelog entry * update changelog * add two tests * reformat code * test UnsafeDialPeers and UnsafeDialSeeds * add TestSwitchDialPeersAsync * spec: update p2p/config spec * fixes after Ismail's review * Apply suggestions from code review Co-Authored-By: melekes * fix merge conflict * remove sleep from TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode We don't need it actually. --- CHANGELOG_PENDING.md | 5 ++ docs/spec/p2p/config.md | 12 ++-- node/node.go | 13 ++-- p2p/pex/pex_reactor.go | 5 +- p2p/pex/pex_reactor_test.go | 42 ++++++++++++- p2p/switch.go | 121 ++++++++++++++++++++++++++---------- p2p/switch_test.go | 111 +++++++++++++++++++++------------ p2p/transport.go | 28 +++++++-- rpc/core/net.go | 14 ++--- rpc/core/net_test.go | 73 ++++++++++++++++++++++ rpc/core/pipe.go | 3 +- 11 files changed, 323 insertions(+), 104 deletions(-) create mode 100644 rpc/core/net_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8f162e072..635d0dd1a 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -23,11 +23,16 @@ - [cli] \#3585 Add option to not clear address book with unsafe reset (@climber73) - [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `-config=` option to `testnet` cmd (@gregdhill) - [cs/replay] \#3460 check appHash for each block +- [rpc] \#3362 `/dial_seeds` & `/dial_peers` return errors if addresses are incorrect (except when IP lookup fails) +- [node] \#3362 returns an error if `persistent_peers` list is invalid (except when IP lookup fails) - [p2p] \#3531 Terminate session on nonce wrapping (@climber73) ### BUG FIXES: - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) - [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests +- [p2p] \#3362 make persistent prop independent of conn direction + * `Switch#DialPeersAsync` now only takes a list of peers + * `Switch#DialPeerWithAddress` now only takes an address - [consensus] \#3067 getBeginBlockValidatorInfo loads validators from stateDB instead of state (@james-ray) - [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) diff --git a/docs/spec/p2p/config.md b/docs/spec/p2p/config.md index b31a36736..7ff2b5e8d 100644 --- a/docs/spec/p2p/config.md +++ b/docs/spec/p2p/config.md @@ -12,14 +12,14 @@ and upon incoming connection shares some peers and disconnects. ## Seeds -`--p2p.seeds “1.2.3.4:26656,2.3.4.5:4444”` +`--p2p.seeds “id100000000000000000000000000000000@1.2.3.4:26656,id200000000000000000000000000000000@2.3.4.5:4444”` Dials these seeds when we need more peers. They should return a list of peers and then disconnect. If we already have enough peers in the address book, we may never need to dial them. ## Persistent Peers -`--p2p.persistent_peers “1.2.3.4:26656,2.3.4.5:26656”` +`--p2p.persistent_peers “id100000000000000000000000000000000@1.2.3.4:26656,id200000000000000000000000000000000@2.3.4.5:26656”` Dial these peers and auto-redial them if the connection fails. These are intended to be trusted persistent peers that can help @@ -30,9 +30,9 @@ backoff and will give up after a day of trying to connect. the user will be warned that seeds may auto-close connections and that the node may not be able to keep the connection persistent. -## Private Persistent Peers +## Private Peers -`--p2p.private_persistent_peers “1.2.3.4:26656,2.3.4.5:26656”` +`--p2p.private_peer_ids “id100000000000000000000000000000000,id200000000000000000000000000000000”` -These are persistent peers that we do not add to the address book or -gossip to other peers. They stay private to us. +These are IDs of the peers that we do not add to the address book or gossip to +other peers. They stay private to us. diff --git a/node/node.go b/node/node.go index 36bdb9d57..5af080ad1 100644 --- a/node/node.go +++ b/node/node.go @@ -579,6 +579,11 @@ func NewNode(config *cfg.Config, consensusReactor, evidenceReactor, nodeInfo, nodeKey, p2pLogger, ) + err = sw.AddPersistentPeers(splitAndTrimEmpty(config.P2P.PersistentPeers, ",", " ")) + if err != nil { + return nil, errors.Wrap(err, "could not add peers from persistent_peers field") + } + addrBook := createAddrBookAndSetOnSwitch(config, sw, p2pLogger) // Optionally, start the pex reactor @@ -675,12 +680,8 @@ func (n *Node) OnStart() error { } // Always connect to persistent peers - if n.config.P2P.PersistentPeers != "" { - err = n.sw.DialPeersAsync(n.addrBook, splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " "), true) - if err != nil { - return err - } - } + // parsing errors are handled above by AddPersistentPeers + _ = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " ")) return nil } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index f24f44dd5..957dbf802 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -531,8 +531,7 @@ func (r *PEXReactor) dialPeer(addr *p2p.NetAddress) error { } } - err := r.Switch.DialPeerWithAddress(addr, false) - + err := r.Switch.DialPeerWithAddress(addr) if err != nil { if _, ok := err.(p2p.ErrCurrentlyDialingOrExistingAddress); ok { return err @@ -584,7 +583,7 @@ func (r *PEXReactor) dialSeeds() { for _, i := range perm { // dial a random seed seedAddr := r.seedAddrs[i] - err := r.Switch.DialPeerWithAddress(seedAddr, false) + err := r.Switch.DialPeerWithAddress(seedAddr) if err == nil { return } diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 6572a5f61..8c52a25ee 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -291,7 +291,8 @@ func TestPEXReactorSeedMode(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) // nolint: errcheck - pexR, book := createReactor(&PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 10 * time.Millisecond}) + pexRConfig := &PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 10 * time.Millisecond} + pexR, book := createReactor(pexRConfig) defer teardownReactor(book) sw := createSwitchAndAddReactors(pexR) @@ -315,13 +316,48 @@ func TestPEXReactorSeedMode(t *testing.T) { pexR.attemptDisconnects() assert.Equal(t, 1, sw.Peers().Size()) - time.Sleep(100 * time.Millisecond) + // sleep for SeedDisconnectWaitPeriod + time.Sleep(pexRConfig.SeedDisconnectWaitPeriod + 1*time.Millisecond) // 3. attemptDisconnects should disconnect after wait period pexR.attemptDisconnects() assert.Equal(t, 0, sw.Peers().Size()) } +func TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode(t *testing.T) { + // directory to store address books + dir, err := ioutil.TempDir("", "pex_reactor") + require.Nil(t, err) + defer os.RemoveAll(dir) // nolint: errcheck + + pexR, book := createReactor(&PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 1 * time.Millisecond}) + defer teardownReactor(book) + + sw := createSwitchAndAddReactors(pexR) + sw.SetAddrBook(book) + err = sw.Start() + require.NoError(t, err) + defer sw.Stop() + + assert.Zero(t, sw.Peers().Size()) + + peerSwitch := testCreateDefaultPeer(dir, 1) + require.NoError(t, peerSwitch.Start()) + defer peerSwitch.Stop() + + err = sw.AddPersistentPeers([]string{peerSwitch.NetAddress().String()}) + require.NoError(t, err) + + // 1. Test crawlPeers dials the peer + pexR.crawlPeers([]*p2p.NetAddress{peerSwitch.NetAddress()}) + assert.Equal(t, 1, sw.Peers().Size()) + assert.True(t, sw.Peers().Has(peerSwitch.NodeInfo().ID())) + + // 2. attemptDisconnects should not disconnect because the peer is persistent + pexR.attemptDisconnects() + assert.Equal(t, 1, sw.Peers().Size()) +} + func TestPEXReactorDialsPeerUpToMaxAttemptsInSeedMode(t *testing.T) { // directory to store address books dir, err := ioutil.TempDir("", "pex_reactor") @@ -398,7 +434,7 @@ func TestPEXReactorSeedModeFlushStop(t *testing.T) { reactor := switches[0].Reactors()["pex"].(*PEXReactor) peerID := switches[1].NodeInfo().ID() - err = switches[1].DialPeerWithAddress(switches[0].NetAddress(), false) + err = switches[1].DialPeerWithAddress(switches[0].NetAddress()) assert.NoError(t, err) // sleep up to a second while waiting for the peer to send us a message. diff --git a/p2p/switch.go b/p2p/switch.go index a566e6029..ccb7119d2 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -77,6 +77,8 @@ type Switch struct { nodeInfo NodeInfo // our node info nodeKey *NodeKey // our node privkey addrBook AddrBook + // peers addresses with whom we'll maintain constant connection + persistentPeersAddrs []*NetAddress transport Transport @@ -104,16 +106,17 @@ func NewSwitch( options ...SwitchOption, ) *Switch { sw := &Switch{ - config: cfg, - reactors: make(map[string]Reactor), - chDescs: make([]*conn.ChannelDescriptor, 0), - reactorsByCh: make(map[byte]Reactor), - peers: NewPeerSet(), - dialing: cmn.NewCMap(), - reconnecting: cmn.NewCMap(), - metrics: NopMetrics(), - transport: transport, - filterTimeout: defaultFilterTimeout, + config: cfg, + reactors: make(map[string]Reactor), + chDescs: make([]*conn.ChannelDescriptor, 0), + reactorsByCh: make(map[byte]Reactor), + peers: NewPeerSet(), + dialing: cmn.NewCMap(), + reconnecting: cmn.NewCMap(), + metrics: NopMetrics(), + transport: transport, + filterTimeout: defaultFilterTimeout, + persistentPeersAddrs: make([]*NetAddress, 0), } // Ensure we have a completely undeterministic PRNG. @@ -297,7 +300,19 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) { sw.stopAndRemovePeer(peer, reason) if peer.IsPersistent() { - go sw.reconnectToPeer(peer.SocketAddr()) + var addr *NetAddress + if peer.IsOutbound() { // socket address for outbound peers + addr = peer.SocketAddr() + } else { // self-reported address for inbound peers + var err error + addr, err = peer.NodeInfo().NetAddress() + if err != nil { + sw.Logger.Error("Wanted to reconnect to inbound peer, but self-reported address is wrong", + "peer", peer, "addr", addr, "err", err) + return + } + } + go sw.reconnectToPeer(addr) } } @@ -341,7 +356,7 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { return } - err := sw.DialPeerWithAddress(addr, true) + err := sw.DialPeerWithAddress(addr) if err == nil { return // success } else if _, ok := err.(ErrCurrentlyDialingOrExistingAddress); ok { @@ -365,7 +380,7 @@ func (sw *Switch) reconnectToPeer(addr *NetAddress) { sleepIntervalSeconds := math.Pow(reconnectBackOffBaseSeconds, float64(i)) sw.randomSleep(time.Duration(sleepIntervalSeconds) * time.Second) - err := sw.DialPeerWithAddress(addr, true) + err := sw.DialPeerWithAddress(addr) if err == nil { return // success } else if _, ok := err.(ErrCurrentlyDialingOrExistingAddress); ok { @@ -401,28 +416,41 @@ func isPrivateAddr(err error) bool { return ok && te.PrivateAddr() } -// DialPeersAsync dials a list of peers asynchronously in random order (optionally, making them persistent). +// DialPeersAsync dials a list of peers asynchronously in random order. // Used to dial peers from config on startup or from unsafe-RPC (trusted sources). -// TODO: remove addrBook arg since it's now set on the switch -func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent bool) error { +// It ignores ErrNetAddressLookup. However, if there are other errors, first +// encounter is returned. +// Nop if there are no peers. +func (sw *Switch) DialPeersAsync(peers []string) error { netAddrs, errs := NewNetAddressStrings(peers) - // only log errors, dial correct addresses + // report all the errors for _, err := range errs { sw.Logger.Error("Error in peer's address", "err", err) } + // return first non-ErrNetAddressLookup error + for _, err := range errs { + if _, ok := err.(ErrNetAddressLookup); ok { + continue + } + return err + } + sw.dialPeersAsync(netAddrs) + return nil +} +func (sw *Switch) dialPeersAsync(netAddrs []*NetAddress) { ourAddr := sw.NetAddress() // TODO: this code feels like it's in the wrong place. // The integration tests depend on the addrBook being saved // right away but maybe we can change that. Recall that // the addrBook is only written to disk every 2min - if addrBook != nil { + if sw.addrBook != nil { // add peers to `addrBook` for _, netAddr := range netAddrs { // do not add our address or ID if !netAddr.Same(ourAddr) { - if err := addrBook.AddAddress(netAddr, ourAddr); err != nil { + if err := sw.addrBook.AddAddress(netAddr, ourAddr); err != nil { if isPrivateAddr(err) { sw.Logger.Debug("Won't add peer's address to addrbook", "err", err) } else { @@ -433,7 +461,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b } // Persist some peers to disk right away. // NOTE: integration tests depend on this - addrBook.Save() + sw.addrBook.Save() } // permute the list, dial them in random order. @@ -450,7 +478,7 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b sw.randomSleep(0) - err := sw.DialPeerWithAddress(addr, persistent) + err := sw.DialPeerWithAddress(addr) if err != nil { switch err.(type) { case ErrSwitchConnectToSelf, ErrSwitchDuplicatePeerID, ErrCurrentlyDialingOrExistingAddress: @@ -461,16 +489,13 @@ func (sw *Switch) DialPeersAsync(addrBook AddrBook, peers []string, persistent b } }(i) } - return nil } // DialPeerWithAddress dials the given peer and runs sw.addPeer if it connects // and authenticates successfully. -// If `persistent == true`, the switch will always try to reconnect to this -// peer if the connection ever fails. // If we're currently dialing this address or it belongs to an existing peer, // ErrCurrentlyDialingOrExistingAddress is returned. -func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) error { +func (sw *Switch) DialPeerWithAddress(addr *NetAddress) error { if sw.IsDialingOrExistingAddress(addr) { return ErrCurrentlyDialingOrExistingAddress{addr.String()} } @@ -478,7 +503,7 @@ func (sw *Switch) DialPeerWithAddress(addr *NetAddress, persistent bool) error { sw.dialing.Set(string(addr.ID), addr) defer sw.dialing.Delete(string(addr.ID)) - return sw.addOutboundPeerWithConfig(addr, sw.config, persistent) + return sw.addOutboundPeerWithConfig(addr, sw.config) } // sleep for interval plus some random amount of ms on [0, dialRandomizerIntervalMilliseconds] @@ -495,6 +520,38 @@ func (sw *Switch) IsDialingOrExistingAddress(addr *NetAddress) bool { (!sw.config.AllowDuplicateIP && sw.peers.HasIP(addr.IP)) } +// AddPersistentPeers allows you to set persistent peers. It ignores +// ErrNetAddressLookup. However, if there are other errors, first encounter is +// returned. +func (sw *Switch) AddPersistentPeers(addrs []string) error { + sw.Logger.Info("Adding persistent peers", "addrs", addrs) + netAddrs, errs := NewNetAddressStrings(addrs) + // report all the errors + for _, err := range errs { + sw.Logger.Error("Error in peer's address", "err", err) + } + // return first non-ErrNetAddressLookup error + for _, err := range errs { + if _, ok := err.(ErrNetAddressLookup); ok { + continue + } + return err + } + sw.persistentPeersAddrs = netAddrs + return nil +} + +func (sw *Switch) isPeerPersistentFn() func(*NetAddress) bool { + return func(na *NetAddress) bool { + for _, pa := range sw.persistentPeersAddrs { + if pa.Equals(na) { + return true + } + } + return false + } +} + func (sw *Switch) acceptRoutine() { for { p, err := sw.transport.Accept(peerConfig{ @@ -502,6 +559,7 @@ func (sw *Switch) acceptRoutine() { onPeerError: sw.StopPeerForError, reactorsByCh: sw.reactorsByCh, metrics: sw.metrics, + isPersistent: sw.isPeerPersistentFn(), }) if err != nil { switch err := err.(type) { @@ -581,13 +639,12 @@ func (sw *Switch) acceptRoutine() { // dial the peer; make secret connection; authenticate against the dialed ID; // add the peer. -// if dialing fails, start the reconnect loop. If handhsake fails, its over. -// If peer is started succesffuly, reconnectLoop will start when -// StopPeerForError is called +// if dialing fails, start the reconnect loop. If handshake fails, it's over. +// If peer is started successfully, reconnectLoop will start when +// StopPeerForError is called. func (sw *Switch) addOutboundPeerWithConfig( addr *NetAddress, cfg *config.P2PConfig, - persistent bool, ) error { sw.Logger.Info("Dialing peer", "address", addr) @@ -600,7 +657,7 @@ func (sw *Switch) addOutboundPeerWithConfig( p, err := sw.transport.Dial(*addr, peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, - persistent: persistent, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, metrics: sw.metrics, }) @@ -619,7 +676,7 @@ func (sw *Switch) addOutboundPeerWithConfig( // retry persistent peers after // any dial error besides IsSelf() - if persistent { + if sw.isPeerPersistentFn()(addr) { go sw.reconnectToPeer(addr) } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index bf105e0fa..6c7538b51 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -167,7 +167,7 @@ func TestSwitchFiltersOutItself(t *testing.T) { rp.Start() // addr should be rejected in addPeer based on the same ID - err := s1.DialPeerWithAddress(rp.Addr(), false) + err := s1.DialPeerWithAddress(rp.Addr()) if assert.Error(t, err) { if err, ok := err.(ErrRejected); ok { if !err.IsSelf() { @@ -212,6 +212,7 @@ func TestSwitchPeerFilter(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) if err != nil { @@ -256,6 +257,7 @@ func TestSwitchPeerFilterTimeout(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) if err != nil { @@ -281,6 +283,7 @@ func TestSwitchPeerFilterDuplicate(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) if err != nil { @@ -326,6 +329,7 @@ func TestSwitchStopsNonPersistentPeerOnError(t *testing.T) { p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ chDescs: sw.chDescs, onPeerError: sw.StopPeerForError, + isPersistent: sw.isPeerPersistentFn(), reactorsByCh: sw.reactorsByCh, }) require.Nil(err) @@ -390,49 +394,33 @@ func TestSwitchStopPeerForError(t *testing.T) { assert.EqualValues(t, 0, peersMetricValue()) } -func TestSwitchReconnectsToPersistentPeer(t *testing.T) { - assert, require := assert.New(t), require.New(t) - +func TestSwitchReconnectsToOutboundPersistentPeer(t *testing.T) { sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) err := sw.Start() - if err != nil { - t.Error(err) - } + require.NoError(t, err) defer sw.Stop() - // simulate remote peer + // 1. simulate failure by closing connection rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} rp.Start() defer rp.Stop() - p, err := sw.transport.Dial(*rp.Addr(), peerConfig{ - chDescs: sw.chDescs, - onPeerError: sw.StopPeerForError, - persistent: true, - reactorsByCh: sw.reactorsByCh, - }) - require.Nil(err) + err = sw.AddPersistentPeers([]string{rp.Addr().String()}) + require.NoError(t, err) - require.Nil(sw.addPeer(p)) + err = sw.DialPeerWithAddress(rp.Addr()) + require.Nil(t, err) + time.Sleep(50 * time.Millisecond) + require.NotNil(t, sw.Peers().Get(rp.ID())) - require.NotNil(sw.Peers().Get(rp.ID())) - - // simulate failure by closing connection + p := sw.Peers().List()[0] p.(*peer).CloseConn() - // TODO: remove sleep, detect the disconnection, wait for reconnect - npeers := sw.Peers().Size() - for i := 0; i < 20; i++ { - time.Sleep(250 * time.Millisecond) - npeers = sw.Peers().Size() - if npeers > 0 { - break - } - } - assert.NotZero(npeers) - assert.False(p.IsRunning()) + waitUntilSwitchHasAtLeastNPeers(sw, 1) + assert.False(t, p.IsRunning()) // old peer instance + assert.Equal(t, 1, sw.Peers().Size()) // new peer instance - // simulate another remote peer + // 2. simulate first time dial failure rp = &remotePeer{ PrivKey: ed25519.GenPrivKey(), Config: cfg, @@ -443,23 +431,68 @@ func TestSwitchReconnectsToPersistentPeer(t *testing.T) { rp.Start() defer rp.Stop() - // simulate first time dial failure conf := config.DefaultP2PConfig() conf.TestDialFail = true - err = sw.addOutboundPeerWithConfig(rp.Addr(), conf, true) - require.NotNil(err) - + err = sw.addOutboundPeerWithConfig(rp.Addr(), conf) + require.NotNil(t, err) // DialPeerWithAddres - sw.peerConfig resets the dialer + waitUntilSwitchHasAtLeastNPeers(sw, 2) + assert.Equal(t, 2, sw.Peers().Size()) +} - // TODO: same as above +func TestSwitchReconnectsToInboundPersistentPeer(t *testing.T) { + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + // 1. simulate failure by closing the connection + rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} + rp.Start() + defer rp.Stop() + + err = sw.AddPersistentPeers([]string{rp.Addr().String()}) + require.NoError(t, err) + + conn, err := rp.Dial(sw.NetAddress()) + require.NoError(t, err) + time.Sleep(50 * time.Millisecond) + require.NotNil(t, sw.Peers().Get(rp.ID())) + + conn.Close() + + waitUntilSwitchHasAtLeastNPeers(sw, 1) + assert.Equal(t, 1, sw.Peers().Size()) +} + +func TestSwitchDialPeersAsync(t *testing.T) { + if testing.Short() { + return + } + + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", initSwitchFunc) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} + rp.Start() + defer rp.Stop() + + err = sw.DialPeersAsync([]string{rp.Addr().String()}) + require.NoError(t, err) + time.Sleep(dialRandomizerIntervalMilliseconds * time.Millisecond) + require.NotNil(t, sw.Peers().Get(rp.ID())) +} + +func waitUntilSwitchHasAtLeastNPeers(sw *Switch, n int) { for i := 0; i < 20; i++ { time.Sleep(250 * time.Millisecond) - npeers = sw.Peers().Size() - if npeers > 1 { + has := sw.Peers().Size() + if has >= n { break } } - assert.EqualValues(2, npeers) } func TestSwitchFullConnectivity(t *testing.T) { diff --git a/p2p/transport.go b/p2p/transport.go index ebf77c9f4..8d6ea236e 100644 --- a/p2p/transport.go +++ b/p2p/transport.go @@ -37,11 +37,15 @@ type accept struct { // events. // TODO(xla): Refactor out with more static Reactor setup and PeerBehaviour. type peerConfig struct { - chDescs []*conn.ChannelDescriptor - onPeerError func(Peer, interface{}) - outbound, persistent bool - reactorsByCh map[byte]Reactor - metrics *Metrics + chDescs []*conn.ChannelDescriptor + onPeerError func(Peer, interface{}) + outbound bool + // isPersistent allows you to set a function, which, given socket address + // (for outbound peers) OR self-reported address (for inbound peers), tells + // if the peer is persistent or not. + isPersistent func(*NetAddress) bool + reactorsByCh map[byte]Reactor + metrics *Metrics } // Transport emits and connects to Peers. The implementation of Peer is left to @@ -446,9 +450,21 @@ func (mt *MultiplexTransport) wrapPeer( socketAddr *NetAddress, ) Peer { + persistent := false + if cfg.isPersistent != nil { + if cfg.outbound { + persistent = cfg.isPersistent(socketAddr) + } else { + selfReportedAddr, err := ni.NetAddress() + if err == nil { + persistent = cfg.isPersistent(selfReportedAddr) + } + } + } + peerConn := newPeerConn( cfg.outbound, - cfg.persistent, + persistent, c, socketAddr, ) diff --git a/rpc/core/net.go b/rpc/core/net.go index 23bc40e88..48bf576a8 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -184,10 +184,8 @@ func UnsafeDialSeeds(ctx *rpctypes.Context, seeds []string) (*ctypes.ResultDialS if len(seeds) == 0 { return &ctypes.ResultDialSeeds{}, errors.New("No seeds provided") } - // starts go routines to dial each peer after random delays - logger.Info("DialSeeds", "addrBook", addrBook, "seeds", seeds) - err := p2pPeers.DialPeersAsync(addrBook, seeds, false) - if err != nil { + logger.Info("DialSeeds", "seeds", seeds) + if err := p2pPeers.DialPeersAsync(seeds); err != nil { return &ctypes.ResultDialSeeds{}, err } return &ctypes.ResultDialSeeds{Log: "Dialing seeds in progress. See /net_info for details"}, nil @@ -197,12 +195,12 @@ func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*c if len(peers) == 0 { return &ctypes.ResultDialPeers{}, errors.New("No peers provided") } - // starts go routines to dial each peer after random delays - logger.Info("DialPeers", "addrBook", addrBook, "peers", peers, "persistent", persistent) - err := p2pPeers.DialPeersAsync(addrBook, peers, persistent) - if err != nil { + logger.Info("DialPeers", "peers", peers, "persistent", persistent) + if err := p2pPeers.AddPersistentPeers(peers); err != nil { return &ctypes.ResultDialPeers{}, err } + // parsing errors are handled above by AddPersistentPeers + _ = p2pPeers.DialPeersAsync(peers) return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil } diff --git a/rpc/core/net_test.go b/rpc/core/net_test.go new file mode 100644 index 000000000..651e1f69d --- /dev/null +++ b/rpc/core/net_test.go @@ -0,0 +1,73 @@ +package core + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + cfg "github.com/tendermint/tendermint/config" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" +) + +func TestUnsafeDialSeeds(t *testing.T) { + sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", + func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + logger = log.TestingLogger() + p2pPeers = sw + + testCases := []struct { + seeds []string + isErr bool + }{ + {[]string{}, true}, + {[]string{"d51fb70907db1c6c2d5237e78379b25cf1a37ab4@127.0.0.1:41198"}, false}, + {[]string{"127.0.0.1:41198"}, true}, + } + + for _, tc := range testCases { + res, err := UnsafeDialSeeds(&rpctypes.Context{}, tc.seeds) + if tc.isErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, res) + } + } +} + +func TestUnsafeDialPeers(t *testing.T) { + sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", + func(n int, sw *p2p.Switch) *p2p.Switch { return sw }) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + logger = log.TestingLogger() + p2pPeers = sw + + testCases := []struct { + peers []string + isErr bool + }{ + {[]string{}, true}, + {[]string{"d51fb70907db1c6c2d5237e78379b25cf1a37ab4@127.0.0.1:41198"}, false}, + {[]string{"127.0.0.1:41198"}, true}, + } + + for _, tc := range testCases { + res, err := UnsafeDialPeers(&rpctypes.Context{}, tc.peers, false) + if tc.isErr { + assert.Error(t, err) + } else { + assert.NoError(t, err) + assert.NotNil(t, res) + } + } +} diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index ad8afdefc..cefb0e371 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -44,7 +44,8 @@ type transport interface { } type peers interface { - DialPeersAsync(p2p.AddrBook, []string, bool) error + AddPersistentPeers([]string) error + DialPeersAsync([]string) error NumPeers() (outbound, inbound, dialig int) Peers() p2p.IPeerSet } From 5051a1f7bcda164835592baccee3caf0d02984ec Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 4 May 2019 10:41:31 +0400 Subject: [PATCH 188/259] mempool: move interface into mempool package (#3524) ## Description Refs #2659 Breaking changes in the mempool package: [mempool] #2659 Mempool now an interface old Mempool renamed to CListMempool NewMempool renamed to NewCListMempool Option renamed to CListOption MempoolReactor renamed to Reactor NewMempoolReactor renamed to NewReactor unexpose TxID method TxInfo.PeerID renamed to SenderID unexpose MempoolReactor.Mempool Breaking changes in the state package: [state] #2659 Mempool interface moved to mempool package MockMempool moved to top-level mock package and renamed to Mempool Non Breaking changes in the node package: [node] #2659 Add Mempool method, which allows you to access mempool ## Commits * move Mempool interface into mempool package Refs #2659 Breaking changes in the mempool package: - Mempool now an interface - old Mempool renamed to CListMempool Breaking changes to state package: - MockMempool moved to mempool/mock package and renamed to Mempool - Mempool interface moved to mempool package * assert CListMempool impl Mempool * gofmt code * rename MempoolReactor to Reactor - combine everything into one interface - rename TxInfo.PeerID to TxInfo.SenderID - unexpose MempoolReactor.Mempool * move mempool mock into top-level mock package * add a fixme TxsFront should not be a part of the Mempool interface because it leaks implementation details. Instead, we need to come up with general interface for querying the mempool so the MempoolReactor can fetch and broadcast txs to peers. * change node#Mempool to return interface * save commit = new reactor arch * Revert "save commit = new reactor arch" This reverts commit 1bfceacd9d65a720574683a7f22771e69af9af4d. * require CListMempool in mempool.Reactor * add two changelog entries * fixes after my own review * quote interfaces, structs and functions * fixes after Ismail's review * make node's mempool an interface * make InitWAL/CloseWAL methods a part of Mempool interface * fix merge conflicts * make node's mempool an interface --- CHANGELOG_PENDING.md | 12 + blockchain/reactor_test.go | 3 +- consensus/common_test.go | 2 +- consensus/mempool_test.go | 5 +- consensus/reactor_test.go | 2 +- consensus/replay.go | 4 +- consensus/replay_file.go | 3 +- consensus/replay_test.go | 3 +- consensus/wal_generator.go | 4 +- mempool/clist_mempool.go | 714 ++++++++++++++ ...{mempool_test.go => clist_mempool_test.go} | 23 +- mempool/doc.go | 24 + mempool/errors.go | 46 + mempool/mempool.go | 868 ++---------------- mempool/reactor.go | 44 +- mempool/reactor_test.go | 47 +- mock/mempool.go | 45 + node/node.go | 39 +- node/node_test.go | 2 +- rpc/client/rpc_test.go | 9 +- rpc/core/pipe.go | 4 +- state/execution.go | 5 +- state/execution_test.go | 10 +- state/services.go | 55 +- 24 files changed, 1025 insertions(+), 948 deletions(-) create mode 100644 mempool/clist_mempool.go rename mempool/{mempool_test.go => clist_mempool_test.go} (96%) create mode 100644 mempool/doc.go create mode 100644 mempool/errors.go create mode 100644 mock/mempool.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 635d0dd1a..3b6dffb3a 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,6 +9,17 @@ * Apps * Go API +- [mempool] \#2659 `Mempool` now an interface + * old `Mempool` implementation renamed to `CListMempool` + * `NewMempool` renamed to `NewCListMempool` + * `Option` renamed to `CListOption` + * unexpose `MempoolReactor.Mempool` + * `MempoolReactor` renamed to `Reactor` + * `NewMempoolReactor` renamed to `NewReactor` + * unexpose `TxID` method + * `TxInfo.PeerID` renamed to `SenderID` +- [state] \#2659 `Mempool` interface moved to mempool package + * `MockMempool` moved to top-level mock package and renamed to `Mempool` - [libs/common] Removed `PanicSanity`, `PanicCrisis`, `PanicConsensus` and `PanicQ` - [node] Moved `GenesisDocProvider` and `DefaultGenesisDocProviderFunc` to state package @@ -17,6 +28,7 @@ * P2P Protocol ### FEATURES: +- [node] \#2659 Add `node.Mempool()` method, which allows you to access mempool ### IMPROVEMENTS: - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 9cd3489c8..52ef9ba64 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -13,6 +13,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/p2p" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -93,7 +94,7 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals fastSync := true db := dbm.NewMemDB() blockExec := sm.NewBlockExecutor(db, log.TestingLogger(), proxyApp.Consensus(), - sm.MockMempool{}, sm.MockEvidencePool{}) + mock.Mempool{}, sm.MockEvidencePool{}) sm.SaveState(db, state) // let's add some blocks in diff --git a/consensus/common_test.go b/consensus/common_test.go index a0c179ae1..a4ad79c94 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -288,7 +288,7 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S proxyAppConnCon := abcicli.NewLocalClient(mtx, app) // Make Mempool - mempool := mempl.NewMempool(thisConfig.Mempool, proxyAppConnMem, 0) + mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0) mempool.SetLogger(log.TestingLogger().With("module", "mempool")) if thisConfig.Consensus.WaitForTxs() { mempool.EnableTxsAvailable() diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index 1baa92b89..af15a1fef 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -12,13 +12,14 @@ import ( "github.com/tendermint/tendermint/abci/example/code" abci "github.com/tendermint/tendermint/abci/types" dbm "github.com/tendermint/tendermint/libs/db" + mempl "github.com/tendermint/tendermint/mempool" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) // for testing -func assertMempool(txn txNotifier) sm.Mempool { - return txn.(sm.Mempool) +func assertMempool(txn txNotifier) mempl.Mempool { + return txn.(mempl.Mempool) } func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) { diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index bf58a8eeb..9440b50c3 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -140,7 +140,7 @@ func TestReactorWithEvidence(t *testing.T) { proxyAppConnCon := abcicli.NewLocalClient(mtx, app) // Make Mempool - mempool := mempl.NewMempool(thisConfig.Mempool, proxyAppConnMem, 0) + mempool := mempl.NewCListMempool(thisConfig.Mempool, proxyAppConnMem, 0) mempool.SetLogger(log.TestingLogger().With("module", "mempool")) if thisConfig.Consensus.WaitForTxs() { mempool.EnableTxsAvailable() diff --git a/consensus/replay.go b/consensus/replay.go index cc1344eba..794f870d0 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -16,7 +16,7 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -454,7 +454,7 @@ func (h *Handshaker) replayBlock(state sm.State, height int64, proxyApp proxy.Ap block := h.store.LoadBlock(height) meta := h.store.LoadBlockMeta(height) - blockExec := sm.NewBlockExecutor(h.stateDB, h.logger, proxyApp, sm.MockMempool{}, sm.MockEvidencePool{}) + blockExec := sm.NewBlockExecutor(h.stateDB, h.logger, proxyApp, mock.Mempool{}, sm.MockEvidencePool{}) blockExec.SetEventBus(h.eventBus) var err error diff --git a/consensus/replay_file.go b/consensus/replay_file.go index d45f9c5a4..5bb73484e 100644 --- a/consensus/replay_file.go +++ b/consensus/replay_file.go @@ -16,6 +16,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" @@ -312,7 +313,7 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo cmn.Exit(fmt.Sprintf("Error on handshake: %v", err)) } - mempool, evpool := sm.MockMempool{}, sm.MockEvidencePool{} + mempool, evpool := mock.Mempool{}, sm.MockEvidencePool{} blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) consensusState := NewConsensusState(csConfig, state.Copy(), blockExec, diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 6ed361905..724dd056b 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -24,6 +24,7 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -277,7 +278,7 @@ const ( ) var ( - mempool = sm.MockMempool{} + mempool = mock.Mempool{} evpool = sm.MockEvidencePool{} sim testSim diff --git a/consensus/wal_generator.go b/consensus/wal_generator.go index c007a21cf..2faff27b5 100644 --- a/consensus/wal_generator.go +++ b/consensus/wal_generator.go @@ -10,12 +10,14 @@ import ( "time" "github.com/pkg/errors" + "github.com/tendermint/tendermint/abci/example/kvstore" bc "github.com/tendermint/tendermint/blockchain" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -67,7 +69,7 @@ func WALGenerateNBlocks(t *testing.T, wr io.Writer, numBlocks int) (err error) { return errors.Wrap(err, "failed to start event bus") } defer eventBus.Stop() - mempool := sm.MockMempool{} + mempool := mock.Mempool{} evpool := sm.MockEvidencePool{} blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool) consensusState := NewConsensusState(config.Consensus, state.Copy(), blockExec, blockStore, mempool, evpool) diff --git a/mempool/clist_mempool.go b/mempool/clist_mempool.go new file mode 100644 index 000000000..2a4bacbbb --- /dev/null +++ b/mempool/clist_mempool.go @@ -0,0 +1,714 @@ +package mempool + +import ( + "bytes" + "container/list" + "crypto/sha256" + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/pkg/errors" + + abci "github.com/tendermint/tendermint/abci/types" + cfg "github.com/tendermint/tendermint/config" + auto "github.com/tendermint/tendermint/libs/autofile" + "github.com/tendermint/tendermint/libs/clist" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/proxy" + "github.com/tendermint/tendermint/types" +) + +//-------------------------------------------------------------------------------- + +// CListMempool is an ordered in-memory pool for transactions before they are +// proposed in a consensus round. Transaction validity is checked using the +// CheckTx abci message before the transaction is added to the pool. The +// mempool uses a concurrent list structure for storing transactions that can +// be efficiently accessed by multiple concurrent readers. +type CListMempool struct { + config *cfg.MempoolConfig + + proxyMtx sync.Mutex + proxyAppConn proxy.AppConnMempool + txs *clist.CList // concurrent linked-list of good txs + preCheck PreCheckFunc + postCheck PostCheckFunc + + // Track whether we're rechecking txs. + // These are not protected by a mutex and are expected to be mutated + // in serial (ie. by abci responses which are called in serial). + recheckCursor *clist.CElement // next expected response + recheckEnd *clist.CElement // re-checking stops here + + // notify listeners (ie. consensus) when txs are available + notifiedTxsAvailable bool + txsAvailable chan struct{} // fires once for each height, when the mempool is not empty + + // Map for quick access to txs to record sender in CheckTx. + // txsMap: txKey -> CElement + txsMap sync.Map + + // Atomic integers + height int64 // the last block Update()'d to + rechecking int32 // for re-checking filtered txs on Update() + txsBytes int64 // total size of mempool, in bytes + + // Keep a cache of already-seen txs. + // This reduces the pressure on the proxyApp. + cache txCache + + // A log of mempool txs + wal *auto.AutoFile + + logger log.Logger + + metrics *Metrics +} + +var _ Mempool = &CListMempool{} + +// CListMempoolOption sets an optional parameter on the mempool. +type CListMempoolOption func(*CListMempool) + +// NewCListMempool returns a new mempool with the given configuration and connection to an application. +func NewCListMempool( + config *cfg.MempoolConfig, + proxyAppConn proxy.AppConnMempool, + height int64, + options ...CListMempoolOption, +) *CListMempool { + mempool := &CListMempool{ + config: config, + proxyAppConn: proxyAppConn, + txs: clist.New(), + height: height, + rechecking: 0, + recheckCursor: nil, + recheckEnd: nil, + logger: log.NewNopLogger(), + metrics: NopMetrics(), + } + if config.CacheSize > 0 { + mempool.cache = newMapTxCache(config.CacheSize) + } else { + mempool.cache = nopTxCache{} + } + proxyAppConn.SetResponseCallback(mempool.globalCb) + for _, option := range options { + option(mempool) + } + return mempool +} + +// NOTE: not thread safe - should only be called once, on startup +func (mem *CListMempool) EnableTxsAvailable() { + mem.txsAvailable = make(chan struct{}, 1) +} + +// SetLogger sets the Logger. +func (mem *CListMempool) SetLogger(l log.Logger) { + mem.logger = l +} + +// WithPreCheck sets a filter for the mempool to reject a tx if f(tx) returns +// false. This is ran before CheckTx. +func WithPreCheck(f PreCheckFunc) CListMempoolOption { + return func(mem *CListMempool) { mem.preCheck = f } +} + +// WithPostCheck sets a filter for the mempool to reject a tx if f(tx) returns +// false. This is ran after CheckTx. +func WithPostCheck(f PostCheckFunc) CListMempoolOption { + return func(mem *CListMempool) { mem.postCheck = f } +} + +// WithMetrics sets the metrics. +func WithMetrics(metrics *Metrics) CListMempoolOption { + return func(mem *CListMempool) { mem.metrics = metrics } +} + +// *panics* if can't create directory or open file. +// *not thread safe* +func (mem *CListMempool) InitWAL() { + walDir := mem.config.WalDir() + err := cmn.EnsureDir(walDir, 0700) + if err != nil { + panic(errors.Wrap(err, "Error ensuring WAL dir")) + } + af, err := auto.OpenAutoFile(walDir + "/wal") + if err != nil { + panic(errors.Wrap(err, "Error opening WAL file")) + } + mem.wal = af +} + +func (mem *CListMempool) CloseWAL() { + mem.proxyMtx.Lock() + defer mem.proxyMtx.Unlock() + + if err := mem.wal.Close(); err != nil { + mem.logger.Error("Error closing WAL", "err", err) + } + mem.wal = nil +} + +func (mem *CListMempool) Lock() { + mem.proxyMtx.Lock() +} + +func (mem *CListMempool) Unlock() { + mem.proxyMtx.Unlock() +} + +func (mem *CListMempool) Size() int { + return mem.txs.Len() +} + +func (mem *CListMempool) TxsBytes() int64 { + return atomic.LoadInt64(&mem.txsBytes) +} + +func (mem *CListMempool) FlushAppConn() error { + return mem.proxyAppConn.FlushSync() +} + +func (mem *CListMempool) Flush() { + mem.proxyMtx.Lock() + defer mem.proxyMtx.Unlock() + + mem.cache.Reset() + + for e := mem.txs.Front(); e != nil; e = e.Next() { + mem.txs.Remove(e) + e.DetachPrev() + } + + mem.txsMap = sync.Map{} + _ = atomic.SwapInt64(&mem.txsBytes, 0) +} + +// TxsFront returns the first transaction in the ordered list for peer +// goroutines to call .NextWait() on. +// FIXME: leaking implementation details! +func (mem *CListMempool) TxsFront() *clist.CElement { + return mem.txs.Front() +} + +// TxsWaitChan returns a channel to wait on transactions. It will be closed +// once the mempool is not empty (ie. the internal `mem.txs` has at least one +// element) +func (mem *CListMempool) TxsWaitChan() <-chan struct{} { + return mem.txs.WaitChan() +} + +// It blocks if we're waiting on Update() or Reap(). +// cb: A callback from the CheckTx command. +// It gets called from another goroutine. +// CONTRACT: Either cb will get called, or err returned. +func (mem *CListMempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) { + return mem.CheckTxWithInfo(tx, cb, TxInfo{SenderID: UnknownPeerID}) +} + +func (mem *CListMempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo TxInfo) (err error) { + mem.proxyMtx.Lock() + // use defer to unlock mutex because application (*local client*) might panic + defer mem.proxyMtx.Unlock() + + var ( + memSize = mem.Size() + txsBytes = mem.TxsBytes() + ) + if memSize >= mem.config.Size || + int64(len(tx))+txsBytes > mem.config.MaxTxsBytes { + return ErrMempoolIsFull{ + memSize, mem.config.Size, + txsBytes, mem.config.MaxTxsBytes} + } + + // The size of the corresponding amino-encoded TxMessage + // can't be larger than the maxMsgSize, otherwise we can't + // relay it to peers. + if len(tx) > maxTxSize { + return ErrTxTooLarge + } + + if mem.preCheck != nil { + if err := mem.preCheck(tx); err != nil { + return ErrPreCheck{err} + } + } + + // CACHE + if !mem.cache.Push(tx) { + // Record a new sender for a tx we've already seen. + // Note it's possible a tx is still in the cache but no longer in the mempool + // (eg. after committing a block, txs are removed from mempool but not cache), + // so we only record the sender for txs still in the mempool. + if e, ok := mem.txsMap.Load(txKey(tx)); ok { + memTx := e.(*clist.CElement).Value.(*mempoolTx) + if _, loaded := memTx.senders.LoadOrStore(txInfo.SenderID, true); loaded { + // TODO: consider punishing peer for dups, + // its non-trivial since invalid txs can become valid, + // but they can spam the same tx with little cost to them atm. + } + } + + return ErrTxInCache + } + // END CACHE + + // WAL + if mem.wal != nil { + // TODO: Notify administrators when WAL fails + _, err := mem.wal.Write([]byte(tx)) + if err != nil { + mem.logger.Error("Error writing to WAL", "err", err) + } + _, err = mem.wal.Write([]byte("\n")) + if err != nil { + mem.logger.Error("Error writing to WAL", "err", err) + } + } + // END WAL + + // NOTE: proxyAppConn may error if tx buffer is full + if err = mem.proxyAppConn.Error(); err != nil { + return err + } + + reqRes := mem.proxyAppConn.CheckTxAsync(tx) + reqRes.SetCallback(mem.reqResCb(tx, txInfo.SenderID, cb)) + + return nil +} + +// Global callback that will be called after every ABCI response. +// Having a single global callback avoids needing to set a callback for each request. +// However, processing the checkTx response requires the peerID (so we can track which txs we heard from who), +// and peerID is not included in the ABCI request, so we have to set request-specific callbacks that +// include this information. If we're not in the midst of a recheck, this function will just return, +// so the request specific callback can do the work. +// When rechecking, we don't need the peerID, so the recheck callback happens here. +func (mem *CListMempool) globalCb(req *abci.Request, res *abci.Response) { + if mem.recheckCursor == nil { + return + } + + mem.metrics.RecheckTimes.Add(1) + mem.resCbRecheck(req, res) + + // update metrics + mem.metrics.Size.Set(float64(mem.Size())) +} + +// Request specific callback that should be set on individual reqRes objects +// to incorporate local information when processing the response. +// This allows us to track the peer that sent us this tx, so we can avoid sending it back to them. +// NOTE: alternatively, we could include this information in the ABCI request itself. +// +// External callers of CheckTx, like the RPC, can also pass an externalCb through here that is called +// when all other response processing is complete. +// +// Used in CheckTxWithInfo to record PeerID who sent us the tx. +func (mem *CListMempool) reqResCb(tx []byte, peerID uint16, externalCb func(*abci.Response)) func(res *abci.Response) { + return func(res *abci.Response) { + if mem.recheckCursor != nil { + // this should never happen + panic("recheck cursor is not nil in reqResCb") + } + + mem.resCbFirstTime(tx, peerID, res) + + // update metrics + mem.metrics.Size.Set(float64(mem.Size())) + + // passed in by the caller of CheckTx, eg. the RPC + if externalCb != nil { + externalCb(res) + } + } +} + +// Called from: +// - resCbFirstTime (lock not held) if tx is valid +func (mem *CListMempool) addTx(memTx *mempoolTx) { + e := mem.txs.PushBack(memTx) + mem.txsMap.Store(txKey(memTx.tx), e) + atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx))) + mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx))) +} + +// Called from: +// - Update (lock held) if tx was committed +// - resCbRecheck (lock not held) if tx was invalidated +func (mem *CListMempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) { + mem.txs.Remove(elem) + elem.DetachPrev() + mem.txsMap.Delete(txKey(tx)) + atomic.AddInt64(&mem.txsBytes, int64(-len(tx))) + + if removeFromCache { + mem.cache.Remove(tx) + } +} + +// callback, which is called after the app checked the tx for the first time. +// +// The case where the app checks the tx for the second and subsequent times is +// handled by the resCbRecheck callback. +func (mem *CListMempool) resCbFirstTime(tx []byte, peerID uint16, res *abci.Response) { + switch r := res.Value.(type) { + case *abci.Response_CheckTx: + var postCheckErr error + if mem.postCheck != nil { + postCheckErr = mem.postCheck(tx, r.CheckTx) + } + if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil { + memTx := &mempoolTx{ + height: mem.height, + gasWanted: r.CheckTx.GasWanted, + tx: tx, + } + memTx.senders.Store(peerID, true) + mem.addTx(memTx) + mem.logger.Info("Added good transaction", + "tx", txID(tx), + "res", r, + "height", memTx.height, + "total", mem.Size(), + ) + mem.notifyTxsAvailable() + } else { + // ignore bad transaction + mem.logger.Info("Rejected bad transaction", "tx", txID(tx), "res", r, "err", postCheckErr) + mem.metrics.FailedTxs.Add(1) + // remove from cache (it might be good later) + mem.cache.Remove(tx) + } + default: + // ignore other messages + } +} + +// callback, which is called after the app rechecked the tx. +// +// The case where the app checks the tx for the first time is handled by the +// resCbFirstTime callback. +func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) { + switch r := res.Value.(type) { + case *abci.Response_CheckTx: + tx := req.GetCheckTx().Tx + memTx := mem.recheckCursor.Value.(*mempoolTx) + if !bytes.Equal(tx, memTx.tx) { + panic(fmt.Sprintf( + "Unexpected tx response from proxy during recheck\nExpected %X, got %X", + memTx.tx, + tx)) + } + var postCheckErr error + if mem.postCheck != nil { + postCheckErr = mem.postCheck(tx, r.CheckTx) + } + if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil { + // Good, nothing to do. + } else { + // Tx became invalidated due to newly committed block. + mem.logger.Info("Tx is no longer valid", "tx", txID(tx), "res", r, "err", postCheckErr) + // NOTE: we remove tx from the cache because it might be good later + mem.removeTx(tx, mem.recheckCursor, true) + } + if mem.recheckCursor == mem.recheckEnd { + mem.recheckCursor = nil + } else { + mem.recheckCursor = mem.recheckCursor.Next() + } + if mem.recheckCursor == nil { + // Done! + atomic.StoreInt32(&mem.rechecking, 0) + mem.logger.Info("Done rechecking txs") + + // incase the recheck removed all txs + if mem.Size() > 0 { + mem.notifyTxsAvailable() + } + } + default: + // ignore other messages + } +} + +func (mem *CListMempool) TxsAvailable() <-chan struct{} { + return mem.txsAvailable +} + +func (mem *CListMempool) notifyTxsAvailable() { + if mem.Size() == 0 { + panic("notified txs available but mempool is empty!") + } + if mem.txsAvailable != nil && !mem.notifiedTxsAvailable { + // channel cap is 1, so this will send once + mem.notifiedTxsAvailable = true + select { + case mem.txsAvailable <- struct{}{}: + default: + } + } +} + +func (mem *CListMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs { + mem.proxyMtx.Lock() + defer mem.proxyMtx.Unlock() + + for atomic.LoadInt32(&mem.rechecking) > 0 { + // TODO: Something better? + time.Sleep(time.Millisecond * 10) + } + + var totalBytes int64 + var totalGas int64 + // TODO: we will get a performance boost if we have a good estimate of avg + // size per tx, and set the initial capacity based off of that. + // txs := make([]types.Tx, 0, cmn.MinInt(mem.txs.Len(), max/mem.avgTxSize)) + txs := make([]types.Tx, 0, mem.txs.Len()) + for e := mem.txs.Front(); e != nil; e = e.Next() { + memTx := e.Value.(*mempoolTx) + // Check total size requirement + aminoOverhead := types.ComputeAminoOverhead(memTx.tx, 1) + if maxBytes > -1 && totalBytes+int64(len(memTx.tx))+aminoOverhead > maxBytes { + return txs + } + totalBytes += int64(len(memTx.tx)) + aminoOverhead + // Check total gas requirement. + // If maxGas is negative, skip this check. + // Since newTotalGas < masGas, which + // must be non-negative, it follows that this won't overflow. + newTotalGas := totalGas + memTx.gasWanted + if maxGas > -1 && newTotalGas > maxGas { + return txs + } + totalGas = newTotalGas + txs = append(txs, memTx.tx) + } + return txs +} + +func (mem *CListMempool) ReapMaxTxs(max int) types.Txs { + mem.proxyMtx.Lock() + defer mem.proxyMtx.Unlock() + + if max < 0 { + max = mem.txs.Len() + } + + for atomic.LoadInt32(&mem.rechecking) > 0 { + // TODO: Something better? + time.Sleep(time.Millisecond * 10) + } + + txs := make([]types.Tx, 0, cmn.MinInt(mem.txs.Len(), max)) + for e := mem.txs.Front(); e != nil && len(txs) <= max; e = e.Next() { + memTx := e.Value.(*mempoolTx) + txs = append(txs, memTx.tx) + } + return txs +} + +func (mem *CListMempool) Update( + height int64, + txs types.Txs, + preCheck PreCheckFunc, + postCheck PostCheckFunc, +) error { + // Set height + mem.height = height + mem.notifiedTxsAvailable = false + + if preCheck != nil { + mem.preCheck = preCheck + } + if postCheck != nil { + mem.postCheck = postCheck + } + + // Add committed transactions to cache (if missing). + for _, tx := range txs { + _ = mem.cache.Push(tx) + } + + // Remove committed transactions. + txsLeft := mem.removeTxs(txs) + + // Either recheck non-committed txs to see if they became invalid + // or just notify there're some txs left. + if len(txsLeft) > 0 { + if mem.config.Recheck { + mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height) + mem.recheckTxs(txsLeft) + // At this point, mem.txs are being rechecked. + // mem.recheckCursor re-scans mem.txs and possibly removes some txs. + // Before mem.Reap(), we should wait for mem.recheckCursor to be nil. + } else { + mem.notifyTxsAvailable() + } + } + + // Update metrics + mem.metrics.Size.Set(float64(mem.Size())) + + return nil +} + +func (mem *CListMempool) removeTxs(txs types.Txs) []types.Tx { + // Build a map for faster lookups. + txsMap := make(map[string]struct{}, len(txs)) + for _, tx := range txs { + txsMap[string(tx)] = struct{}{} + } + + txsLeft := make([]types.Tx, 0, mem.txs.Len()) + for e := mem.txs.Front(); e != nil; e = e.Next() { + memTx := e.Value.(*mempoolTx) + // Remove the tx if it's already in a block. + if _, ok := txsMap[string(memTx.tx)]; ok { + // NOTE: we don't remove committed txs from the cache. + mem.removeTx(memTx.tx, e, false) + + continue + } + txsLeft = append(txsLeft, memTx.tx) + } + return txsLeft +} + +// NOTE: pass in txs because mem.txs can mutate concurrently. +func (mem *CListMempool) recheckTxs(txs []types.Tx) { + if len(txs) == 0 { + return + } + atomic.StoreInt32(&mem.rechecking, 1) + mem.recheckCursor = mem.txs.Front() + mem.recheckEnd = mem.txs.Back() + + // Push txs to proxyAppConn + // NOTE: globalCb may be called concurrently. + for _, tx := range txs { + mem.proxyAppConn.CheckTxAsync(tx) + } + mem.proxyAppConn.FlushAsync() +} + +//-------------------------------------------------------------------------------- + +// mempoolTx is a transaction that successfully ran +type mempoolTx struct { + height int64 // height that this tx had been validated in + gasWanted int64 // amount of gas this tx states it will require + tx types.Tx // + + // ids of peers who've sent us this tx (as a map for quick lookups). + // senders: PeerID -> bool + senders sync.Map +} + +// Height returns the height for this transaction +func (memTx *mempoolTx) Height() int64 { + return atomic.LoadInt64(&memTx.height) +} + +//-------------------------------------------------------------------------------- + +type txCache interface { + Reset() + Push(tx types.Tx) bool + Remove(tx types.Tx) +} + +// mapTxCache maintains a LRU cache of transactions. This only stores the hash +// of the tx, due to memory concerns. +type mapTxCache struct { + mtx sync.Mutex + size int + map_ map[[sha256.Size]byte]*list.Element + list *list.List +} + +var _ txCache = (*mapTxCache)(nil) + +// newMapTxCache returns a new mapTxCache. +func newMapTxCache(cacheSize int) *mapTxCache { + return &mapTxCache{ + size: cacheSize, + map_: make(map[[sha256.Size]byte]*list.Element, cacheSize), + list: list.New(), + } +} + +// Reset resets the cache to an empty state. +func (cache *mapTxCache) Reset() { + cache.mtx.Lock() + cache.map_ = make(map[[sha256.Size]byte]*list.Element, cache.size) + cache.list.Init() + cache.mtx.Unlock() +} + +// Push adds the given tx to the cache and returns true. It returns +// false if tx is already in the cache. +func (cache *mapTxCache) Push(tx types.Tx) bool { + cache.mtx.Lock() + defer cache.mtx.Unlock() + + // Use the tx hash in the cache + txHash := txKey(tx) + if moved, exists := cache.map_[txHash]; exists { + cache.list.MoveToBack(moved) + return false + } + + if cache.list.Len() >= cache.size { + popped := cache.list.Front() + poppedTxHash := popped.Value.([sha256.Size]byte) + delete(cache.map_, poppedTxHash) + if popped != nil { + cache.list.Remove(popped) + } + } + e := cache.list.PushBack(txHash) + cache.map_[txHash] = e + return true +} + +// Remove removes the given tx from the cache. +func (cache *mapTxCache) Remove(tx types.Tx) { + cache.mtx.Lock() + txHash := txKey(tx) + popped := cache.map_[txHash] + delete(cache.map_, txHash) + if popped != nil { + cache.list.Remove(popped) + } + + cache.mtx.Unlock() +} + +type nopTxCache struct{} + +var _ txCache = (*nopTxCache)(nil) + +func (nopTxCache) Reset() {} +func (nopTxCache) Push(types.Tx) bool { return true } +func (nopTxCache) Remove(types.Tx) {} + +//-------------------------------------------------------------------------------- + +// txKey is the fixed length array sha256 hash used as the key in maps. +func txKey(tx types.Tx) [sha256.Size]byte { + return sha256.Sum256(tx) +} + +// txID is the hex encoded hash of the bytes as a types.Tx. +func txID(tx []byte) string { + return fmt.Sprintf("%X", types.Tx(tx).Hash()) +} diff --git a/mempool/mempool_test.go b/mempool/clist_mempool_test.go similarity index 96% rename from mempool/mempool_test.go rename to mempool/clist_mempool_test.go index d5f25396d..69d4eb68c 100644 --- a/mempool/mempool_test.go +++ b/mempool/clist_mempool_test.go @@ -32,18 +32,18 @@ import ( // test. type cleanupFunc func() -func newMempoolWithApp(cc proxy.ClientCreator) (*Mempool, cleanupFunc) { +func newMempoolWithApp(cc proxy.ClientCreator) (*CListMempool, cleanupFunc) { return newMempoolWithAppAndConfig(cc, cfg.ResetTestRoot("mempool_test")) } -func newMempoolWithAppAndConfig(cc proxy.ClientCreator, config *cfg.Config) (*Mempool, cleanupFunc) { +func newMempoolWithAppAndConfig(cc proxy.ClientCreator, config *cfg.Config) (*CListMempool, cleanupFunc) { appConnMem, _ := cc.NewABCIClient() appConnMem.SetLogger(log.TestingLogger().With("module", "abci-client", "connection", "mempool")) err := appConnMem.Start() if err != nil { panic(err) } - mempool := NewMempool(config.Mempool, appConnMem, 0) + mempool := NewCListMempool(config.Mempool, appConnMem, 0) mempool.SetLogger(log.TestingLogger()) return mempool, func() { os.RemoveAll(config.RootDir) } } @@ -66,9 +66,9 @@ func ensureFire(t *testing.T, ch <-chan struct{}, timeoutMS int) { } } -func checkTxs(t *testing.T, mempool *Mempool, count int, peerID uint16) types.Txs { +func checkTxs(t *testing.T, mempool Mempool, count int, peerID uint16) types.Txs { txs := make(types.Txs, count) - txInfo := TxInfo{PeerID: peerID} + txInfo := TxInfo{SenderID: peerID} for i := 0; i < count; i++ { txBytes := make([]byte, 20) txs[i] = txBytes @@ -348,7 +348,6 @@ func TestMempoolCloseWAL(t *testing.T) { // 1. Create the temporary directory for mempool and WAL testing. rootDir, err := ioutil.TempDir("", "mempool-test") require.Nil(t, err, "expecting successful tmpdir creation") - defer os.RemoveAll(rootDir) // 2. Ensure that it doesn't contain any elements -- Sanity check m1, err := filepath.Glob(filepath.Join(rootDir, "*")) @@ -356,13 +355,13 @@ func TestMempoolCloseWAL(t *testing.T) { require.Equal(t, 0, len(m1), "no matches yet") // 3. Create the mempool - wcfg := cfg.DefaultMempoolConfig() - wcfg.RootDir = rootDir - defer os.RemoveAll(wcfg.RootDir) + wcfg := cfg.DefaultConfig() + wcfg.Mempool.RootDir = rootDir app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) - appConnMem, _ := cc.NewABCIClient() - mempool := NewMempool(wcfg, appConnMem, 10) + mempool, cleanup := newMempoolWithAppAndConfig(cc, wcfg) + defer cleanup() + mempool.height = 10 mempool.InitWAL() // 4. Ensure that the directory contains the WAL file @@ -542,7 +541,7 @@ func TestMempoolRemoteAppConcurrency(t *testing.T) { tx := txs[int(txNum)] // this will err with ErrTxInCache many times ... - mempool.CheckTxWithInfo(tx, nil, TxInfo{PeerID: uint16(peerID)}) + mempool.CheckTxWithInfo(tx, nil, TxInfo{SenderID: uint16(peerID)}) } err := mempool.FlushAppConn() require.NoError(t, err) diff --git a/mempool/doc.go b/mempool/doc.go new file mode 100644 index 000000000..ddd47aa2d --- /dev/null +++ b/mempool/doc.go @@ -0,0 +1,24 @@ +// The mempool pushes new txs onto the proxyAppConn. +// It gets a stream of (req, res) tuples from the proxy. +// The mempool stores good txs in a concurrent linked-list. + +// Multiple concurrent go-routines can traverse this linked-list +// safely by calling .NextWait() on each element. + +// So we have several go-routines: +// 1. Consensus calling Update() and Reap() synchronously +// 2. Many mempool reactor's peer routines calling CheckTx() +// 3. Many mempool reactor's peer routines traversing the txs linked list +// 4. Another goroutine calling GarbageCollectTxs() periodically + +// To manage these goroutines, there are three methods of locking. +// 1. Mutations to the linked-list is protected by an internal mtx (CList is goroutine-safe) +// 2. Mutations to the linked-list elements are atomic +// 3. CheckTx() calls can be paused upon Update() and Reap(), protected by .proxyMtx + +// Garbage collection of old elements from mempool.txs is handlde via +// the DetachPrev() call, which makes old elements not reachable by +// peer broadcastTxRoutine() automatically garbage collected. + +// TODO: Better handle abci client errors. (make it automatically handle connection errors) +package mempool diff --git a/mempool/errors.go b/mempool/errors.go new file mode 100644 index 000000000..ac2a9b3c2 --- /dev/null +++ b/mempool/errors.go @@ -0,0 +1,46 @@ +package mempool + +import ( + "fmt" + + "github.com/pkg/errors" +) + +var ( + // ErrTxInCache is returned to the client if we saw tx earlier + ErrTxInCache = errors.New("Tx already exists in cache") + + // ErrTxTooLarge means the tx is too big to be sent in a message to other peers + ErrTxTooLarge = fmt.Errorf("Tx too large. Max size is %d", maxTxSize) +) + +// ErrMempoolIsFull means Tendermint & an application can't handle that much load +type ErrMempoolIsFull struct { + numTxs int + maxTxs int + + txsBytes int64 + maxTxsBytes int64 +} + +func (e ErrMempoolIsFull) Error() string { + return fmt.Sprintf( + "mempool is full: number of txs %d (max: %d), total txs bytes %d (max: %d)", + e.numTxs, e.maxTxs, + e.txsBytes, e.maxTxsBytes) +} + +// ErrPreCheck is returned when tx is too big +type ErrPreCheck struct { + Reason error +} + +func (e ErrPreCheck) Error() string { + return e.Reason.Error() +} + +// IsPreCheckError returns true if err is due to pre check failure. +func IsPreCheckError(err error) bool { + _, ok := err.(ErrPreCheck) + return ok +} diff --git a/mempool/mempool.go b/mempool/mempool.go index a5b14466a..1c1873d47 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -1,26 +1,82 @@ package mempool import ( - "bytes" - "container/list" - "crypto/sha256" "fmt" - "sync" - "sync/atomic" - "time" - - "github.com/pkg/errors" abci "github.com/tendermint/tendermint/abci/types" - cfg "github.com/tendermint/tendermint/config" - auto "github.com/tendermint/tendermint/libs/autofile" - "github.com/tendermint/tendermint/libs/clist" - cmn "github.com/tendermint/tendermint/libs/common" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" ) +// Mempool defines the mempool interface. +// +// Updates to the mempool need to be synchronized with committing a block so +// apps can reset their transient state on Commit. +type Mempool interface { + // CheckTx executes a new transaction against the application to determine + // its validity and whether it should be added to the mempool. + CheckTx(tx types.Tx, callback func(*abci.Response)) error + + // CheckTxWithInfo performs the same operation as CheckTx, but with extra + // meta data about the tx. + // Currently this metadata is the peer who sent it, used to prevent the tx + // from being gossiped back to them. + CheckTxWithInfo(tx types.Tx, callback func(*abci.Response), txInfo TxInfo) error + + // ReapMaxBytesMaxGas reaps transactions from the mempool up to maxBytes + // bytes total with the condition that the total gasWanted must be less than + // maxGas. + // If both maxes are negative, there is no cap on the size of all returned + // transactions (~ all available transactions). + ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs + + // ReapMaxTxs reaps up to max transactions from the mempool. + // If max is negative, there is no cap on the size of all returned + // transactions (~ all available transactions). + ReapMaxTxs(max int) types.Txs + + // Lock locks the mempool. The consensus must be able to hold lock to safely update. + Lock() + + // Unlock unlocks the mempool. + Unlock() + + // Update informs the mempool that the given txs were committed and can be discarded. + // NOTE: this should be called *after* block is committed by consensus. + // NOTE: unsafe; Lock/Unlock must be managed by caller + Update(blockHeight int64, blockTxs types.Txs, newPreFn PreCheckFunc, newPostFn PostCheckFunc) error + + // FlushAppConn flushes the mempool connection to ensure async reqResCb calls are + // done. E.g. from CheckTx. + FlushAppConn() error + + // Flush removes all transactions from the mempool and cache + Flush() + + // TxsAvailable returns a channel which fires once for every height, + // and only when transactions are available in the mempool. + // NOTE: the returned channel may be nil if EnableTxsAvailable was not called. + TxsAvailable() <-chan struct{} + + // EnableTxsAvailable initializes the TxsAvailable channel, ensuring it will + // trigger once every height when transactions are available. + EnableTxsAvailable() + + // Size returns the number of transactions in the mempool. + Size() int + + // TxsBytes returns the total size of all txs in the mempool. + TxsBytes() int64 + + // InitWAL creates a directory for the WAL file and opens a file itself. + InitWAL() + + // CloseWAL closes and discards the underlying WAL file. + // Any further writes will not be relayed to disk. + CloseWAL() +} + +//-------------------------------------------------------------------------------- + // PreCheckFunc is an optional filter executed before CheckTx and rejects // transaction if false is returned. An example would be to ensure that a // transaction doesn't exceeded the block size. @@ -36,75 +92,10 @@ type PostCheckFunc func(types.Tx, *abci.ResponseCheckTx) error type TxInfo struct { // We don't use p2p.ID here because it's too big. The gain is to store max 2 // bytes with each tx to identify the sender rather than 20 bytes. - PeerID uint16 + SenderID uint16 } -/* - -The mempool pushes new txs onto the proxyAppConn. -It gets a stream of (req, res) tuples from the proxy. -The mempool stores good txs in a concurrent linked-list. - -Multiple concurrent go-routines can traverse this linked-list -safely by calling .NextWait() on each element. - -So we have several go-routines: -1. Consensus calling Update() and Reap() synchronously -2. Many mempool reactor's peer routines calling CheckTx() -3. Many mempool reactor's peer routines traversing the txs linked list -4. Another goroutine calling GarbageCollectTxs() periodically - -To manage these goroutines, there are three methods of locking. -1. Mutations to the linked-list is protected by an internal mtx (CList is goroutine-safe) -2. Mutations to the linked-list elements are atomic -3. CheckTx() calls can be paused upon Update() and Reap(), protected by .proxyMtx - -Garbage collection of old elements from mempool.txs is handlde via -the DetachPrev() call, which makes old elements not reachable by -peer broadcastTxRoutine() automatically garbage collected. - -TODO: Better handle abci client errors. (make it automatically handle connection errors) - -*/ - -var ( - // ErrTxInCache is returned to the client if we saw tx earlier - ErrTxInCache = errors.New("Tx already exists in cache") - - // ErrTxTooLarge means the tx is too big to be sent in a message to other peers - ErrTxTooLarge = fmt.Errorf("Tx too large. Max size is %d", maxTxSize) -) - -// ErrMempoolIsFull means Tendermint & an application can't handle that much load -type ErrMempoolIsFull struct { - numTxs int - maxTxs int - - txsBytes int64 - maxTxsBytes int64 -} - -func (e ErrMempoolIsFull) Error() string { - return fmt.Sprintf( - "Mempool is full: number of txs %d (max: %d), total txs bytes %d (max: %d)", - e.numTxs, e.maxTxs, - e.txsBytes, e.maxTxsBytes) -} - -// ErrPreCheck is returned when tx is too big -type ErrPreCheck struct { - Reason error -} - -func (e ErrPreCheck) Error() string { - return e.Reason.Error() -} - -// IsPreCheckError returns true if err is due to pre check failure. -func IsPreCheckError(err error) bool { - _, ok := err.(ErrPreCheck) - return ok -} +//-------------------------------------------------------------------------------- // PreCheckAminoMaxBytes checks that the size of the transaction plus the amino // overhead is smaller or equal to the expected maxBytes. @@ -143,718 +134,3 @@ func PostCheckMaxGas(maxGas int64) PostCheckFunc { return nil } } - -// TxID is the hex encoded hash of the bytes as a types.Tx. -func TxID(tx []byte) string { - return fmt.Sprintf("%X", types.Tx(tx).Hash()) -} - -// txKey is the fixed length array sha256 hash used as the key in maps. -func txKey(tx types.Tx) [sha256.Size]byte { - return sha256.Sum256(tx) -} - -// Mempool is an ordered in-memory pool for transactions before they are proposed in a consensus -// round. Transaction validity is checked using the CheckTx abci message before the transaction is -// added to the pool. The Mempool uses a concurrent list structure for storing transactions that -// can be efficiently accessed by multiple concurrent readers. -type Mempool struct { - config *cfg.MempoolConfig - - proxyMtx sync.Mutex - proxyAppConn proxy.AppConnMempool - txs *clist.CList // concurrent linked-list of good txs - preCheck PreCheckFunc - postCheck PostCheckFunc - - // Track whether we're rechecking txs. - // These are not protected by a mutex and are expected to be mutated - // in serial (ie. by abci responses which are called in serial). - recheckCursor *clist.CElement // next expected response - recheckEnd *clist.CElement // re-checking stops here - - // notify listeners (ie. consensus) when txs are available - notifiedTxsAvailable bool - txsAvailable chan struct{} // fires once for each height, when the mempool is not empty - - // Map for quick access to txs to record sender in CheckTx. - // txsMap: txKey -> CElement - txsMap sync.Map - - // Atomic integers - height int64 // the last block Update()'d to - rechecking int32 // for re-checking filtered txs on Update() - txsBytes int64 // total size of mempool, in bytes - - // Keep a cache of already-seen txs. - // This reduces the pressure on the proxyApp. - cache txCache - - // A log of mempool txs - wal *auto.AutoFile - - logger log.Logger - - metrics *Metrics -} - -// MempoolOption sets an optional parameter on the Mempool. -type MempoolOption func(*Mempool) - -// NewMempool returns a new Mempool with the given configuration and connection to an application. -func NewMempool( - config *cfg.MempoolConfig, - proxyAppConn proxy.AppConnMempool, - height int64, - options ...MempoolOption, -) *Mempool { - mempool := &Mempool{ - config: config, - proxyAppConn: proxyAppConn, - txs: clist.New(), - height: height, - rechecking: 0, - recheckCursor: nil, - recheckEnd: nil, - logger: log.NewNopLogger(), - metrics: NopMetrics(), - } - if config.CacheSize > 0 { - mempool.cache = newMapTxCache(config.CacheSize) - } else { - mempool.cache = nopTxCache{} - } - proxyAppConn.SetResponseCallback(mempool.globalCb) - for _, option := range options { - option(mempool) - } - return mempool -} - -// EnableTxsAvailable initializes the TxsAvailable channel, -// ensuring it will trigger once every height when transactions are available. -// NOTE: not thread safe - should only be called once, on startup -func (mem *Mempool) EnableTxsAvailable() { - mem.txsAvailable = make(chan struct{}, 1) -} - -// SetLogger sets the Logger. -func (mem *Mempool) SetLogger(l log.Logger) { - mem.logger = l -} - -// WithPreCheck sets a filter for the mempool to reject a tx if f(tx) returns -// false. This is ran before CheckTx. -func WithPreCheck(f PreCheckFunc) MempoolOption { - return func(mem *Mempool) { mem.preCheck = f } -} - -// WithPostCheck sets a filter for the mempool to reject a tx if f(tx) returns -// false. This is ran after CheckTx. -func WithPostCheck(f PostCheckFunc) MempoolOption { - return func(mem *Mempool) { mem.postCheck = f } -} - -// WithMetrics sets the metrics. -func WithMetrics(metrics *Metrics) MempoolOption { - return func(mem *Mempool) { mem.metrics = metrics } -} - -// InitWAL creates a directory for the WAL file and opens a file itself. -// -// *panics* if can't create directory or open file. -// *not thread safe* -func (mem *Mempool) InitWAL() { - walDir := mem.config.WalDir() - err := cmn.EnsureDir(walDir, 0700) - if err != nil { - panic(errors.Wrap(err, "Error ensuring Mempool WAL dir")) - } - af, err := auto.OpenAutoFile(walDir + "/wal") - if err != nil { - panic(errors.Wrap(err, "Error opening Mempool WAL file")) - } - mem.wal = af -} - -// CloseWAL closes and discards the underlying WAL file. -// Any further writes will not be relayed to disk. -func (mem *Mempool) CloseWAL() { - mem.proxyMtx.Lock() - defer mem.proxyMtx.Unlock() - - if err := mem.wal.Close(); err != nil { - mem.logger.Error("Error closing WAL", "err", err) - } - mem.wal = nil -} - -// Lock locks the mempool. The consensus must be able to hold lock to safely update. -func (mem *Mempool) Lock() { - mem.proxyMtx.Lock() -} - -// Unlock unlocks the mempool. -func (mem *Mempool) Unlock() { - mem.proxyMtx.Unlock() -} - -// Size returns the number of transactions in the mempool. -func (mem *Mempool) Size() int { - return mem.txs.Len() -} - -// TxsBytes returns the total size of all txs in the mempool. -func (mem *Mempool) TxsBytes() int64 { - return atomic.LoadInt64(&mem.txsBytes) -} - -// FlushAppConn flushes the mempool connection to ensure async reqResCb calls are -// done. E.g. from CheckTx. -func (mem *Mempool) FlushAppConn() error { - return mem.proxyAppConn.FlushSync() -} - -// Flush removes all transactions from the mempool and cache -func (mem *Mempool) Flush() { - mem.proxyMtx.Lock() - defer mem.proxyMtx.Unlock() - - mem.cache.Reset() - - for e := mem.txs.Front(); e != nil; e = e.Next() { - mem.txs.Remove(e) - e.DetachPrev() - } - - mem.txsMap = sync.Map{} - _ = atomic.SwapInt64(&mem.txsBytes, 0) -} - -// TxsFront returns the first transaction in the ordered list for peer -// goroutines to call .NextWait() on. -func (mem *Mempool) TxsFront() *clist.CElement { - return mem.txs.Front() -} - -// TxsWaitChan returns a channel to wait on transactions. It will be closed -// once the mempool is not empty (ie. the internal `mem.txs` has at least one -// element) -func (mem *Mempool) TxsWaitChan() <-chan struct{} { - return mem.txs.WaitChan() -} - -// CheckTx executes a new transaction against the application to determine its validity -// and whether it should be added to the mempool. -// It blocks if we're waiting on Update() or Reap(). -// cb: A callback from the CheckTx command. -// It gets called from another goroutine. -// CONTRACT: Either cb will get called, or err returned. -func (mem *Mempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) { - return mem.CheckTxWithInfo(tx, cb, TxInfo{PeerID: UnknownPeerID}) -} - -// CheckTxWithInfo performs the same operation as CheckTx, but with extra meta data about the tx. -// Currently this metadata is the peer who sent it, -// used to prevent the tx from being gossiped back to them. -func (mem *Mempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo TxInfo) (err error) { - mem.proxyMtx.Lock() - // use defer to unlock mutex because application (*local client*) might panic - defer mem.proxyMtx.Unlock() - - var ( - memSize = mem.Size() - txsBytes = mem.TxsBytes() - ) - if memSize >= mem.config.Size || - int64(len(tx))+txsBytes > mem.config.MaxTxsBytes { - return ErrMempoolIsFull{ - memSize, mem.config.Size, - txsBytes, mem.config.MaxTxsBytes} - } - - // The size of the corresponding amino-encoded TxMessage - // can't be larger than the maxMsgSize, otherwise we can't - // relay it to peers. - if len(tx) > maxTxSize { - return ErrTxTooLarge - } - - if mem.preCheck != nil { - if err := mem.preCheck(tx); err != nil { - return ErrPreCheck{err} - } - } - - // CACHE - if !mem.cache.Push(tx) { - // Record a new sender for a tx we've already seen. - // Note it's possible a tx is still in the cache but no longer in the mempool - // (eg. after committing a block, txs are removed from mempool but not cache), - // so we only record the sender for txs still in the mempool. - if e, ok := mem.txsMap.Load(txKey(tx)); ok { - memTx := e.(*clist.CElement).Value.(*mempoolTx) - if _, loaded := memTx.senders.LoadOrStore(txInfo.PeerID, true); loaded { - // TODO: consider punishing peer for dups, - // its non-trivial since invalid txs can become valid, - // but they can spam the same tx with little cost to them atm. - } - } - - return ErrTxInCache - } - // END CACHE - - // WAL - if mem.wal != nil { - // TODO: Notify administrators when WAL fails - _, err := mem.wal.Write([]byte(tx)) - if err != nil { - mem.logger.Error("Error writing to WAL", "err", err) - } - _, err = mem.wal.Write([]byte("\n")) - if err != nil { - mem.logger.Error("Error writing to WAL", "err", err) - } - } - // END WAL - - // NOTE: proxyAppConn may error if tx buffer is full - if err = mem.proxyAppConn.Error(); err != nil { - return err - } - - reqRes := mem.proxyAppConn.CheckTxAsync(tx) - reqRes.SetCallback(mem.reqResCb(tx, txInfo.PeerID, cb)) - - return nil -} - -// Global callback that will be called after every ABCI response. -// Having a single global callback avoids needing to set a callback for each request. -// However, processing the checkTx response requires the peerID (so we can track which txs we heard from who), -// and peerID is not included in the ABCI request, so we have to set request-specific callbacks that -// include this information. If we're not in the midst of a recheck, this function will just return, -// so the request specific callback can do the work. -// When rechecking, we don't need the peerID, so the recheck callback happens here. -func (mem *Mempool) globalCb(req *abci.Request, res *abci.Response) { - if mem.recheckCursor == nil { - return - } - - mem.metrics.RecheckTimes.Add(1) - mem.resCbRecheck(req, res) - - // update metrics - mem.metrics.Size.Set(float64(mem.Size())) -} - -// Request specific callback that should be set on individual reqRes objects -// to incorporate local information when processing the response. -// This allows us to track the peer that sent us this tx, so we can avoid sending it back to them. -// NOTE: alternatively, we could include this information in the ABCI request itself. -// -// External callers of CheckTx, like the RPC, can also pass an externalCb through here that is called -// when all other response processing is complete. -// -// Used in CheckTxWithInfo to record PeerID who sent us the tx. -func (mem *Mempool) reqResCb(tx []byte, peerID uint16, externalCb func(*abci.Response)) func(res *abci.Response) { - return func(res *abci.Response) { - if mem.recheckCursor != nil { - // this should never happen - panic("recheck cursor is not nil in reqResCb") - } - - mem.resCbFirstTime(tx, peerID, res) - - // update metrics - mem.metrics.Size.Set(float64(mem.Size())) - - // passed in by the caller of CheckTx, eg. the RPC - if externalCb != nil { - externalCb(res) - } - } -} - -// Called from: -// - resCbFirstTime (lock not held) if tx is valid -func (mem *Mempool) addTx(memTx *mempoolTx) { - e := mem.txs.PushBack(memTx) - mem.txsMap.Store(txKey(memTx.tx), e) - atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx))) - mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx))) -} - -// Called from: -// - Update (lock held) if tx was committed -// - resCbRecheck (lock not held) if tx was invalidated -func (mem *Mempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) { - mem.txs.Remove(elem) - elem.DetachPrev() - mem.txsMap.Delete(txKey(tx)) - atomic.AddInt64(&mem.txsBytes, int64(-len(tx))) - - if removeFromCache { - mem.cache.Remove(tx) - } -} - -// callback, which is called after the app checked the tx for the first time. -// -// The case where the app checks the tx for the second and subsequent times is -// handled by the resCbRecheck callback. -func (mem *Mempool) resCbFirstTime(tx []byte, peerID uint16, res *abci.Response) { - switch r := res.Value.(type) { - case *abci.Response_CheckTx: - var postCheckErr error - if mem.postCheck != nil { - postCheckErr = mem.postCheck(tx, r.CheckTx) - } - if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil { - memTx := &mempoolTx{ - height: mem.height, - gasWanted: r.CheckTx.GasWanted, - tx: tx, - } - memTx.senders.Store(peerID, true) - mem.addTx(memTx) - mem.logger.Info("Added good transaction", - "tx", TxID(tx), - "res", r, - "height", memTx.height, - "total", mem.Size(), - ) - mem.notifyTxsAvailable() - } else { - // ignore bad transaction - mem.logger.Info("Rejected bad transaction", "tx", TxID(tx), "res", r, "err", postCheckErr) - mem.metrics.FailedTxs.Add(1) - // remove from cache (it might be good later) - mem.cache.Remove(tx) - } - default: - // ignore other messages - } -} - -// callback, which is called after the app rechecked the tx. -// -// The case where the app checks the tx for the first time is handled by the -// resCbFirstTime callback. -func (mem *Mempool) resCbRecheck(req *abci.Request, res *abci.Response) { - switch r := res.Value.(type) { - case *abci.Response_CheckTx: - tx := req.GetCheckTx().Tx - memTx := mem.recheckCursor.Value.(*mempoolTx) - if !bytes.Equal(tx, memTx.tx) { - panic(fmt.Sprintf( - "Unexpected tx response from proxy during recheck\nExpected %X, got %X", - memTx.tx, - tx)) - } - var postCheckErr error - if mem.postCheck != nil { - postCheckErr = mem.postCheck(tx, r.CheckTx) - } - if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil { - // Good, nothing to do. - } else { - // Tx became invalidated due to newly committed block. - mem.logger.Info("Tx is no longer valid", "tx", TxID(tx), "res", r, "err", postCheckErr) - // NOTE: we remove tx from the cache because it might be good later - mem.removeTx(tx, mem.recheckCursor, true) - } - if mem.recheckCursor == mem.recheckEnd { - mem.recheckCursor = nil - } else { - mem.recheckCursor = mem.recheckCursor.Next() - } - if mem.recheckCursor == nil { - // Done! - atomic.StoreInt32(&mem.rechecking, 0) - mem.logger.Info("Done rechecking txs") - - // incase the recheck removed all txs - if mem.Size() > 0 { - mem.notifyTxsAvailable() - } - } - default: - // ignore other messages - } -} - -// TxsAvailable returns a channel which fires once for every height, -// and only when transactions are available in the mempool. -// NOTE: the returned channel may be nil if EnableTxsAvailable was not called. -func (mem *Mempool) TxsAvailable() <-chan struct{} { - return mem.txsAvailable -} - -func (mem *Mempool) notifyTxsAvailable() { - if mem.Size() == 0 { - panic("notified txs available but mempool is empty!") - } - if mem.txsAvailable != nil && !mem.notifiedTxsAvailable { - // channel cap is 1, so this will send once - mem.notifiedTxsAvailable = true - select { - case mem.txsAvailable <- struct{}{}: - default: - } - } -} - -// ReapMaxBytesMaxGas reaps transactions from the mempool up to maxBytes bytes total -// with the condition that the total gasWanted must be less than maxGas. -// If both maxes are negative, there is no cap on the size of all returned -// transactions (~ all available transactions). -func (mem *Mempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs { - mem.proxyMtx.Lock() - defer mem.proxyMtx.Unlock() - - for atomic.LoadInt32(&mem.rechecking) > 0 { - // TODO: Something better? - time.Sleep(time.Millisecond * 10) - } - - var totalBytes int64 - var totalGas int64 - // TODO: we will get a performance boost if we have a good estimate of avg - // size per tx, and set the initial capacity based off of that. - // txs := make([]types.Tx, 0, cmn.MinInt(mem.txs.Len(), max/mem.avgTxSize)) - txs := make([]types.Tx, 0, mem.txs.Len()) - for e := mem.txs.Front(); e != nil; e = e.Next() { - memTx := e.Value.(*mempoolTx) - // Check total size requirement - aminoOverhead := types.ComputeAminoOverhead(memTx.tx, 1) - if maxBytes > -1 && totalBytes+int64(len(memTx.tx))+aminoOverhead > maxBytes { - return txs - } - totalBytes += int64(len(memTx.tx)) + aminoOverhead - // Check total gas requirement. - // If maxGas is negative, skip this check. - // Since newTotalGas < masGas, which - // must be non-negative, it follows that this won't overflow. - newTotalGas := totalGas + memTx.gasWanted - if maxGas > -1 && newTotalGas > maxGas { - return txs - } - totalGas = newTotalGas - txs = append(txs, memTx.tx) - } - return txs -} - -// ReapMaxTxs reaps up to max transactions from the mempool. -// If max is negative, there is no cap on the size of all returned -// transactions (~ all available transactions). -func (mem *Mempool) ReapMaxTxs(max int) types.Txs { - mem.proxyMtx.Lock() - defer mem.proxyMtx.Unlock() - - if max < 0 { - max = mem.txs.Len() - } - - for atomic.LoadInt32(&mem.rechecking) > 0 { - // TODO: Something better? - time.Sleep(time.Millisecond * 10) - } - - txs := make([]types.Tx, 0, cmn.MinInt(mem.txs.Len(), max)) - for e := mem.txs.Front(); e != nil && len(txs) <= max; e = e.Next() { - memTx := e.Value.(*mempoolTx) - txs = append(txs, memTx.tx) - } - return txs -} - -// Update informs the mempool that the given txs were committed and can be discarded. -// NOTE: this should be called *after* block is committed by consensus. -// NOTE: unsafe; Lock/Unlock must be managed by caller -func (mem *Mempool) Update( - height int64, - txs types.Txs, - preCheck PreCheckFunc, - postCheck PostCheckFunc, -) error { - // Set height - mem.height = height - mem.notifiedTxsAvailable = false - - if preCheck != nil { - mem.preCheck = preCheck - } - if postCheck != nil { - mem.postCheck = postCheck - } - - // Add committed transactions to cache (if missing). - for _, tx := range txs { - _ = mem.cache.Push(tx) - } - - // Remove committed transactions. - txsLeft := mem.removeTxs(txs) - - // Either recheck non-committed txs to see if they became invalid - // or just notify there're some txs left. - if len(txsLeft) > 0 { - if mem.config.Recheck { - mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height) - mem.recheckTxs(txsLeft) - // At this point, mem.txs are being rechecked. - // mem.recheckCursor re-scans mem.txs and possibly removes some txs. - // Before mem.Reap(), we should wait for mem.recheckCursor to be nil. - } else { - mem.notifyTxsAvailable() - } - } - - // Update metrics - mem.metrics.Size.Set(float64(mem.Size())) - - return nil -} - -func (mem *Mempool) removeTxs(txs types.Txs) []types.Tx { - // Build a map for faster lookups. - txsMap := make(map[string]struct{}, len(txs)) - for _, tx := range txs { - txsMap[string(tx)] = struct{}{} - } - - txsLeft := make([]types.Tx, 0, mem.txs.Len()) - for e := mem.txs.Front(); e != nil; e = e.Next() { - memTx := e.Value.(*mempoolTx) - // Remove the tx if it's already in a block. - if _, ok := txsMap[string(memTx.tx)]; ok { - // NOTE: we don't remove committed txs from the cache. - mem.removeTx(memTx.tx, e, false) - - continue - } - txsLeft = append(txsLeft, memTx.tx) - } - return txsLeft -} - -// NOTE: pass in txs because mem.txs can mutate concurrently. -func (mem *Mempool) recheckTxs(txs []types.Tx) { - if len(txs) == 0 { - return - } - atomic.StoreInt32(&mem.rechecking, 1) - mem.recheckCursor = mem.txs.Front() - mem.recheckEnd = mem.txs.Back() - - // Push txs to proxyAppConn - // NOTE: globalCb may be called concurrently. - for _, tx := range txs { - mem.proxyAppConn.CheckTxAsync(tx) - } - mem.proxyAppConn.FlushAsync() -} - -//-------------------------------------------------------------------------------- - -// mempoolTx is a transaction that successfully ran -type mempoolTx struct { - height int64 // height that this tx had been validated in - gasWanted int64 // amount of gas this tx states it will require - tx types.Tx // - - // ids of peers who've sent us this tx (as a map for quick lookups). - // senders: PeerID -> bool - senders sync.Map -} - -// Height returns the height for this transaction -func (memTx *mempoolTx) Height() int64 { - return atomic.LoadInt64(&memTx.height) -} - -//-------------------------------------------------------------------------------- - -type txCache interface { - Reset() - Push(tx types.Tx) bool - Remove(tx types.Tx) -} - -// mapTxCache maintains a LRU cache of transactions. This only stores the hash -// of the tx, due to memory concerns. -type mapTxCache struct { - mtx sync.Mutex - size int - map_ map[[sha256.Size]byte]*list.Element - list *list.List -} - -var _ txCache = (*mapTxCache)(nil) - -// newMapTxCache returns a new mapTxCache. -func newMapTxCache(cacheSize int) *mapTxCache { - return &mapTxCache{ - size: cacheSize, - map_: make(map[[sha256.Size]byte]*list.Element, cacheSize), - list: list.New(), - } -} - -// Reset resets the cache to an empty state. -func (cache *mapTxCache) Reset() { - cache.mtx.Lock() - cache.map_ = make(map[[sha256.Size]byte]*list.Element, cache.size) - cache.list.Init() - cache.mtx.Unlock() -} - -// Push adds the given tx to the cache and returns true. It returns -// false if tx is already in the cache. -func (cache *mapTxCache) Push(tx types.Tx) bool { - cache.mtx.Lock() - defer cache.mtx.Unlock() - - // Use the tx hash in the cache - txHash := txKey(tx) - if moved, exists := cache.map_[txHash]; exists { - cache.list.MoveToBack(moved) - return false - } - - if cache.list.Len() >= cache.size { - popped := cache.list.Front() - poppedTxHash := popped.Value.([sha256.Size]byte) - delete(cache.map_, poppedTxHash) - if popped != nil { - cache.list.Remove(popped) - } - } - e := cache.list.PushBack(txHash) - cache.map_[txHash] = e - return true -} - -// Remove removes the given tx from the cache. -func (cache *mapTxCache) Remove(tx types.Tx) { - cache.mtx.Lock() - txHash := txKey(tx) - popped := cache.map_[txHash] - delete(cache.map_, txHash) - if popped != nil { - cache.list.Remove(popped) - } - - cache.mtx.Unlock() -} - -type nopTxCache struct{} - -var _ txCache = (*nopTxCache)(nil) - -func (nopTxCache) Reset() {} -func (nopTxCache) Push(types.Tx) bool { return true } -func (nopTxCache) Remove(types.Tx) {} diff --git a/mempool/reactor.go b/mempool/reactor.go index e1376b287..65ccd7dfd 100644 --- a/mempool/reactor.go +++ b/mempool/reactor.go @@ -31,13 +31,13 @@ const ( maxActiveIDs = math.MaxUint16 ) -// MempoolReactor handles mempool tx broadcasting amongst peers. +// Reactor handles mempool tx broadcasting amongst peers. // It maintains a map from peer ID to counter, to prevent gossiping txs to the // peers you received it from. -type MempoolReactor struct { +type Reactor struct { p2p.BaseReactor config *cfg.MempoolConfig - Mempool *Mempool + mempool *CListMempool ids *mempoolIDs } @@ -104,25 +104,25 @@ func newMempoolIDs() *mempoolIDs { } } -// NewMempoolReactor returns a new MempoolReactor with the given config and mempool. -func NewMempoolReactor(config *cfg.MempoolConfig, mempool *Mempool) *MempoolReactor { - memR := &MempoolReactor{ +// NewReactor returns a new Reactor with the given config and mempool. +func NewReactor(config *cfg.MempoolConfig, mempool *CListMempool) *Reactor { + memR := &Reactor{ config: config, - Mempool: mempool, + mempool: mempool, ids: newMempoolIDs(), } - memR.BaseReactor = *p2p.NewBaseReactor("MempoolReactor", memR) + memR.BaseReactor = *p2p.NewBaseReactor("Reactor", memR) return memR } -// SetLogger sets the Logger on the reactor and the underlying Mempool. -func (memR *MempoolReactor) SetLogger(l log.Logger) { +// SetLogger sets the Logger on the reactor and the underlying mempool. +func (memR *Reactor) SetLogger(l log.Logger) { memR.Logger = l - memR.Mempool.SetLogger(l) + memR.mempool.SetLogger(l) } // OnStart implements p2p.BaseReactor. -func (memR *MempoolReactor) OnStart() error { +func (memR *Reactor) OnStart() error { if !memR.config.Broadcast { memR.Logger.Info("Tx broadcasting is disabled") } @@ -131,7 +131,7 @@ func (memR *MempoolReactor) OnStart() error { // GetChannels implements Reactor. // It returns the list of channels for this reactor. -func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor { +func (memR *Reactor) GetChannels() []*p2p.ChannelDescriptor { return []*p2p.ChannelDescriptor{ { ID: MempoolChannel, @@ -142,20 +142,20 @@ func (memR *MempoolReactor) GetChannels() []*p2p.ChannelDescriptor { // AddPeer implements Reactor. // It starts a broadcast routine ensuring all txs are forwarded to the given peer. -func (memR *MempoolReactor) AddPeer(peer p2p.Peer) { +func (memR *Reactor) AddPeer(peer p2p.Peer) { memR.ids.ReserveForPeer(peer) go memR.broadcastTxRoutine(peer) } // RemovePeer implements Reactor. -func (memR *MempoolReactor) RemovePeer(peer p2p.Peer, reason interface{}) { +func (memR *Reactor) RemovePeer(peer p2p.Peer, reason interface{}) { memR.ids.Reclaim(peer) // broadcast routine checks if peer is gone and returns } // Receive implements Reactor. // It adds any received transactions to the mempool. -func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { +func (memR *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { msg, err := decodeMsg(msgBytes) if err != nil { memR.Logger.Error("Error decoding message", "src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes) @@ -167,9 +167,9 @@ func (memR *MempoolReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { switch msg := msg.(type) { case *TxMessage: peerID := memR.ids.GetForPeer(src) - err := memR.Mempool.CheckTxWithInfo(msg.Tx, nil, TxInfo{PeerID: peerID}) + err := memR.mempool.CheckTxWithInfo(msg.Tx, nil, TxInfo{SenderID: peerID}) if err != nil { - memR.Logger.Info("Could not check tx", "tx", TxID(msg.Tx), "err", err) + memR.Logger.Info("Could not check tx", "tx", txID(msg.Tx), "err", err) } // broadcasting happens from go routines per peer default: @@ -183,7 +183,7 @@ type PeerState interface { } // Send new mempool txs to peer. -func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) { +func (memR *Reactor) broadcastTxRoutine(peer p2p.Peer) { if !memR.config.Broadcast { return } @@ -200,8 +200,8 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) { // start from the beginning. if next == nil { select { - case <-memR.Mempool.TxsWaitChan(): // Wait until a tx is available - if next = memR.Mempool.TxsFront(); next == nil { + case <-memR.mempool.TxsWaitChan(): // Wait until a tx is available + if next = memR.mempool.TxsFront(); next == nil { continue } case <-peer.Quit(): @@ -255,7 +255,7 @@ func (memR *MempoolReactor) broadcastTxRoutine(peer p2p.Peer) { //----------------------------------------------------------------------------- // Messages -// MempoolMessage is a message sent or received by the MempoolReactor. +// MempoolMessage is a message sent or received by the Reactor. type MempoolMessage interface{} func RegisterMempoolMessages(cdc *amino.Codec) { diff --git a/mempool/reactor_test.go b/mempool/reactor_test.go index c9cf49809..94c0d1900 100644 --- a/mempool/reactor_test.go +++ b/mempool/reactor_test.go @@ -1,7 +1,6 @@ package mempool import ( - "fmt" "net" "sync" "testing" @@ -43,8 +42,8 @@ func mempoolLogger() log.Logger { } // connect N mempool reactors through N switches -func makeAndConnectMempoolReactors(config *cfg.Config, N int) []*MempoolReactor { - reactors := make([]*MempoolReactor, N) +func makeAndConnectReactors(config *cfg.Config, N int) []*Reactor { + reactors := make([]*Reactor, N) logger := mempoolLogger() for i := 0; i < N; i++ { app := kvstore.NewKVStoreApplication() @@ -52,7 +51,7 @@ func makeAndConnectMempoolReactors(config *cfg.Config, N int) []*MempoolReactor mempool, cleanup := newMempoolWithApp(cc) defer cleanup() - reactors[i] = NewMempoolReactor(config.Mempool, mempool) // so we dont start the consensus states + reactors[i] = NewReactor(config.Mempool, mempool) // so we dont start the consensus states reactors[i].SetLogger(logger.With("validator", i)) } @@ -64,13 +63,15 @@ func makeAndConnectMempoolReactors(config *cfg.Config, N int) []*MempoolReactor return reactors } -// wait for all txs on all reactors -func waitForTxs(t *testing.T, txs types.Txs, reactors []*MempoolReactor) { +func waitForTxsOnReactors(t *testing.T, txs types.Txs, reactors []*Reactor) { // wait for the txs in all mempools wg := new(sync.WaitGroup) - for i := 0; i < len(reactors); i++ { + for i, reactor := range reactors { wg.Add(1) - go _waitForTxs(t, wg, txs, i, reactors) + go func(r *Reactor, reactorIndex int) { + defer wg.Done() + waitForTxsOnReactor(t, txs, r, reactorIndex) + }(reactor, i) } done := make(chan struct{}) @@ -87,25 +88,23 @@ func waitForTxs(t *testing.T, txs types.Txs, reactors []*MempoolReactor) { } } -// wait for all txs on a single mempool -func _waitForTxs(t *testing.T, wg *sync.WaitGroup, txs types.Txs, reactorIdx int, reactors []*MempoolReactor) { - - mempool := reactors[reactorIdx].Mempool - for mempool.Size() != len(txs) { +func waitForTxsOnReactor(t *testing.T, txs types.Txs, reactor *Reactor, reactorIndex int) { + mempool := reactor.mempool + for mempool.Size() < len(txs) { time.Sleep(time.Millisecond * 100) } reapedTxs := mempool.ReapMaxTxs(len(txs)) for i, tx := range txs { - assert.Equal(t, tx, reapedTxs[i], fmt.Sprintf("txs at index %d on reactor %d don't match: %v vs %v", i, reactorIdx, tx, reapedTxs[i])) + assert.Equalf(t, tx, reapedTxs[i], + "txs at index %d on reactor %d don't match: %v vs %v", i, reactorIndex, tx, reapedTxs[i]) } - wg.Done() } // ensure no txs on reactor after some timeout -func ensureNoTxs(t *testing.T, reactor *MempoolReactor, timeout time.Duration) { +func ensureNoTxs(t *testing.T, reactor *Reactor, timeout time.Duration) { time.Sleep(timeout) // wait for the txs in all mempools - assert.Zero(t, reactor.Mempool.Size()) + assert.Zero(t, reactor.mempool.Size()) } const ( @@ -116,7 +115,7 @@ const ( func TestReactorBroadcastTxMessage(t *testing.T) { config := cfg.TestConfig() const N = 4 - reactors := makeAndConnectMempoolReactors(config, N) + reactors := makeAndConnectReactors(config, N) defer func() { for _, r := range reactors { r.Stop() @@ -130,14 +129,14 @@ func TestReactorBroadcastTxMessage(t *testing.T) { // send a bunch of txs to the first reactor's mempool // and wait for them all to be received in the others - txs := checkTxs(t, reactors[0].Mempool, NUM_TXS, UnknownPeerID) - waitForTxs(t, txs, reactors) + txs := checkTxs(t, reactors[0].mempool, NUM_TXS, UnknownPeerID) + waitForTxsOnReactors(t, txs, reactors) } func TestReactorNoBroadcastToSender(t *testing.T) { config := cfg.TestConfig() const N = 2 - reactors := makeAndConnectMempoolReactors(config, N) + reactors := makeAndConnectReactors(config, N) defer func() { for _, r := range reactors { r.Stop() @@ -146,7 +145,7 @@ func TestReactorNoBroadcastToSender(t *testing.T) { // send a bunch of txs to the first reactor's mempool, claiming it came from peer // ensure peer gets no txs - checkTxs(t, reactors[0].Mempool, NUM_TXS, 1) + checkTxs(t, reactors[0].mempool, NUM_TXS, 1) ensureNoTxs(t, reactors[1], 100*time.Millisecond) } @@ -157,7 +156,7 @@ func TestBroadcastTxForPeerStopsWhenPeerStops(t *testing.T) { config := cfg.TestConfig() const N = 2 - reactors := makeAndConnectMempoolReactors(config, N) + reactors := makeAndConnectReactors(config, N) defer func() { for _, r := range reactors { r.Stop() @@ -180,7 +179,7 @@ func TestBroadcastTxForPeerStopsWhenReactorStops(t *testing.T) { config := cfg.TestConfig() const N = 2 - reactors := makeAndConnectMempoolReactors(config, N) + reactors := makeAndConnectReactors(config, N) // stop reactors for _, r := range reactors { diff --git a/mock/mempool.go b/mock/mempool.go new file mode 100644 index 000000000..66ad68d30 --- /dev/null +++ b/mock/mempool.go @@ -0,0 +1,45 @@ +package mock + +import ( + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/clist" + mempl "github.com/tendermint/tendermint/mempool" + "github.com/tendermint/tendermint/types" +) + +// Mempool is an empty implementation of a Mempool, useful for testing. +type Mempool struct{} + +var _ mempl.Mempool = Mempool{} + +func (Mempool) Lock() {} +func (Mempool) Unlock() {} +func (Mempool) Size() int { return 0 } +func (Mempool) CheckTx(_ types.Tx, _ func(*abci.Response)) error { + return nil +} +func (Mempool) CheckTxWithInfo(_ types.Tx, _ func(*abci.Response), + _ mempl.TxInfo) error { + return nil +} +func (Mempool) ReapMaxBytesMaxGas(_, _ int64) types.Txs { return types.Txs{} } +func (Mempool) ReapMaxTxs(n int) types.Txs { return types.Txs{} } +func (Mempool) Update( + _ int64, + _ types.Txs, + _ mempl.PreCheckFunc, + _ mempl.PostCheckFunc, +) error { + return nil +} +func (Mempool) Flush() {} +func (Mempool) FlushAppConn() error { return nil } +func (Mempool) TxsAvailable() <-chan struct{} { return make(chan struct{}) } +func (Mempool) EnableTxsAvailable() {} +func (Mempool) TxsBytes() int64 { return 0 } + +func (Mempool) TxsFront() *clist.CElement { return nil } +func (Mempool) TxsWaitChan() <-chan struct{} { return nil } + +func (Mempool) InitWAL() {} +func (Mempool) CloseWAL() {} diff --git a/node/node.go b/node/node.go index 5af080ad1..1221e8b55 100644 --- a/node/node.go +++ b/node/node.go @@ -146,9 +146,10 @@ type Node struct { // services eventBus *types.EventBus // pub/sub for services stateDB dbm.DB - blockStore *bc.BlockStore // store the blockchain to disk - bcReactor *bc.BlockchainReactor // for fast-syncing - mempoolReactor *mempl.MempoolReactor // for gossipping transactions + blockStore *bc.BlockStore // store the blockchain to disk + bcReactor *bc.BlockchainReactor // for fast-syncing + mempoolReactor *mempl.Reactor // for gossipping transactions + mempool mempl.Mempool consensusState *cs.ConsensusState // latest consensus state consensusReactor *cs.ConsensusReactor // for participating in the consensus pexReactor *pex.PEXReactor // for exchanging peer addresses @@ -272,9 +273,9 @@ func onlyValidatorIsUs(state sm.State, privVal types.PrivValidator) bool { } func createMempoolAndMempoolReactor(config *cfg.Config, proxyApp proxy.AppConns, - state sm.State, memplMetrics *mempl.Metrics, logger log.Logger) (*mempl.MempoolReactor, *mempl.Mempool) { + state sm.State, memplMetrics *mempl.Metrics, logger log.Logger) (*mempl.Reactor, *mempl.CListMempool) { - mempool := mempl.NewMempool( + mempool := mempl.NewCListMempool( config.Mempool, proxyApp.Mempool(), state.LastBlockHeight, @@ -283,11 +284,7 @@ func createMempoolAndMempoolReactor(config *cfg.Config, proxyApp proxy.AppConns, mempl.WithPostCheck(sm.TxPostCheck(state)), ) mempoolLogger := logger.With("module", "mempool") - mempool.SetLogger(mempoolLogger) - if config.Mempool.WalEnabled() { - mempool.InitWAL() // no need to have the mempool wal during tests - } - mempoolReactor := mempl.NewMempoolReactor(config.Mempool, mempool) + mempoolReactor := mempl.NewReactor(config.Mempool, mempool) mempoolReactor.SetLogger(mempoolLogger) if config.Consensus.WaitForTxs() { @@ -315,7 +312,7 @@ func createConsensusReactor(config *cfg.Config, state sm.State, blockExec *sm.BlockExecutor, blockStore sm.BlockStore, - mempool *mempl.Mempool, + mempool *mempl.CListMempool, evidencePool *evidence.EvidencePool, privValidator types.PrivValidator, csMetrics *cs.Metrics, @@ -404,7 +401,7 @@ func createSwitch(config *cfg.Config, transport *p2p.MultiplexTransport, p2pMetrics *p2p.Metrics, peerFilters []p2p.PeerFilterFunc, - mempoolReactor *mempl.MempoolReactor, + mempoolReactor *mempl.Reactor, bcReactor *blockchain.BlockchainReactor, consensusReactor *consensus.ConsensusReactor, evidenceReactor *evidence.EvidenceReactor, @@ -622,6 +619,7 @@ func NewNode(config *cfg.Config, blockStore: blockStore, bcReactor: bcReactor, mempoolReactor: mempoolReactor, + mempool: mempool, consensusState: consensusState, consensusReactor: consensusReactor, pexReactor: pexReactor, @@ -673,6 +671,10 @@ func (n *Node) OnStart() error { n.isListening = true + if n.config.Mempool.WalEnabled() { + n.mempool.InitWAL() // no need to have the mempool wal during tests + } + // Start the switch (the P2P server). err = n.sw.Start() if err != nil { @@ -702,7 +704,7 @@ func (n *Node) OnStop() { // stop mempool WAL if n.config.Mempool.WalEnabled() { - n.mempoolReactor.Mempool.CloseWAL() + n.mempool.CloseWAL() } if err := n.transport.Close(); err != nil { @@ -737,7 +739,7 @@ func (n *Node) ConfigureRPC() { rpccore.SetStateDB(n.stateDB) rpccore.SetBlockStore(n.blockStore) rpccore.SetConsensusState(n.consensusState) - rpccore.SetMempool(n.mempoolReactor.Mempool) + rpccore.SetMempool(n.mempool) rpccore.SetEvidencePool(n.evidencePool) rpccore.SetP2PPeers(n.sw) rpccore.SetP2PTransport(n) @@ -884,11 +886,16 @@ func (n *Node) ConsensusReactor() *cs.ConsensusReactor { return n.consensusReactor } -// MempoolReactor returns the Node's MempoolReactor. -func (n *Node) MempoolReactor() *mempl.MempoolReactor { +// MempoolReactor returns the Node's mempool reactor. +func (n *Node) MempoolReactor() *mempl.Reactor { return n.mempoolReactor } +// Mempool returns the Node's mempool. +func (n *Node) Mempool() mempl.Mempool { + return n.mempool +} + // PEXReactor returns the Node's PEXReactor. It returns nil if PEX is disabled. func (n *Node) PEXReactor() *pex.PEXReactor { return n.pexReactor diff --git a/node/node_test.go b/node/node_test.go index a2725d845..6971ddd34 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -224,7 +224,7 @@ func TestCreateProposalBlock(t *testing.T) { // Make Mempool memplMetrics := mempl.PrometheusMetrics("node_test") - mempool := mempl.NewMempool( + mempool := mempl.NewCListMempool( config.Mempool, proxyApp.Mempool(), state.LastBlockHeight, diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go index 1544a3d95..a1a48abc4 100644 --- a/rpc/client/rpc_test.go +++ b/rpc/client/rpc_test.go @@ -252,7 +252,8 @@ func TestAppCalls(t *testing.T) { func TestBroadcastTxSync(t *testing.T) { require := require.New(t) - mempool := node.MempoolReactor().Mempool + // TODO (melekes): use mempool which is set on RPC rather than getting it from node + mempool := node.Mempool() initMempoolSize := mempool.Size() for i, c := range GetClients() { @@ -272,7 +273,7 @@ func TestBroadcastTxSync(t *testing.T) { func TestBroadcastTxCommit(t *testing.T) { require := require.New(t) - mempool := node.MempoolReactor().Mempool + mempool := node.Mempool() for i, c := range GetClients() { _, _, tx := MakeTxKV() bres, err := c.BroadcastTxCommit(tx) @@ -287,7 +288,7 @@ func TestBroadcastTxCommit(t *testing.T) { func TestUnconfirmedTxs(t *testing.T) { _, _, tx := MakeTxKV() - mempool := node.MempoolReactor().Mempool + mempool := node.Mempool() _ = mempool.CheckTx(tx, nil) for i, c := range GetClients() { @@ -308,7 +309,7 @@ func TestUnconfirmedTxs(t *testing.T) { func TestNumUnconfirmedTxs(t *testing.T) { _, _, tx := MakeTxKV() - mempool := node.MempoolReactor().Mempool + mempool := node.Mempool() _ = mempool.CheckTx(tx, nil) mempoolSize := mempool.Size() diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go index cefb0e371..28a492e6f 100644 --- a/rpc/core/pipe.go +++ b/rpc/core/pipe.go @@ -73,7 +73,7 @@ var ( txIndexer txindex.TxIndexer consensusReactor *consensus.ConsensusReactor eventBus *types.EventBus // thread safe - mempool *mempl.Mempool + mempool mempl.Mempool logger log.Logger @@ -88,7 +88,7 @@ func SetBlockStore(bs sm.BlockStore) { blockStore = bs } -func SetMempool(mem *mempl.Mempool) { +func SetMempool(mem mempl.Mempool) { mempool = mem } diff --git a/state/execution.go b/state/execution.go index 245325525..d872145ef 100644 --- a/state/execution.go +++ b/state/execution.go @@ -8,6 +8,7 @@ import ( dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/fail" "github.com/tendermint/tendermint/libs/log" + mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" ) @@ -30,7 +31,7 @@ type BlockExecutor struct { // manage the mempool lock during commit // and update both with block results after commit. - mempool Mempool + mempool mempl.Mempool evpool EvidencePool logger log.Logger @@ -48,7 +49,7 @@ func BlockExecutorWithMetrics(metrics *Metrics) BlockExecutorOption { // NewBlockExecutor returns a new BlockExecutor with a NopEventBus. // Call SetEventBus to provide one. -func NewBlockExecutor(db dbm.DB, logger log.Logger, proxyApp proxy.AppConnConsensus, mempool Mempool, evpool EvidencePool, options ...BlockExecutorOption) *BlockExecutor { +func NewBlockExecutor(db dbm.DB, logger log.Logger, proxyApp proxy.AppConnConsensus, mempool mempl.Mempool, evpool EvidencePool, options ...BlockExecutorOption) *BlockExecutor { res := &BlockExecutor{ db: db, proxyApp: proxyApp, diff --git a/state/execution_test.go b/state/execution_test.go index 092fbd68f..465b6b0be 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -16,10 +16,10 @@ import ( cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" - tmtime "github.com/tendermint/tendermint/types/time" - + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" ) var ( @@ -38,7 +38,7 @@ func TestApplyBlock(t *testing.T) { state, stateDB := state(1, 1) blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), - MockMempool{}, MockEvidencePool{}) + mock.Mempool{}, MockEvidencePool{}) block := makeBlock(state, 1) blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} @@ -310,7 +310,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { state, stateDB := state(1, 1) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), MockMempool{}, MockEvidencePool{}) + blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{}) eventBus := types.NewEventBus() err = eventBus.Start() @@ -367,7 +367,7 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { defer proxyApp.Stop() state, stateDB := state(1, 1) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), MockMempool{}, MockEvidencePool{}) + blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{}) block := makeBlock(state, 1) blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} diff --git a/state/services.go b/state/services.go index 07d12c5a1..98f6afce3 100644 --- a/state/services.go +++ b/state/services.go @@ -1,8 +1,6 @@ package state import ( - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/types" ) @@ -11,57 +9,6 @@ import ( // NOTE: Interfaces used by RPC must be thread safe! //------------------------------------------------------ -//------------------------------------------------------ -// mempool - -// Mempool defines the mempool interface as used by the ConsensusState. -// Updates to the mempool need to be synchronized with committing a block -// so apps can reset their transient state on Commit -type Mempool interface { - Lock() - Unlock() - - Size() int - CheckTx(types.Tx, func(*abci.Response)) error - CheckTxWithInfo(types.Tx, func(*abci.Response), mempool.TxInfo) error - ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs - Update(int64, types.Txs, mempool.PreCheckFunc, mempool.PostCheckFunc) error - Flush() - FlushAppConn() error - - TxsAvailable() <-chan struct{} - EnableTxsAvailable() -} - -// MockMempool is an empty implementation of a Mempool, useful for testing. -type MockMempool struct{} - -var _ Mempool = MockMempool{} - -func (MockMempool) Lock() {} -func (MockMempool) Unlock() {} -func (MockMempool) Size() int { return 0 } -func (MockMempool) CheckTx(_ types.Tx, _ func(*abci.Response)) error { - return nil -} -func (MockMempool) CheckTxWithInfo(_ types.Tx, _ func(*abci.Response), - _ mempool.TxInfo) error { - return nil -} -func (MockMempool) ReapMaxBytesMaxGas(_, _ int64) types.Txs { return types.Txs{} } -func (MockMempool) Update( - _ int64, - _ types.Txs, - _ mempool.PreCheckFunc, - _ mempool.PostCheckFunc, -) error { - return nil -} -func (MockMempool) Flush() {} -func (MockMempool) FlushAppConn() error { return nil } -func (MockMempool) TxsAvailable() <-chan struct{} { return make(chan struct{}) } -func (MockMempool) EnableTxsAvailable() {} - //------------------------------------------------------ // blockstore @@ -96,7 +43,7 @@ type EvidencePool interface { IsCommitted(types.Evidence) bool } -// MockMempool is an empty implementation of a Mempool, useful for testing. +// MockEvidencePool is an empty implementation of a Mempool, useful for testing. type MockEvidencePool struct{} func (m MockEvidencePool) PendingEvidence(int64) []types.Evidence { return nil } From 4c60ab83c8b46339a30646e91a5c9a547356726e Mon Sep 17 00:00:00 2001 From: needkane Date: Sun, 5 May 2019 22:23:41 +0800 Subject: [PATCH 189/259] Update apps.md (#3621) --- docs/spec/abci/apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/spec/abci/apps.md b/docs/spec/abci/apps.md index 47e62eed9..908ad3eaf 100644 --- a/docs/spec/abci/apps.md +++ b/docs/spec/abci/apps.md @@ -265,7 +265,7 @@ This is enforced by Tendermint consensus. If a block includes evidence older than this, the block will be rejected (validators won't vote for it). -Must have `0 < MaxAge`. +Must have `MaxAge > 0`. ### Updates From 4905640e9bad30f89e74835924a024ef51783b02 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 5 May 2019 12:01:35 -0400 Subject: [PATCH 190/259] types: CommitVotes struct as last step towards #1648 (#3298) * VoteSignBytes builds CanonicalVote * CommitVotes implements VoteSetReader - new CommitVotes struct holds both the Commit and the ValidatorSet and implements VoteSetReader - ToVote takes a ValidatorSet * fix TestCommit * use CommitSig.BlockID Commits may include votes for a different BlockID, could be nil, or different altogether. This means we can't use `commit.BlockID` for reconstructing the sign bytes, since up to -1/3 of the commits might be for independent BlockIDs. This means CommitSig will need to include an indicator for what BlockID it signed - if it's not the committed one or nil, it will need to include it fully in order to be verified. This is unfortunate but unavoidable so long as we include votes for non-committed BlockIDs (which we do to track validator liveness) * fixes from review * remove CommitVotes. CommitSig contains address * remove commit.canonicalVote method * toVote -> getVote, takes valIdx * update adr-025 * commit.ToVoteSet -> CommitToVoteSet * add test * fix from review --- consensus/state.go | 11 +---- docs/architecture/adr-025-commit.md | 62 ++++++++++++++++++---------- types/block.go | 63 +++++++++++++++++++++++------ types/block_test.go | 24 +++++++++++ types/validator_set.go | 10 ++--- types/vote_set.go | 3 ++ 6 files changed, 125 insertions(+), 48 deletions(-) diff --git a/consensus/state.go b/consensus/state.go index a6256f82d..1f6bad9ab 100644 --- a/consensus/state.go +++ b/consensus/state.go @@ -484,16 +484,7 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) { return } seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight) - lastPrecommits := types.NewVoteSet(state.ChainID, state.LastBlockHeight, seenCommit.Round(), types.PrecommitType, state.LastValidators) - for _, precommit := range seenCommit.Precommits { - if precommit == nil { - continue - } - added, err := lastPrecommits.AddVote(seenCommit.ToVote(precommit)) - if !added || err != nil { - panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) - } - } + lastPrecommits := types.CommitToVoteSet(state.ChainID, seenCommit, state.LastValidators) if !lastPrecommits.HasTwoThirdsMajority() { panic("Failed to reconstruct LastCommit: Does not have +2/3 maj") } diff --git a/docs/architecture/adr-025-commit.md b/docs/architecture/adr-025-commit.md index 3f2527951..6db039d43 100644 --- a/docs/architecture/adr-025-commit.md +++ b/docs/architecture/adr-025-commit.md @@ -1,14 +1,18 @@ # ADR 025 Commit ## Context + Currently the `Commit` structure contains a lot of potentially redundant or unnecessary data. -In particular it contains an array of every precommit from the validators, which includes many copies of the same data. Such as `Height`, `Round`, `Type`, and `BlockID`. Also the `ValidatorIndex` could be derived from the vote's position in the array, and the `ValidatorAddress` could potentially be derived from runtime context. The only truely necessary data is the `Signature` and `Timestamp` associated with each `Vote`. +It contains a list of precommits from every validator, where the precommit +includes the whole `Vote` structure. Thus each of the commit height, round, +type, and blockID are repeated for every validator, and could be deduplicated. ``` type Commit struct { BlockID BlockID `json:"block_id"` Precommits []*Vote `json:"precommits"` } + type Vote struct { ValidatorAddress Address `json:"validator_address"` ValidatorIndex int `json:"validator_index"` @@ -26,7 +30,9 @@ References: [#2226](https://github.com/tendermint/tendermint/issues/2226) ## Proposed Solution + We can improve efficiency by replacing the usage of the `Vote` struct with a subset of each vote, and by storing the constant values (`Height`, `Round`, `BlockID`) in the Commit itself. + ``` type Commit struct { Height int64 @@ -34,42 +40,56 @@ type Commit struct { BlockID BlockID `json:"block_id"` Precommits []*CommitSig `json:"precommits"` } + type CommitSig struct { + BlockID BlockIDFlag ValidatorAddress Address - Signature []byte Timestamp time.Time + Signature []byte } + + +// indicate which BlockID the signature is for +type BlockIDFlag int + +const ( + BlockIDFlagAbsent BlockIDFlag = iota // vote is not included in the Commit.Precommits + BlockIDFlagCommit // voted for the Commit.BlockID + BlockIDFlagNil // voted for nil +) + ``` -Continuing to store the `ValidatorAddress` in the `CommitSig` takes up extra space, but simplifies the process and allows for easier debugging. + +Note the need for an extra byte to indicate whether the signature is for the BlockID or for nil. +This byte can also be used to indicate an absent vote, rather than using a nil object like we currently do, +which has been [problematic for compatibility between Amino and proto3](https://github.com/tendermint/go-amino/issues/260). + +Note we also continue to store the `ValidatorAddress` in the `CommitSig`. +While this still takes 20-bytes per signature, it ensures that the Commit has all +information necessary to reconstruct Vote, which simplifies mapping between Commit and Vote objects +and with debugging. It also may be necessary for the light-client to know which address a signature corresponds to if +it is trying to verify a current commit with an older validtor set. ## Status + Proposed ## Consequences ### Positive -The size of a `Commit` transmitted over the network goes from: -|BlockID| + n * (|Address| + |ValidatorIndex| + |Height| + |Round| + |Timestamp| + |Type| + |BlockID| + |Signature|) +Removing the Type/Height/Round/Index and the BlockID saves roughly 80 bytes per precommit. +It varies because some integers are varint. The BlockID contains two 32-byte hashes an integer, +and the Height is 8-bytes. -to: +For a chain with 100 validators, that's up to 8kB in savings per block! -|BlockID|+|Height|+|Round| + n*(|Address| + |Signature| + |Timestamp|) - -This saves: - -n * (|BlockID| + |ValidatorIndex| + |Type|) + (n-1) * (Height + Round) - -In the current context, this would concretely be: -(assuming all ints are int64, and hashes are 32 bytes) - -n *(72 + 8 + 1 + 8 + 8) - 16 = n * 97 - 16 - -With 100 validators this is a savings of almost 10KB on every block. - ### Negative -This would add some complexity to the processing and verification of blocks and commits, as votes would have to be reconstructed to be verified and gossiped. The reconstruction could be relatively straightforward, only requiring the copying of data from the `Commit` itself into the newly created `Vote`. + +- Large breaking change to the block and commit structure +- Requires differentiating in code between the Vote and CommitSig objects, which may add some complexity (votes need to be reconstructed to be verified and gossiped) ### Neutral -This design leaves the `ValidatorAddress` in the `CommitSig` and in the `Vote`. These could be removed at some point for additional savings, but that would introduce more complexity, and make printing of `Commit` and `VoteSet` objects less informative, which could harm debugging efficiency and UI/UX. \ No newline at end of file + +- Commit.Precommits no longer contains nil values diff --git a/types/block.go b/types/block.go index 6616c0ee6..d793ce9dd 100644 --- a/types/block.go +++ b/types/block.go @@ -500,6 +500,8 @@ func (cs *CommitSig) toVote() *Vote { return &v } +//------------------------------------- + // Commit contains the evidence that a block was committed by a set of validators. // NOTE: Commit is empty for height 1, but never nil. type Commit struct { @@ -528,11 +530,56 @@ func NewCommit(blockID BlockID, precommits []*CommitSig) *Commit { } } +// Construct a VoteSet from the Commit and validator set. Panics +// if precommits from the commit can't be added to the voteset. +// Inverse of VoteSet.MakeCommit(). +func CommitToVoteSet(chainID string, commit *Commit, vals *ValidatorSet) *VoteSet { + height, round, typ := commit.Height(), commit.Round(), PrecommitType + voteSet := NewVoteSet(chainID, height, round, typ, vals) + for idx, precommit := range commit.Precommits { + if precommit == nil { + continue + } + added, err := voteSet.AddVote(commit.GetVote(idx)) + if !added || err != nil { + panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err)) + } + } + return voteSet +} + +// GetVote converts the CommitSig for the given valIdx to a Vote. +// Returns nil if the precommit at valIdx is nil. +// Panics if valIdx >= commit.Size(). +func (commit *Commit) GetVote(valIdx int) *Vote { + commitSig := commit.Precommits[valIdx] + if commitSig == nil { + return nil + } + + // NOTE: this commitSig might be for a nil blockID, + // so we can't just use commit.BlockID here. + // For #1648, CommitSig will need to indicate what BlockID it's for ! + blockID := commitSig.BlockID + commit.memoizeHeightRound() + return &Vote{ + Type: PrecommitType, + Height: commit.height, + Round: commit.round, + BlockID: blockID, + Timestamp: commitSig.Timestamp, + ValidatorAddress: commitSig.ValidatorAddress, + ValidatorIndex: valIdx, + Signature: commitSig.Signature, + } +} + // VoteSignBytes constructs the SignBytes for the given CommitSig. // The only unique part of the SignBytes is the Timestamp - all other fields // signed over are otherwise the same for all validators. -func (commit *Commit) VoteSignBytes(chainID string, cs *CommitSig) []byte { - return commit.ToVote(cs).SignBytes(chainID) +// Panics if valIdx >= commit.Size(). +func (commit *Commit) VoteSignBytes(chainID string, valIdx int) []byte { + return commit.GetVote(valIdx).SignBytes(chainID) } // memoizeHeightRound memoizes the height and round of the commit using @@ -553,14 +600,6 @@ func (commit *Commit) memoizeHeightRound() { } } -// ToVote converts a CommitSig to a Vote. -// If the CommitSig is nil, the Vote will be nil. -func (commit *Commit) ToVote(cs *CommitSig) *Vote { - // TODO: use commit.validatorSet to reconstruct vote - // and deprecate .toVote - return cs.toVote() -} - // Height returns the height of the commit func (commit *Commit) Height() int64 { commit.memoizeHeightRound() @@ -602,8 +641,8 @@ func (commit *Commit) BitArray() *cmn.BitArray { // GetByIndex returns the vote corresponding to a given validator index. // Panics if `index >= commit.Size()`. // Implements VoteSetReader. -func (commit *Commit) GetByIndex(index int) *Vote { - return commit.ToVote(commit.Precommits[index]) +func (commit *Commit) GetByIndex(valIdx int) *Vote { + return commit.GetVote(valIdx) } // IsCommit returns true if there is at least one vote. diff --git a/types/block_test.go b/types/block_test.go index 75b5c19dd..ff7edd27a 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -342,3 +342,27 @@ func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) { } } } + +func TestCommitToVoteSet(t *testing.T) { + lastID := makeBlockIDRandom() + h := int64(3) + + voteSet, valSet, vals := randVoteSet(h-1, 1, PrecommitType, 10, 1) + commit, err := MakeCommit(lastID, h-1, 1, voteSet, vals) + assert.NoError(t, err) + + chainID := voteSet.ChainID() + voteSet2 := CommitToVoteSet(chainID, commit, valSet) + + for i := 0; i < len(vals); i++ { + vote1 := voteSet.GetByIndex(i) + vote2 := voteSet2.GetByIndex(i) + vote3 := commit.GetVote(i) + + vote1bz := cdc.MustMarshalBinaryBare(vote1) + vote2bz := cdc.MustMarshalBinaryBare(vote2) + vote3bz := cdc.MustMarshalBinaryBare(vote3) + assert.Equal(t, vote1bz, vote2bz) + assert.Equal(t, vote1bz, vote3bz) + } +} diff --git a/types/validator_set.go b/types/validator_set.go index 36ce67f06..9e78fbc77 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -612,7 +612,7 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i } _, val := vals.GetByIndex(idx) // Validate signature. - precommitSignBytes := commit.VoteSignBytes(chainID, precommit) + precommitSignBytes := commit.VoteSignBytes(chainID, idx) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return fmt.Errorf("Invalid commit -- invalid signature: %v", precommit) } @@ -689,14 +689,14 @@ func (vals *ValidatorSet) VerifyFutureCommit(newSet *ValidatorSet, chainID strin return cmn.NewError("Invalid commit -- not precommit @ index %v", idx) } // See if this validator is in oldVals. - idx, val := oldVals.GetByAddress(precommit.ValidatorAddress) - if val == nil || seen[idx] { + oldIdx, val := oldVals.GetByAddress(precommit.ValidatorAddress) + if val == nil || seen[oldIdx] { continue // missing or double vote... } - seen[idx] = true + seen[oldIdx] = true // Validate signature. - precommitSignBytes := commit.VoteSignBytes(chainID, precommit) + precommitSignBytes := commit.VoteSignBytes(chainID, idx) if !val.PubKey.VerifyBytes(precommitSignBytes, precommit.Signature) { return cmn.NewError("Invalid commit -- invalid signature: %v", precommit) } diff --git a/types/vote_set.go b/types/vote_set.go index 2c896b39c..a4a42bb4c 100644 --- a/types/vote_set.go +++ b/types/vote_set.go @@ -528,6 +528,9 @@ func (voteSet *VoteSet) sumTotalFrac() (int64, int64, float64) { //-------------------------------------------------------------------------------- // Commit +// MakeCommit constructs a Commit from the VoteSet. +// Panics if the vote type is not PrecommitType or if +// there's no +2/3 votes for a single block. func (voteSet *VoteSet) MakeCommit() *Commit { if voteSet.type_ != PrecommitType { panic("Cannot MakeCommit() unless VoteSet.Type is PrecommitType") From e2f547154592ebe3f174a15b52639d691dc53fbc Mon Sep 17 00:00:00 2001 From: Daniil Lashin Date: Sun, 5 May 2019 19:19:19 +0300 Subject: [PATCH 191/259] ADR-037: DeliverBlock (#3420) * ADR 037 Initial draft * Add link to initial conversation * Update initial draft * Fix indentations * Change negative consequences * Fix indentations * Change ADR id * Add one more positive consequence * Rollback ADR number --- docs/architecture/adr-038-deliver-block.md | 100 +++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 docs/architecture/adr-038-deliver-block.md diff --git a/docs/architecture/adr-038-deliver-block.md b/docs/architecture/adr-038-deliver-block.md new file mode 100644 index 000000000..ac23ab693 --- /dev/null +++ b/docs/architecture/adr-038-deliver-block.md @@ -0,0 +1,100 @@ +# ADR 037: Deliver Block + +Author: Daniil Lashin (@danil-lashin) + +## Changelog + +13-03-2019: Initial draft + +## Context + +Initial conversation: https://github.com/tendermint/tendermint/issues/2901 + +Some applications can handle transactions in parallel, or at least some +part of tx processing can be parallelized. Now it is not possible for developer +to execute txs in parallel because Tendermint delivers them consequentially. + +## Decision + +Now Tendermint have `BeginBlock`, `EndBlock`, `Commit`, `DeliverTx` steps +while executing block. This doc proposes merging this steps into one `DeliverBlock` +step. It will allow developers of applications to decide how they want to +execute transactions (in parallel or consequentially). Also it will simplify and +speed up communications between application and Tendermint. + +As @jaekwon [mentioned](https://github.com/tendermint/tendermint/issues/2901#issuecomment-477746128) +in discussion not all application will benefit from this solution. In some cases, +when application handles transaction consequentially, it way slow down the blockchain, +because it need to wait until full block is transmitted to application to start +processing it. Also, in the case of complete change of ABCI, we need to force all the apps +to change their implementation completely. That's why I propose to introduce one more ABCI +type. + +# Implementation Changes + +In addition to default application interface which now have this structure + +```go +type Application interface { + // Info and Mempool methods... + + // Consensus Connection + InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore + BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block + DeliverTx(tx []byte) ResponseDeliverTx // Deliver a tx for full processing + EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set + Commit() ResponseCommit // Commit the state and return the application Merkle root hash +} +``` + +this doc proposes to add one more: + +```go +type Application interface { + // Info and Mempool methods... + + // Consensus Connection + InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore + DeliverBlock(RequestDeliverBlock) ResponseDeliverBlock // Deliver full block + Commit() ResponseCommit // Commit the state and return the application Merkle root hash +} + +type RequestDeliverBlock struct { + Hash []byte + Header Header + Txs Txs + LastCommitInfo LastCommitInfo + ByzantineValidators []Evidence +} + +type ResponseDeliverBlock struct { + ValidatorUpdates []ValidatorUpdate + ConsensusParamUpdates *ConsensusParams + Tags []common.KVPair + TxResults []ResponseDeliverTx +} + +``` + +Also, we will need to add new config param, which will specify what kind of ABCI application uses. +For example, it can be `abci_type`. Then we will have 2 types: +- `advanced` - current ABCI +- `simple` - proposed implementation + +## Status + +In review + +## Consequences + +### Positive + +- much simpler introduction and tutorials for new developers (instead of implementing 5 methods whey +will need to implement only 3) +- txs can be handled in parallel +- simpler interface +- faster communications between Tendermint and application + +### Negative + +- Tendermint should now support 2 kinds of ABCI From 86f2765b32ab92864e1a59eab08663b06904d518 Mon Sep 17 00:00:00 2001 From: Mostafa Sedaghat Joo Date: Mon, 6 May 2019 00:19:41 +0800 Subject: [PATCH 192/259] Add adr-36 (#3324) --- .../adr-036-validator-set-by-app.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/architecture/adr-036-validator-set-by-app.md diff --git a/docs/architecture/adr-036-validator-set-by-app.md b/docs/architecture/adr-036-validator-set-by-app.md new file mode 100644 index 000000000..ff752b75d --- /dev/null +++ b/docs/architecture/adr-036-validator-set-by-app.md @@ -0,0 +1,29 @@ +# ADR 036: Application should be in charge of validator set + +## Changelog + + +## Context + +Currently Tendermint is in charge of validator set and proposer selection. Application can only update the validator set changes at EndBlock time. +To support Light Client, application should make sure at least 2/3 of validator are same at each round. + +Application should have full control on validator set changes and proposer selection. In each round Application can provide the list of validators for next rounds in order with their power. The proposer is the first in the list, in case the proposer is offline, the next one can propose the proposal and so on. + +## Decision + +## Status + +## Consequences + +Tendermint is no more in charge of validator set and its changes. The Application should provide the correct information. +However Tendermint can provide psedo-randomness algorithm to help application for selecting proposer in each round. + +### Positive + +### Negative + +### Neutral + +## References + From 4519ef3109fccebadf3d691e7d0c1ee3217636be Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Sun, 5 May 2019 12:42:26 -0400 Subject: [PATCH 193/259] adr renames (#3622) --- ...iver-block.md => adr-037-deliver-block.md} | 32 +++++++++---------- ...=> adr-041-proposer-selection-via-abci.md} | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) rename docs/architecture/{adr-038-deliver-block.md => adr-037-deliver-block.md} (87%) rename docs/architecture/{adr-036-validator-set-by-app.md => adr-041-proposer-selection-via-abci.md} (93%) diff --git a/docs/architecture/adr-038-deliver-block.md b/docs/architecture/adr-037-deliver-block.md similarity index 87% rename from docs/architecture/adr-038-deliver-block.md rename to docs/architecture/adr-037-deliver-block.md index ac23ab693..31907c9ae 100644 --- a/docs/architecture/adr-038-deliver-block.md +++ b/docs/architecture/adr-037-deliver-block.md @@ -10,24 +10,24 @@ Author: Daniil Lashin (@danil-lashin) Initial conversation: https://github.com/tendermint/tendermint/issues/2901 -Some applications can handle transactions in parallel, or at least some +Some applications can handle transactions in parallel, or at least some part of tx processing can be parallelized. Now it is not possible for developer to execute txs in parallel because Tendermint delivers them consequentially. ## Decision -Now Tendermint have `BeginBlock`, `EndBlock`, `Commit`, `DeliverTx` steps -while executing block. This doc proposes merging this steps into one `DeliverBlock` -step. It will allow developers of applications to decide how they want to -execute transactions (in parallel or consequentially). Also it will simplify and -speed up communications between application and Tendermint. +Now Tendermint have `BeginBlock`, `EndBlock`, `Commit`, `DeliverTx` steps +while executing block. This doc proposes merging this steps into one `DeliverBlock` +step. It will allow developers of applications to decide how they want to +execute transactions (in parallel or consequentially). Also it will simplify and +speed up communications between application and Tendermint. -As @jaekwon [mentioned](https://github.com/tendermint/tendermint/issues/2901#issuecomment-477746128) -in discussion not all application will benefit from this solution. In some cases, +As @jaekwon [mentioned](https://github.com/tendermint/tendermint/issues/2901#issuecomment-477746128) +in discussion not all application will benefit from this solution. In some cases, when application handles transaction consequentially, it way slow down the blockchain, -because it need to wait until full block is transmitted to application to start +because it need to wait until full block is transmitted to application to start processing it. Also, in the case of complete change of ABCI, we need to force all the apps -to change their implementation completely. That's why I propose to introduce one more ABCI +to change their implementation completely. That's why I propose to introduce one more ABCI type. # Implementation Changes @@ -52,7 +52,7 @@ this doc proposes to add one more: ```go type Application interface { // Info and Mempool methods... - + // Consensus Connection InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore DeliverBlock(RequestDeliverBlock) ResponseDeliverBlock // Deliver full block @@ -61,9 +61,9 @@ type Application interface { type RequestDeliverBlock struct { Hash []byte - Header Header + Header Header Txs Txs - LastCommitInfo LastCommitInfo + LastCommitInfo LastCommitInfo ByzantineValidators []Evidence } @@ -71,7 +71,7 @@ type ResponseDeliverBlock struct { ValidatorUpdates []ValidatorUpdate ConsensusParamUpdates *ConsensusParams Tags []common.KVPair - TxResults []ResponseDeliverTx + TxResults []ResponseDeliverTx } ``` @@ -79,7 +79,7 @@ type ResponseDeliverBlock struct { Also, we will need to add new config param, which will specify what kind of ABCI application uses. For example, it can be `abci_type`. Then we will have 2 types: - `advanced` - current ABCI -- `simple` - proposed implementation +- `simple` - proposed implementation ## Status @@ -89,7 +89,7 @@ In review ### Positive -- much simpler introduction and tutorials for new developers (instead of implementing 5 methods whey +- much simpler introduction and tutorials for new developers (instead of implementing 5 methods whey will need to implement only 3) - txs can be handled in parallel - simpler interface diff --git a/docs/architecture/adr-036-validator-set-by-app.md b/docs/architecture/adr-041-proposer-selection-via-abci.md similarity index 93% rename from docs/architecture/adr-036-validator-set-by-app.md rename to docs/architecture/adr-041-proposer-selection-via-abci.md index ff752b75d..58bf20de3 100644 --- a/docs/architecture/adr-036-validator-set-by-app.md +++ b/docs/architecture/adr-041-proposer-selection-via-abci.md @@ -1,4 +1,4 @@ -# ADR 036: Application should be in charge of validator set +# ADR 041: Application should be in charge of validator set ## Changelog From debf8f70c9026157c433c952f7893c0b1e52574f Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 6 May 2019 13:42:21 +0400 Subject: [PATCH 194/259] libs/db: bbolt (etcd's fork of bolt) (#3610) Description: add boltdb implement for db interface. The boltdb is safe and high performence embedded KV database. It is based on B+tree and Mmap. Now it is maintained by etcd-io org. https://github.com/etcd-io/bbolt It is much better than LSM tree (levelDB) when RANDOM and SCAN read. Replaced #3604 Refs #1835 Commits: * add boltdb for storage * update boltdb in gopkg.toml * update bbolt in Gopkg.lock * dep update Gopkg.lock * add boltdb'bucket when create Boltdb * some modifies * address my own comments * fix most of the Iterator tests for boltdb * bolt does not support concurrent writes while iterating * add WARNING word * note that ReadOnly option is not supported * fixes after Ismail's review Co-Authored-By: melekes * panic if View returns error plus specify bucket in the top comment --- Gopkg.lock | 17 +- Gopkg.toml | 4 + libs/db/boltdb.go | 323 ++++++++++++++++++++++++++++++++++++ libs/db/boltdb_test.go | 35 ++++ libs/db/common_test.go | 66 ++++++++ libs/db/db.go | 1 + libs/db/fsdb.go | 2 +- libs/db/go_level_db_test.go | 95 ++--------- libs/db/mem_db.go | 2 +- libs/db/util_test.go | 5 + 10 files changed, 468 insertions(+), 82 deletions(-) create mode 100644 libs/db/boltdb.go create mode 100644 libs/db/boltdb_test.go diff --git a/Gopkg.lock b/Gopkg.lock index 530cd89dd..6d9236559 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -34,6 +34,14 @@ revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" version = "v1.1.1" +[[projects]] + digest = "1:5f7414cf41466d4b4dd7ec52b2cd3e481e08cfd11e7e24fef730c0e483e88bb1" + name = "github.com/etcd-io/bbolt" + packages = ["."] + pruneopts = "UT" + revision = "63597a96ec0ad9e6d43c3fc81e809909e0237461" + version = "v1.3.2" + [[projects]] digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70" name = "github.com/fortytw2/leaktest" @@ -170,9 +178,12 @@ revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" [[projects]] - digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7" + digest = "1:53e8c5c79716437e601696140e8b1801aae4204f4ec54a504333702a49572c4f" name = "github.com/magiconair/properties" - packages = ["."] + packages = [ + ".", + "assert", + ] pruneopts = "UT" revision = "c2353362d570a7bfa228149c62842019201cfb71" version = "v1.8.0" @@ -500,6 +511,7 @@ "github.com/btcsuite/btcd/btcec", "github.com/btcsuite/btcutil/base58", "github.com/btcsuite/btcutil/bech32", + "github.com/etcd-io/bbolt", "github.com/fortytw2/leaktest", "github.com/go-kit/kit/log", "github.com/go-kit/kit/log/level", @@ -516,6 +528,7 @@ "github.com/golang/protobuf/ptypes/timestamp", "github.com/gorilla/websocket", "github.com/jmhodges/levigo", + "github.com/magiconair/properties/assert", "github.com/pkg/errors", "github.com/prometheus/client_golang/prometheus", "github.com/prometheus/client_golang/prometheus/promhttp", diff --git a/Gopkg.toml b/Gopkg.toml index 505f0da4a..e5abf051c 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -22,6 +22,10 @@ ########################################################### # Allow only patch releases for serialization libraries +[[constraint]] + name = "github.com/etcd-io/bbolt" + version = "v1.3.2" + [[constraint]] name = "github.com/tendermint/go-amino" version = "~0.14.1" diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go new file mode 100644 index 000000000..6a31f11a9 --- /dev/null +++ b/libs/db/boltdb.go @@ -0,0 +1,323 @@ +package db + +import ( + "bytes" + "errors" + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/etcd-io/bbolt" +) + +var bucket = []byte("tm") + +func init() { + registerDBCreator(BoltDBBackend, func(name, dir string) (DB, error) { + return NewBoltDB(name, dir) + }, false) +} + +// BoltDB is a wrapper around etcd's fork of bolt +// (https://github.com/etcd-io/bbolt). +// +// NOTE: All operations (including Set, Delete) are synchronous by default. One +// can globally turn it off by using NoSync config option (not recommended). +// +// A single bucket ([]byte("tm")) is used per a database instance. This could +// lead to performance issues when/if there will be lots of keys. +type BoltDB struct { + db *bbolt.DB +} + +// NewBoltDB returns a BoltDB with default options. +func NewBoltDB(name, dir string) (DB, error) { + return NewBoltDBWithOpts(name, dir, bbolt.DefaultOptions) +} + +// NewBoltDBWithOpts allows you to supply *bbolt.Options. ReadOnly: true is not +// supported because NewBoltDBWithOpts creates a global bucket. +func NewBoltDBWithOpts(name string, dir string, opts *bbolt.Options) (DB, error) { + if opts.ReadOnly { + return nil, errors.New("ReadOnly: true is not supported") + } + + dbPath := filepath.Join(dir, name+".db") + db, err := bbolt.Open(dbPath, os.ModePerm, opts) + if err != nil { + return nil, err + } + + // create a global bucket + err = db.Update(func(tx *bbolt.Tx) error { + _, err := tx.CreateBucketIfNotExists(bucket) + return err + }) + if err != nil { + return nil, err + } + + return &BoltDB{db: db}, nil +} + +func (bdb *BoltDB) Get(key []byte) (value []byte) { + key = nonEmptyKey(nonNilBytes(key)) + err := bdb.db.View(func(tx *bbolt.Tx) error { + b := tx.Bucket(bucket) + value = b.Get(key) + return nil + }) + if err != nil { + panic(err) + } + return +} + +func (bdb *BoltDB) Has(key []byte) bool { + return bdb.Get(key) != nil +} + +func (bdb *BoltDB) Set(key, value []byte) { + key = nonEmptyKey(nonNilBytes(key)) + value = nonNilBytes(value) + err := bdb.db.Update(func(tx *bbolt.Tx) error { + b := tx.Bucket(bucket) + return b.Put(key, value) + }) + if err != nil { + panic(err) + } +} + +func (bdb *BoltDB) SetSync(key, value []byte) { + bdb.Set(key, value) +} + +func (bdb *BoltDB) Delete(key []byte) { + key = nonEmptyKey(nonNilBytes(key)) + err := bdb.db.Update(func(tx *bbolt.Tx) error { + return tx.Bucket(bucket).Delete(key) + }) + if err != nil { + panic(err) + } +} + +func (bdb *BoltDB) DeleteSync(key []byte) { + bdb.Delete(key) +} + +func (bdb *BoltDB) Close() { + bdb.db.Close() +} + +func (bdb *BoltDB) Print() { + stats := bdb.db.Stats() + fmt.Printf("%v\n", stats) + + err := bdb.db.View(func(tx *bbolt.Tx) error { + tx.Bucket(bucket).ForEach(func(k, v []byte) error { + fmt.Printf("[%X]:\t[%X]\n", k, v) + return nil + }) + return nil + }) + if err != nil { + panic(err) + } +} + +func (bdb *BoltDB) Stats() map[string]string { + stats := bdb.db.Stats() + m := make(map[string]string) + + // Freelist stats + m["FreePageN"] = fmt.Sprintf("%v", stats.FreePageN) + m["PendingPageN"] = fmt.Sprintf("%v", stats.PendingPageN) + m["FreeAlloc"] = fmt.Sprintf("%v", stats.FreeAlloc) + m["FreelistInuse"] = fmt.Sprintf("%v", stats.FreelistInuse) + + // Transaction stats + m["TxN"] = fmt.Sprintf("%v", stats.TxN) + m["OpenTxN"] = fmt.Sprintf("%v", stats.OpenTxN) + + return m +} + +// boltDBBatch stores key values in sync.Map and dumps them to the underlying +// DB upon Write call. +type boltDBBatch struct { + buffer *sync.Map + db *BoltDB +} + +// NewBatch returns a new batch. +func (bdb *BoltDB) NewBatch() Batch { + return &boltDBBatch{ + buffer: &sync.Map{}, + db: bdb, + } +} + +func (bdb *boltDBBatch) Set(key, value []byte) { + bdb.buffer.Store(key, value) +} + +func (bdb *boltDBBatch) Delete(key []byte) { + bdb.buffer.Delete(key) +} + +// NOTE: the operation is synchronous (see BoltDB for reasons) +func (bdb *boltDBBatch) Write() { + err := bdb.db.db.Batch(func(tx *bbolt.Tx) error { + b := tx.Bucket(bucket) + var putErr error + bdb.buffer.Range(func(key, value interface{}) bool { + putErr = b.Put(key.([]byte), value.([]byte)) + return putErr == nil // stop if putErr is not nil + }) + return putErr + }) + if err != nil { + panic(err) + } +} + +func (bdb *boltDBBatch) WriteSync() { + bdb.Write() +} + +func (bdb *boltDBBatch) Close() {} + +// WARNING: Any concurrent writes (Set, SetSync) will block until the Iterator +// is closed. +func (bdb *BoltDB) Iterator(start, end []byte) Iterator { + tx, err := bdb.db.Begin(false) + if err != nil { + panic(err) + } + c := tx.Bucket(bucket).Cursor() + return newBoltDBIterator(c, start, end, false) +} + +// WARNING: Any concurrent writes (Set, SetSync) will block until the Iterator +// is closed. +func (bdb *BoltDB) ReverseIterator(start, end []byte) Iterator { + tx, err := bdb.db.Begin(false) + if err != nil { + panic(err) + } + c := tx.Bucket(bucket).Cursor() + return newBoltDBIterator(c, start, end, true) +} + +// boltDBIterator allows you to iterate on range of keys/values given some +// start / end keys (nil & nil will result in doing full scan). +type boltDBIterator struct { + itr *bbolt.Cursor + start []byte + end []byte + + currentKey []byte + currentValue []byte + + isInvalid bool + isReverse bool +} + +func newBoltDBIterator(itr *bbolt.Cursor, start, end []byte, isReverse bool) *boltDBIterator { + var ck, cv []byte + if isReverse { + if end == nil { + ck, cv = itr.Last() + } else { + _, _ = itr.Seek(end) // after key + ck, cv = itr.Prev() // return to end key + } + } else { + if start == nil { + ck, cv = itr.First() + } else { + ck, cv = itr.Seek(start) + } + } + + return &boltDBIterator{ + itr: itr, + start: start, + end: end, + currentKey: ck, + currentValue: cv, + isReverse: isReverse, + isInvalid: false, + } +} + +func (itr *boltDBIterator) Domain() ([]byte, []byte) { + return itr.start, itr.end +} + +func (itr *boltDBIterator) Valid() bool { + if itr.isInvalid { + return false + } + + // iterated to the end of the cursor + if len(itr.currentKey) == 0 { + itr.isInvalid = true + return false + } + + if itr.isReverse { + if itr.start != nil && bytes.Compare(itr.currentKey, itr.start) < 0 { + itr.isInvalid = true + return false + } + } else { + if itr.end != nil && bytes.Compare(itr.end, itr.currentKey) <= 0 { + itr.isInvalid = true + return false + } + } + + // Valid + return true +} + +func (itr *boltDBIterator) Next() { + itr.assertIsValid() + if itr.isReverse { + itr.currentKey, itr.currentValue = itr.itr.Prev() + } else { + itr.currentKey, itr.currentValue = itr.itr.Next() + } +} + +func (itr *boltDBIterator) Key() []byte { + itr.assertIsValid() + return itr.currentKey +} + +func (itr *boltDBIterator) Value() []byte { + itr.assertIsValid() + return itr.currentValue +} + +// boltdb cursor has no close op. +func (itr *boltDBIterator) Close() {} + +func (itr *boltDBIterator) assertIsValid() { + if !itr.Valid() { + panic("Boltdb-iterator is invalid") + } +} + +// nonEmptyKey returns a []byte("nil") if key is empty. +// WARNING: this may collude with "nil" user key! +func nonEmptyKey(key []byte) []byte { + if len(key) == 0 { + return []byte("nil") + } + return key +} diff --git a/libs/db/boltdb_test.go b/libs/db/boltdb_test.go new file mode 100644 index 000000000..ced57d2e0 --- /dev/null +++ b/libs/db/boltdb_test.go @@ -0,0 +1,35 @@ +package db + +import ( + "fmt" + "os" + "testing" + + "github.com/stretchr/testify/require" + + cmn "github.com/tendermint/tendermint/libs/common" +) + +func TestBoltDBNewBoltDB(t *testing.T) { + name := fmt.Sprintf("test_%x", cmn.RandStr(12)) + dir := os.TempDir() + defer cleanupDBDir(dir, name) + + db, err := NewBoltDB(name, dir) + require.NoError(t, err) + db.Close() +} + +func BenchmarkBoltDBRandomReadsWrites(b *testing.B) { + name := fmt.Sprintf("test_%x", cmn.RandStr(12)) + db, err := NewBoltDB(name, "") + if err != nil { + b.Fatal(err) + } + defer func() { + db.Close() + cleanupDBDir("", name) + }() + + benchmarkRandomReadsWrites(b, db) +} diff --git a/libs/db/common_test.go b/libs/db/common_test.go index 1e27a7cac..64a86979c 100644 --- a/libs/db/common_test.go +++ b/libs/db/common_test.go @@ -1,6 +1,8 @@ package db import ( + "bytes" + "encoding/binary" "fmt" "io/ioutil" "sync" @@ -8,6 +10,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tendermint/libs/common" ) //---------------------------------------- @@ -188,3 +191,66 @@ func (mockIterator) Value() []byte { func (mockIterator) Close() { } + +func benchmarkRandomReadsWrites(b *testing.B, db DB) { + b.StopTimer() + + // create dummy data + const numItems = int64(1000000) + internal := map[int64]int64{} + for i := 0; i < int(numItems); i++ { + internal[int64(i)] = int64(0) + } + + // fmt.Println("ok, starting") + b.StartTimer() + + for i := 0; i < b.N; i++ { + // Write something + { + idx := int64(cmn.RandInt()) % numItems + internal[idx]++ + val := internal[idx] + idxBytes := int642Bytes(int64(idx)) + valBytes := int642Bytes(int64(val)) + //fmt.Printf("Set %X -> %X\n", idxBytes, valBytes) + db.Set(idxBytes, valBytes) + } + + // Read something + { + idx := int64(cmn.RandInt()) % numItems + valExp := internal[idx] + idxBytes := int642Bytes(int64(idx)) + valBytes := db.Get(idxBytes) + //fmt.Printf("Get %X -> %X\n", idxBytes, valBytes) + if valExp == 0 { + if !bytes.Equal(valBytes, nil) { + b.Errorf("Expected %v for %v, got %X", nil, idx, valBytes) + break + } + } else { + if len(valBytes) != 8 { + b.Errorf("Expected length 8 for %v, got %X", idx, valBytes) + break + } + valGot := bytes2Int64(valBytes) + if valExp != valGot { + b.Errorf("Expected %v for %v, got %v", valExp, idx, valGot) + break + } + } + } + + } +} + +func int642Bytes(i int64) []byte { + buf := make([]byte, 8) + binary.BigEndian.PutUint64(buf, uint64(i)) + return buf +} + +func bytes2Int64(buf []byte) int64 { + return int64(binary.BigEndian.Uint64(buf)) +} diff --git a/libs/db/db.go b/libs/db/db.go index 8a3975a84..43b788281 100644 --- a/libs/db/db.go +++ b/libs/db/db.go @@ -16,6 +16,7 @@ const ( GoLevelDBBackend DBBackendType = "goleveldb" MemDBBackend DBBackendType = "memdb" FSDBBackend DBBackendType = "fsdb" // using the filesystem naively + BoltDBBackend DBBackendType = "boltdb" ) type dbCreator func(name string, dir string) (DB, error) diff --git a/libs/db/fsdb.go b/libs/db/fsdb.go index 2d82e7749..ca8eefe94 100644 --- a/libs/db/fsdb.go +++ b/libs/db/fsdb.go @@ -20,7 +20,7 @@ const ( ) func init() { - registerDBCreator(FSDBBackend, func(name string, dir string) (DB, error) { + registerDBCreator(FSDBBackend, func(name, dir string) (DB, error) { dbPath := filepath.Join(dir, name+".db") return NewFSDB(dbPath), nil }, false) diff --git a/libs/db/go_level_db_test.go b/libs/db/go_level_db_test.go index c24eec3c8..f781a2b3d 100644 --- a/libs/db/go_level_db_test.go +++ b/libs/db/go_level_db_test.go @@ -1,29 +1,27 @@ package db import ( - "bytes" - "encoding/binary" "fmt" - "os" "testing" + "github.com/stretchr/testify/require" "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/stretchr/testify/require" cmn "github.com/tendermint/tendermint/libs/common" ) -func TestNewGoLevelDB(t *testing.T) { +func TestGoLevelDBNewGoLevelDB(t *testing.T) { name := fmt.Sprintf("test_%x", cmn.RandStr(12)) - // Test write locks - db, err := NewGoLevelDB(name, "") + defer cleanupDBDir("", name) + + // Test we can't open the db twice for writing + wr1, err := NewGoLevelDB(name, "") require.Nil(t, err) - defer os.RemoveAll("./" + name + ".db") _, err = NewGoLevelDB(name, "") require.NotNil(t, err) - db.Close() // Close the db to release the lock + wr1.Close() // Close the db to release the lock - // Open the db twice in a row to test read-only locks + // Test we can open the db twice for reading only ro1, err := NewGoLevelDBWithOpts(name, "", &opt.Options{ReadOnly: true}) defer ro1.Close() require.Nil(t, err) @@ -32,75 +30,16 @@ func TestNewGoLevelDB(t *testing.T) { require.Nil(t, err) } -func BenchmarkRandomReadsWrites(b *testing.B) { - b.StopTimer() - - numItems := int64(1000000) - internal := map[int64]int64{} - for i := 0; i < int(numItems); i++ { - internal[int64(i)] = int64(0) - } - db, err := NewGoLevelDB(fmt.Sprintf("test_%x", cmn.RandStr(12)), "") +func BenchmarkGoLevelDBRandomReadsWrites(b *testing.B) { + name := fmt.Sprintf("test_%x", cmn.RandStr(12)) + db, err := NewGoLevelDB(name, "") if err != nil { - b.Fatal(err.Error()) - return + b.Fatal(err) } + defer func() { + db.Close() + cleanupDBDir("", name) + }() - fmt.Println("ok, starting") - b.StartTimer() - - for i := 0; i < b.N; i++ { - // Write something - { - idx := (int64(cmn.RandInt()) % numItems) - internal[idx]++ - val := internal[idx] - idxBytes := int642Bytes(int64(idx)) - valBytes := int642Bytes(int64(val)) - //fmt.Printf("Set %X -> %X\n", idxBytes, valBytes) - db.Set( - idxBytes, - valBytes, - ) - } - // Read something - { - idx := (int64(cmn.RandInt()) % numItems) - val := internal[idx] - idxBytes := int642Bytes(int64(idx)) - valBytes := db.Get(idxBytes) - //fmt.Printf("Get %X -> %X\n", idxBytes, valBytes) - if val == 0 { - if !bytes.Equal(valBytes, nil) { - b.Errorf("Expected %v for %v, got %X", - nil, idx, valBytes) - break - } - } else { - if len(valBytes) != 8 { - b.Errorf("Expected length 8 for %v, got %X", - idx, valBytes) - break - } - valGot := bytes2Int64(valBytes) - if val != valGot { - b.Errorf("Expected %v for %v, got %v", - val, idx, valGot) - break - } - } - } - } - - db.Close() -} - -func int642Bytes(i int64) []byte { - buf := make([]byte, 8) - binary.BigEndian.PutUint64(buf, uint64(i)) - return buf -} - -func bytes2Int64(buf []byte) int64 { - return int64(binary.BigEndian.Uint64(buf)) + benchmarkRandomReadsWrites(b, db) } diff --git a/libs/db/mem_db.go b/libs/db/mem_db.go index ff516bc7d..fc567577d 100644 --- a/libs/db/mem_db.go +++ b/libs/db/mem_db.go @@ -7,7 +7,7 @@ import ( ) func init() { - registerDBCreator(MemDBBackend, func(name string, dir string) (DB, error) { + registerDBCreator(MemDBBackend, func(name, dir string) (DB, error) { return NewMemDB(), nil }, false) } diff --git a/libs/db/util_test.go b/libs/db/util_test.go index 07f9dd23e..39a02160c 100644 --- a/libs/db/util_test.go +++ b/libs/db/util_test.go @@ -22,6 +22,11 @@ func TestPrefixIteratorNoMatchNil(t *testing.T) { // Empty iterator for db populated after iterator created. func TestPrefixIteratorNoMatch1(t *testing.T) { for backend := range backends { + if backend == BoltDBBackend { + t.Log("bolt does not support concurrent writes while iterating") + continue + } + t.Run(fmt.Sprintf("Prefix w/ backend %s", backend), func(t *testing.T) { db, dir := newTempDB(t, backend) defer os.RemoveAll(dir) From 60b833403c2f4af0d9a4ab6f535f073f81f22653 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 6 May 2019 21:37:41 +0400 Subject: [PATCH 195/259] libs/db: close boltDBIterator (#3627) Refs https://github.com/tendermint/tendermint/pull/3610#discussion_r281201274 If we do not close, other txs will be stuck forever. --- libs/db/boltdb.go | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index 6a31f11a9..d5b6f5864 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -190,31 +190,31 @@ func (bdb *boltDBBatch) WriteSync() { func (bdb *boltDBBatch) Close() {} -// WARNING: Any concurrent writes (Set, SetSync) will block until the Iterator -// is closed. +// WARNING: Any concurrent writes or reads will block until the iterator is +// closed. func (bdb *BoltDB) Iterator(start, end []byte) Iterator { tx, err := bdb.db.Begin(false) if err != nil { panic(err) } - c := tx.Bucket(bucket).Cursor() - return newBoltDBIterator(c, start, end, false) + return newBoltDBIterator(tx, start, end, false) } -// WARNING: Any concurrent writes (Set, SetSync) will block until the Iterator -// is closed. +// WARNING: Any concurrent writes or reads will block until the iterator is +// closed. func (bdb *BoltDB) ReverseIterator(start, end []byte) Iterator { tx, err := bdb.db.Begin(false) if err != nil { panic(err) } - c := tx.Bucket(bucket).Cursor() - return newBoltDBIterator(c, start, end, true) + return newBoltDBIterator(tx, start, end, true) } // boltDBIterator allows you to iterate on range of keys/values given some // start / end keys (nil & nil will result in doing full scan). type boltDBIterator struct { + tx *bbolt.Tx + itr *bbolt.Cursor start []byte end []byte @@ -226,7 +226,9 @@ type boltDBIterator struct { isReverse bool } -func newBoltDBIterator(itr *bbolt.Cursor, start, end []byte, isReverse bool) *boltDBIterator { +func newBoltDBIterator(tx *bbolt.Tx, start, end []byte, isReverse bool) *boltDBIterator { + itr := tx.Bucket(bucket).Cursor() + var ck, cv []byte if isReverse { if end == nil { @@ -244,6 +246,7 @@ func newBoltDBIterator(itr *bbolt.Cursor, start, end []byte, isReverse bool) *bo } return &boltDBIterator{ + tx: tx, itr: itr, start: start, end: end, @@ -304,8 +307,12 @@ func (itr *boltDBIterator) Value() []byte { return itr.currentValue } -// boltdb cursor has no close op. -func (itr *boltDBIterator) Close() {} +func (itr *boltDBIterator) Close() { + err := itr.tx.Rollback() + if err != nil { + panic(err) + } +} func (itr *boltDBIterator) assertIsValid() { if !itr.Valid() { From d0d9ef16f76a5845557cf3189dd6eef803a38aed Mon Sep 17 00:00:00 2001 From: Leo Date: Mon, 6 May 2019 20:10:39 +0000 Subject: [PATCH 196/259] libs/db: fix boltdb batching `[]byte` is unhashable and needs to be casted to a string. See https://github.com/tendermint/tendermint/pull/3610#discussion_r280995538 Ref https://github.com/tendermint/tendermint/issues/3626 --- libs/db/boltdb.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index d5b6f5864..7ee988d2e 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -161,11 +161,11 @@ func (bdb *BoltDB) NewBatch() Batch { } func (bdb *boltDBBatch) Set(key, value []byte) { - bdb.buffer.Store(key, value) + bdb.buffer.Store(string(key), value) } func (bdb *boltDBBatch) Delete(key []byte) { - bdb.buffer.Delete(key) + bdb.buffer.Delete(string(key)) } // NOTE: the operation is synchronous (see BoltDB for reasons) @@ -174,7 +174,7 @@ func (bdb *boltDBBatch) Write() { b := tx.Bucket(bucket) var putErr error bdb.buffer.Range(func(key, value interface{}) bool { - putErr = b.Put(key.([]byte), value.([]byte)) + putErr = b.Put([]byte(key.(string)), value.([]byte)) return putErr == nil // stop if putErr is not nil }) return putErr From 1e073817de69eca661d99b024810e6d31ff0b50e Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 7 May 2019 11:09:06 +0400 Subject: [PATCH 197/259] rpc: /dial_peers: only mark peers as persistent if flag is on (#3620) ## Description also handle errors from DialPeersAsync remove nil addr from log msg fix TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode This is a follow-up from #3593 (review) Fixes most of the #3617, except #3593 (comment) ## Commits * rpc: /dial_peers: only mark peers as persistent if flag is on also - handle errors from DialPeersAsync - remove nil addr from log msg - fix TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode This is a follow-up from https://github.com/tendermint/tendermint/pull/3593#pullrequestreview-233556909 * remove a call to AddPersistentPeers TestDialFail will trigger a reconnect --- node/node.go | 6 ++++-- p2p/pex/pex_reactor_test.go | 6 +++++- p2p/switch.go | 2 +- p2p/switch_test.go | 3 +-- rpc/core/net.go | 9 ++++++--- 5 files changed, 17 insertions(+), 9 deletions(-) diff --git a/node/node.go b/node/node.go index 1221e8b55..2d7b3b3bb 100644 --- a/node/node.go +++ b/node/node.go @@ -682,8 +682,10 @@ func (n *Node) OnStart() error { } // Always connect to persistent peers - // parsing errors are handled above by AddPersistentPeers - _ = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " ")) + err = n.sw.DialPeersAsync(splitAndTrimEmpty(n.config.P2P.PersistentPeers, ",", " ")) + if err != nil { + return errors.Wrap(err, "could not dial peers from persistent_peers field") + } return nil } diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index 8c52a25ee..b16a0d919 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -330,7 +330,8 @@ func TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode(t *testing.T) { require.Nil(t, err) defer os.RemoveAll(dir) // nolint: errcheck - pexR, book := createReactor(&PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 1 * time.Millisecond}) + pexRConfig := &PEXReactorConfig{SeedMode: true, SeedDisconnectWaitPeriod: 1 * time.Millisecond} + pexR, book := createReactor(pexRConfig) defer teardownReactor(book) sw := createSwitchAndAddReactors(pexR) @@ -353,6 +354,9 @@ func TestPEXReactorDoesNotDisconnectFromPersistentPeerInSeedMode(t *testing.T) { assert.Equal(t, 1, sw.Peers().Size()) assert.True(t, sw.Peers().Has(peerSwitch.NodeInfo().ID())) + // sleep for SeedDisconnectWaitPeriod + time.Sleep(pexRConfig.SeedDisconnectWaitPeriod + 1*time.Millisecond) + // 2. attemptDisconnects should not disconnect because the peer is persistent pexR.attemptDisconnects() assert.Equal(t, 1, sw.Peers().Size()) diff --git a/p2p/switch.go b/p2p/switch.go index ccb7119d2..66e90bec5 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -308,7 +308,7 @@ func (sw *Switch) StopPeerForError(peer Peer, reason interface{}) { addr, err = peer.NodeInfo().NetAddress() if err != nil { sw.Logger.Error("Wanted to reconnect to inbound peer, but self-reported address is wrong", - "peer", peer, "addr", addr, "err", err) + "peer", peer, "err", err) return } } diff --git a/p2p/switch_test.go b/p2p/switch_test.go index 6c7538b51..8485a759f 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -410,7 +410,6 @@ func TestSwitchReconnectsToOutboundPersistentPeer(t *testing.T) { err = sw.DialPeerWithAddress(rp.Addr()) require.Nil(t, err) - time.Sleep(50 * time.Millisecond) require.NotNil(t, sw.Peers().Get(rp.ID())) p := sw.Peers().List()[0] @@ -432,7 +431,7 @@ func TestSwitchReconnectsToOutboundPersistentPeer(t *testing.T) { defer rp.Stop() conf := config.DefaultP2PConfig() - conf.TestDialFail = true + conf.TestDialFail = true // will trigger a reconnect err = sw.addOutboundPeerWithConfig(rp.Addr(), conf) require.NotNil(t, err) // DialPeerWithAddres - sw.peerConfig resets the dialer diff --git a/rpc/core/net.go b/rpc/core/net.go index 48bf576a8..165230619 100644 --- a/rpc/core/net.go +++ b/rpc/core/net.go @@ -196,11 +196,14 @@ func UnsafeDialPeers(ctx *rpctypes.Context, peers []string, persistent bool) (*c return &ctypes.ResultDialPeers{}, errors.New("No peers provided") } logger.Info("DialPeers", "peers", peers, "persistent", persistent) - if err := p2pPeers.AddPersistentPeers(peers); err != nil { + if persistent { + if err := p2pPeers.AddPersistentPeers(peers); err != nil { + return &ctypes.ResultDialPeers{}, err + } + } + if err := p2pPeers.DialPeersAsync(peers); err != nil { return &ctypes.ResultDialPeers{}, err } - // parsing errors are handled above by AddPersistentPeers - _ = p2pPeers.DialPeersAsync(peers) return &ctypes.ResultDialPeers{Log: "Dialing peers in progress. See /net_info for details"}, nil } From 27909e5d2aee605a7cb562a53195b2e99d78eb8e Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 7 May 2019 12:25:35 +0400 Subject: [PATCH 198/259] mempool: remove only valid (Code==0) txs on Update (#3625) * mempool: remove only valid (Code==0) txs on Update so evil proposers can't drop valid txs in Commit stage. Also remove invalid (Code!=0) txs from the cache so they can be resubmitted. Fixes #3322 @rickyyangz: In the end of commit stage, we will update mempool to remove all the txs in current block. // Update mempool. err = blockExec.mempool.Update( block.Height, block.Txs, TxPreCheck(state), TxPostCheck(state), ) Assum an account has 3 transactions in the mempool, the sequences are 100, 101 and 102 separately, So an evil proposal can only package the 101 and 102 transactions into its proposal block, and leave 100 still in mempool, then the two txs will be removed from all validators' mempool when commit. So the account lost the two valid txs. @ebuchman: In the longer term we may want to do something like #2639 so we can validate txs before we commit the block. But even in this case we'd only want to run the equivalent of CheckTx, which means the DeliverTx could still fail even if the CheckTx passes depending on how the app handles the ABCI Code semantics. So more work will be required around the ABCI code. See also #2185 * add changelog entry and tests * improve changelog message * reformat code --- CHANGELOG_PENDING.md | 4 ++ mempool/cache_test.go | 3 +- mempool/clist_mempool.go | 70 +++++++++++++++++------------------ mempool/clist_mempool_test.go | 50 +++++++++++++++++++------ mempool/mempool.go | 2 +- mock/mempool.go | 1 + state/execution.go | 4 +- 7 files changed, 84 insertions(+), 50 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 3b6dffb3a..d03d98112 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -48,3 +48,7 @@ * `Switch#DialPeerWithAddress` now only takes an address - [consensus] \#3067 getBeginBlockValidatorInfo loads validators from stateDB instead of state (@james-ray) - [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) +- [mempool] \#3322 Remove only valid (Code==0) txs on Update + * `Mempool#Update` and `BlockExecutor#Commit` now accept + `[]*abci.ResponseDeliverTx` - list of `DeliverTx` responses, which should + match `block.Txs` diff --git a/mempool/cache_test.go b/mempool/cache_test.go index ea9f63fd6..539bf1197 100644 --- a/mempool/cache_test.go +++ b/mempool/cache_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/abci/example/kvstore" + abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/proxy" "github.com/tendermint/tendermint/types" ) @@ -66,7 +67,7 @@ func TestCacheAfterUpdate(t *testing.T) { tx := types.Tx{byte(v)} updateTxs = append(updateTxs, tx) } - mempool.Update(int64(tcIndex), updateTxs, nil, nil) + mempool.Update(int64(tcIndex), updateTxs, abciResponses(len(updateTxs), abci.CodeTypeOK), nil, nil) for _, v := range tc.reAddIndices { tx := types.Tx{byte(v)} diff --git a/mempool/clist_mempool.go b/mempool/clist_mempool.go index 2a4bacbbb..c6ff475aa 100644 --- a/mempool/clist_mempool.go +++ b/mempool/clist_mempool.go @@ -519,6 +519,7 @@ func (mem *CListMempool) ReapMaxTxs(max int) types.Txs { func (mem *CListMempool) Update( height int64, txs types.Txs, + deliverTxResponses []*abci.ResponseDeliverTx, preCheck PreCheckFunc, postCheck PostCheckFunc, ) error { @@ -533,20 +534,37 @@ func (mem *CListMempool) Update( mem.postCheck = postCheck } - // Add committed transactions to cache (if missing). - for _, tx := range txs { - _ = mem.cache.Push(tx) - } + for i, tx := range txs { + if deliverTxResponses[i].Code == abci.CodeTypeOK { + // Add valid committed tx to the cache (if missing). + _ = mem.cache.Push(tx) - // Remove committed transactions. - txsLeft := mem.removeTxs(txs) + // Remove valid committed tx from the mempool. + if e, ok := mem.txsMap.Load(txKey(tx)); ok { + mem.removeTx(tx, e.(*clist.CElement), false) + } + } else { + // Allow invalid transactions to be resubmitted. + mem.cache.Remove(tx) + + // Don't remove invalid tx from the mempool. + // Otherwise evil proposer can drop valid txs. + // Example: + // 100 -> 101 -> 102 + // Block, proposed by evil proposer: + // 101 -> 102 + // Mempool (if you remove txs): + // 100 + // https://github.com/tendermint/tendermint/issues/3322. + } + } // Either recheck non-committed txs to see if they became invalid // or just notify there're some txs left. - if len(txsLeft) > 0 { + if mem.Size() > 0 { if mem.config.Recheck { - mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height) - mem.recheckTxs(txsLeft) + mem.logger.Info("Recheck txs", "numtxs", mem.Size(), "height", height) + mem.recheckTxs() // At this point, mem.txs are being rechecked. // mem.recheckCursor re-scans mem.txs and possibly removes some txs. // Before mem.Reap(), we should wait for mem.recheckCursor to be nil. @@ -561,42 +579,22 @@ func (mem *CListMempool) Update( return nil } -func (mem *CListMempool) removeTxs(txs types.Txs) []types.Tx { - // Build a map for faster lookups. - txsMap := make(map[string]struct{}, len(txs)) - for _, tx := range txs { - txsMap[string(tx)] = struct{}{} +func (mem *CListMempool) recheckTxs() { + if mem.Size() == 0 { + panic("recheckTxs is called, but the mempool is empty") } - txsLeft := make([]types.Tx, 0, mem.txs.Len()) - for e := mem.txs.Front(); e != nil; e = e.Next() { - memTx := e.Value.(*mempoolTx) - // Remove the tx if it's already in a block. - if _, ok := txsMap[string(memTx.tx)]; ok { - // NOTE: we don't remove committed txs from the cache. - mem.removeTx(memTx.tx, e, false) - - continue - } - txsLeft = append(txsLeft, memTx.tx) - } - return txsLeft -} - -// NOTE: pass in txs because mem.txs can mutate concurrently. -func (mem *CListMempool) recheckTxs(txs []types.Tx) { - if len(txs) == 0 { - return - } atomic.StoreInt32(&mem.rechecking, 1) mem.recheckCursor = mem.txs.Front() mem.recheckEnd = mem.txs.Back() // Push txs to proxyAppConn // NOTE: globalCb may be called concurrently. - for _, tx := range txs { - mem.proxyAppConn.CheckTxAsync(tx) + for e := mem.txs.Front(); e != nil; e = e.Next() { + memTx := e.Value.(*mempoolTx) + mem.proxyAppConn.CheckTxAsync(memTx.tx) } + mem.proxyAppConn.FlushAsync() } diff --git a/mempool/clist_mempool_test.go b/mempool/clist_mempool_test.go index 69d4eb68c..3eb4990b6 100644 --- a/mempool/clist_mempool_test.go +++ b/mempool/clist_mempool_test.go @@ -170,22 +170,42 @@ func TestMempoolFilters(t *testing.T) { {10, PreCheckAminoMaxBytes(22), PostCheckMaxGas(0), 0}, } for tcIndex, tt := range tests { - mempool.Update(1, emptyTxArr, tt.preFilter, tt.postFilter) + mempool.Update(1, emptyTxArr, abciResponses(len(emptyTxArr), abci.CodeTypeOK), tt.preFilter, tt.postFilter) checkTxs(t, mempool, tt.numTxsToCreate, UnknownPeerID) require.Equal(t, tt.expectedNumTxs, mempool.Size(), "mempool had the incorrect size, on test case %d", tcIndex) mempool.Flush() } } -func TestMempoolUpdateAddsTxsToCache(t *testing.T) { +func TestMempoolUpdate(t *testing.T) { app := kvstore.NewKVStoreApplication() cc := proxy.NewLocalClientCreator(app) mempool, cleanup := newMempoolWithApp(cc) defer cleanup() - mempool.Update(1, []types.Tx{[]byte{0x01}}, nil, nil) - err := mempool.CheckTx([]byte{0x01}, nil) - if assert.Error(t, err) { - assert.Equal(t, ErrTxInCache, err) + + // 1. Adds valid txs to the cache + { + mempool.Update(1, []types.Tx{[]byte{0x01}}, abciResponses(1, abci.CodeTypeOK), nil, nil) + err := mempool.CheckTx([]byte{0x01}, nil) + if assert.Error(t, err) { + assert.Equal(t, ErrTxInCache, err) + } + } + + // 2. Removes valid txs from the mempool + { + err := mempool.CheckTx([]byte{0x02}, nil) + require.NoError(t, err) + mempool.Update(1, []types.Tx{[]byte{0x02}}, abciResponses(1, abci.CodeTypeOK), nil, nil) + assert.Zero(t, mempool.Size()) + } + + // 3. Removes invalid transactions from the cache, but leaves them in the mempool (if present) + { + err := mempool.CheckTx([]byte{0x03}, nil) + require.NoError(t, err) + mempool.Update(1, []types.Tx{[]byte{0x03}}, abciResponses(1, 1), nil, nil) + assert.Equal(t, 1, mempool.Size()) } } @@ -210,7 +230,7 @@ func TestTxsAvailable(t *testing.T) { // it should fire once now for the new height // since there are still txs left committedTxs, txs := txs[:50], txs[50:] - if err := mempool.Update(1, committedTxs, nil, nil); err != nil { + if err := mempool.Update(1, committedTxs, abciResponses(len(committedTxs), abci.CodeTypeOK), nil, nil); err != nil { t.Error(err) } ensureFire(t, mempool.TxsAvailable(), timeoutMS) @@ -222,7 +242,7 @@ func TestTxsAvailable(t *testing.T) { // now call update with all the txs. it should not fire as there are no txs left committedTxs = append(txs, moreTxs...) - if err := mempool.Update(2, committedTxs, nil, nil); err != nil { + if err := mempool.Update(2, committedTxs, abciResponses(len(committedTxs), abci.CodeTypeOK), nil, nil); err != nil { t.Error(err) } ensureNoFire(t, mempool.TxsAvailable(), timeoutMS) @@ -281,7 +301,7 @@ func TestSerialReap(t *testing.T) { binary.BigEndian.PutUint64(txBytes, uint64(i)) txs = append(txs, txBytes) } - if err := mempool.Update(0, txs, nil, nil); err != nil { + if err := mempool.Update(0, txs, abciResponses(len(txs), abci.CodeTypeOK), nil, nil); err != nil { t.Error(err) } } @@ -462,7 +482,7 @@ func TestMempoolTxsBytes(t *testing.T) { assert.EqualValues(t, 1, mempool.TxsBytes()) // 3. zero again after tx is removed by Update - mempool.Update(1, []types.Tx{[]byte{0x01}}, nil, nil) + mempool.Update(1, []types.Tx{[]byte{0x01}}, abciResponses(1, abci.CodeTypeOK), nil, nil) assert.EqualValues(t, 0, mempool.TxsBytes()) // 4. zero after Flush @@ -507,7 +527,7 @@ func TestMempoolTxsBytes(t *testing.T) { require.NotEmpty(t, res2.Data) // Pretend like we committed nothing so txBytes gets rechecked and removed. - mempool.Update(1, []types.Tx{}, nil, nil) + mempool.Update(1, []types.Tx{}, abciResponses(0, abci.CodeTypeOK), nil, nil) assert.EqualValues(t, 0, mempool.TxsBytes()) } @@ -570,3 +590,11 @@ func checksumFile(p string, t *testing.T) string { require.Nil(t, err, "expecting successful read of %q", p) return checksumIt(data) } + +func abciResponses(n int, code uint32) []*abci.ResponseDeliverTx { + responses := make([]*abci.ResponseDeliverTx, 0, n) + for i := 0; i < n; i++ { + responses = append(responses, &abci.ResponseDeliverTx{Code: code}) + } + return responses +} diff --git a/mempool/mempool.go b/mempool/mempool.go index 1c1873d47..0995c734f 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -43,7 +43,7 @@ type Mempool interface { // Update informs the mempool that the given txs were committed and can be discarded. // NOTE: this should be called *after* block is committed by consensus. // NOTE: unsafe; Lock/Unlock must be managed by caller - Update(blockHeight int64, blockTxs types.Txs, newPreFn PreCheckFunc, newPostFn PostCheckFunc) error + Update(blockHeight int64, blockTxs types.Txs, deliverTxResponses []*abci.ResponseDeliverTx, newPreFn PreCheckFunc, newPostFn PostCheckFunc) error // FlushAppConn flushes the mempool connection to ensure async reqResCb calls are // done. E.g. from CheckTx. diff --git a/mock/mempool.go b/mock/mempool.go index 66ad68d30..cebe156ba 100644 --- a/mock/mempool.go +++ b/mock/mempool.go @@ -27,6 +27,7 @@ func (Mempool) ReapMaxTxs(n int) types.Txs { return types.Txs{} } func (Mempool) Update( _ int64, _ types.Txs, + _ []*abci.ResponseDeliverTx, _ mempl.PreCheckFunc, _ mempl.PostCheckFunc, ) error { diff --git a/state/execution.go b/state/execution.go index d872145ef..7e49a9ad8 100644 --- a/state/execution.go +++ b/state/execution.go @@ -156,7 +156,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b } // Lock mempool, commit app state, update mempoool. - appHash, err := blockExec.Commit(state, block) + appHash, err := blockExec.Commit(state, block, abciResponses.DeliverTx) if err != nil { return state, fmt.Errorf("Commit failed for application: %v", err) } @@ -188,6 +188,7 @@ func (blockExec *BlockExecutor) ApplyBlock(state State, blockID types.BlockID, b func (blockExec *BlockExecutor) Commit( state State, block *types.Block, + deliverTxResponses []*abci.ResponseDeliverTx, ) ([]byte, error) { blockExec.mempool.Lock() defer blockExec.mempool.Unlock() @@ -222,6 +223,7 @@ func (blockExec *BlockExecutor) Commit( err = blockExec.mempool.Update( block.Height, block.Txs, + deliverTxResponses, TxPreCheck(state), TxPostCheck(state), ) From a7358bc69f63a9e8afdb85df1f3f3080965c1f38 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 7 May 2019 12:33:47 +0400 Subject: [PATCH 199/259] libs/db: conditional compilation (#3628) * libs/db: conditional compilation For cleveldb: go build -tags cleveldb For boltdb: go build -tags boltdb Fixes #3611 * document db_backend param better * remove deprecated LevelDBBackend * update changelog * add missing lines * add new line * fix TestRemoteDB * add a line about boltdb tag * Revert "remove deprecated LevelDBBackend" This reverts commit 1aa85453f76605e0c4d967601bbe26240e668d51. * make PR non breaking * change DEPRECATED label format https://stackoverflow.com/a/36360323/820520 --- CHANGELOG_PENDING.md | 5 ++++ Makefile | 6 ++--- config/config.go | 15 ++++++++++-- config/toml.go | 13 +++++++++- docs/introduction/install.md | 12 ++++----- docs/tendermint-core/configuration.md | 13 +++++++++- libs/db/backend_test.go | 1 + libs/db/boltdb.go | 2 ++ libs/db/boltdb_test.go | 2 ++ libs/db/c_level_db.go | 2 +- libs/db/c_level_db_test.go | 6 ++--- libs/db/db.go | 35 +++++++++++++++++++++------ libs/db/remotedb/remotedb_test.go | 2 +- lite/proxy/verifier.go | 2 +- 14 files changed, 88 insertions(+), 28 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d03d98112..0067c8c15 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -38,6 +38,11 @@ - [rpc] \#3362 `/dial_seeds` & `/dial_peers` return errors if addresses are incorrect (except when IP lookup fails) - [node] \#3362 returns an error if `persistent_peers` list is invalid (except when IP lookup fails) - [p2p] \#3531 Terminate session on nonce wrapping (@climber73) +- [libs/db] \#3611 Conditional compilation + * Use `cleveldb` tag instead of `gcc` to compile Tendermint with CLevelDB or + use `make build_c` / `make install_c` (full instructions can be found at + https://tendermint.com/docs/introduction/install.html#compile-with-cleveldb-support) + * Use `boltdb` tag to compile Tendermint with bolt db ### BUG FIXES: - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode diff --git a/Makefile b/Makefile index 7c2ce1d9c..c5bde692d 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ build: CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint/ build_c: - CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" -o $(OUTPUT) ./cmd/tendermint/ + CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" -o $(OUTPUT) ./cmd/tendermint/ build_race: CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint @@ -32,7 +32,7 @@ install: CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint install_c: - CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" ./cmd/tendermint + CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint ######################################## ### Protobuf @@ -132,7 +132,7 @@ clean_certs: rm -f db/remotedb/::.crt db/remotedb/::.key test_libs: gen_certs - go test -tags gcc $(PACKAGES) + go test -tags clevedb boltdb $(PACKAGES) make clean_certs grpc_dbserver: diff --git a/config/config.go b/config/config.go index 3ac22adbf..6c4654fed 100644 --- a/config/config.go +++ b/config/config.go @@ -153,7 +153,18 @@ type BaseConfig struct { // and verifying their commits FastSync bool `mapstructure:"fast_sync"` - // Database backend: leveldb | memdb | cleveldb + // Database backend: goleveldb | cleveldb | boltdb + // * goleveldb (github.com/syndtr/goleveldb - most popular implementation) + // - pure go + // - stable + // * cleveldb (uses levigo wrapper) + // - fast + // - requires gcc + // - use cleveldb build tag (go build -tags cleveldb) + // * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) + // - EXPERIMENTAL + // - may be faster is some use-cases (random reads - indexer) + // - use boltdb build tag (go build -tags boltdb) DBBackend string `mapstructure:"db_backend"` // Database directory @@ -207,7 +218,7 @@ func DefaultBaseConfig() BaseConfig { ProfListenAddress: "", FastSync: true, FilterPeers: false, - DBBackend: "leveldb", + DBBackend: "goleveldb", DBPath: "data", } } diff --git a/config/toml.go b/config/toml.go index f80f525e4..b1541c05a 100644 --- a/config/toml.go +++ b/config/toml.go @@ -81,7 +81,18 @@ moniker = "{{ .BaseConfig.Moniker }}" # and verifying their commits fast_sync = {{ .BaseConfig.FastSync }} -# Database backend: leveldb | memdb | cleveldb +# Database backend: goleveldb | cleveldb | boltdb +# * goleveldb (github.com/syndtr/goleveldb - most popular implementation) +# - pure go +# - stable +# * cleveldb (uses levigo wrapper) +# - fast +# - requires gcc +# - use cleveldb build tag (go build -tags cleveldb) +# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) +# - EXPERIMENTAL +# - may be faster is some use-cases (random reads - indexer) +# - use boltdb build tag (go build -tags boltdb) db_backend = "{{ .BaseConfig.DBBackend }}" # Database directory diff --git a/docs/introduction/install.md b/docs/introduction/install.md index 3005a7349..e96135826 100644 --- a/docs/introduction/install.md +++ b/docs/introduction/install.md @@ -79,9 +79,7 @@ make install Install [LevelDB](https://github.com/google/leveldb) (minimum version is 1.7). -### Ubuntu - -Install LevelDB with snappy (optionally): +Install LevelDB with snappy (optionally). Below are commands for Ubuntu: ``` sudo apt-get update @@ -100,23 +98,23 @@ wget https://github.com/google/leveldb/archive/v1.20.tar.gz && \ rm -f v1.20.tar.gz ``` -Set database backend to cleveldb: +Set a database backend to `cleveldb`: ``` # config/config.toml db_backend = "cleveldb" ``` -To install Tendermint, run +To install Tendermint, run: ``` CGO_LDFLAGS="-lsnappy" make install_c ``` -or run +or run: ``` CGO_LDFLAGS="-lsnappy" make build_c ``` -to put the binary in `./build`. +which puts the binary in `./build`. diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index d19c272fc..cdbdf1fe2 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -30,7 +30,18 @@ moniker = "anonymous" # and verifying their commits fast_sync = true -# Database backend: leveldb | memdb | cleveldb +# Database backend: goleveldb | cleveldb | boltdb +# * goleveldb (github.com/syndtr/goleveldb - most popular implementation) +# - pure go +# - stable +# * cleveldb (uses levigo wrapper) +# - fast +# - requires gcc +# - use cleveldb build tag (go build -tags cleveldb) +# * boltdb (uses etcd's fork of bolt - github.com/etcd-io/bbolt) +# - EXPERIMENTAL +# - may be faster is some use-cases (random reads - indexer) +# - use boltdb build tag (go build -tags boltdb) db_backend = "leveldb" # Database directory diff --git a/libs/db/backend_test.go b/libs/db/backend_test.go index fb2a3d0b3..d755a6f27 100644 --- a/libs/db/backend_test.go +++ b/libs/db/backend_test.go @@ -9,6 +9,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + cmn "github.com/tendermint/tendermint/libs/common" ) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index 7ee988d2e..f53d2785c 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -1,3 +1,5 @@ +// +build boltdb + package db import ( diff --git a/libs/db/boltdb_test.go b/libs/db/boltdb_test.go index ced57d2e0..416a8fd03 100644 --- a/libs/db/boltdb_test.go +++ b/libs/db/boltdb_test.go @@ -1,3 +1,5 @@ +// +build boltdb + package db import ( diff --git a/libs/db/c_level_db.go b/libs/db/c_level_db.go index 116e51bc2..0ac040d20 100644 --- a/libs/db/c_level_db.go +++ b/libs/db/c_level_db.go @@ -1,4 +1,4 @@ -// +build gcc +// +build cleveldb package db diff --git a/libs/db/c_level_db_test.go b/libs/db/c_level_db_test.go index e71dee0c1..1c10fcdef 100644 --- a/libs/db/c_level_db_test.go +++ b/libs/db/c_level_db_test.go @@ -1,4 +1,4 @@ -// +build gcc +// +build cleveldb package db @@ -93,7 +93,7 @@ func TestCLevelDBBackend(t *testing.T) { // Can't use "" (current directory) or "./" here because levigo.Open returns: // "Error initializing DB: IO error: test_XXX.db: Invalid argument" dir := os.TempDir() - db := NewDB(name, LevelDBBackend, dir) + db := NewDB(name, CLevelDBBackend, dir) defer cleanupDBDir(dir, name) _, ok := db.(*CLevelDB) @@ -103,7 +103,7 @@ func TestCLevelDBBackend(t *testing.T) { func TestCLevelDBStats(t *testing.T) { name := fmt.Sprintf("test_%x", cmn.RandStr(12)) dir := os.TempDir() - db := NewDB(name, LevelDBBackend, dir) + db := NewDB(name, CLevelDBBackend, dir) defer cleanupDBDir(dir, name) assert.NotEmpty(t, db.Stats()) diff --git a/libs/db/db.go b/libs/db/db.go index 43b788281..b68204420 100644 --- a/libs/db/db.go +++ b/libs/db/db.go @@ -5,18 +5,37 @@ import ( "strings" ) -//---------------------------------------- -// Main entry - type DBBackendType string +// These are valid backend types. const ( - LevelDBBackend DBBackendType = "leveldb" // legacy, defaults to goleveldb unless +gcc - CLevelDBBackend DBBackendType = "cleveldb" + // LevelDBBackend is a legacy type. Defaults to goleveldb unless cleveldb + // build tag was used, in which it becomes cleveldb. + // Deprecated: Use concrete types (golevedb, cleveldb, etc.) + LevelDBBackend DBBackendType = "leveldb" + // GoLevelDBBackend represents goleveldb (github.com/syndtr/goleveldb - most + // popular implementation) + // - pure go + // - stable GoLevelDBBackend DBBackendType = "goleveldb" - MemDBBackend DBBackendType = "memdb" - FSDBBackend DBBackendType = "fsdb" // using the filesystem naively - BoltDBBackend DBBackendType = "boltdb" + // CLevelDBBackend represents cleveldb (uses levigo wrapper) + // - fast + // - requires gcc + // - use cleveldb build tag (go build -tags cleveldb) + CLevelDBBackend DBBackendType = "cleveldb" + // MemDBBackend represents in-memoty key value store, which is mostly used + // for testing. + MemDBBackend DBBackendType = "memdb" + // FSDBBackend represents filesystem database + // - EXPERIMENTAL + // - slow + FSDBBackend DBBackendType = "fsdb" + // BoltDBBackend represents bolt (uses etcd's fork of bolt - + // github.com/etcd-io/bbolt) + // - EXPERIMENTAL + // - may be faster is some use-cases (random reads - indexer) + // - use boltdb build tag (go build -tags boltdb) + BoltDBBackend DBBackendType = "boltdb" ) type dbCreator func(name string, dir string) (DB, error) diff --git a/libs/db/remotedb/remotedb_test.go b/libs/db/remotedb/remotedb_test.go index f5c8e2cb5..43a022461 100644 --- a/libs/db/remotedb/remotedb_test.go +++ b/libs/db/remotedb/remotedb_test.go @@ -28,7 +28,7 @@ func TestRemoteDB(t *testing.T) { client, err := remotedb.NewRemoteDB(ln.Addr().String(), cert) require.Nil(t, err, "expecting a successful client creation") dbName := "test-remote-db" - require.Nil(t, client.InitRemote(&remotedb.Init{Name: dbName, Type: "leveldb"})) + require.Nil(t, client.InitRemote(&remotedb.Init{Name: dbName, Type: "goleveldb"})) defer func() { err := os.RemoveAll(dbName + ".db") if err != nil { diff --git a/lite/proxy/verifier.go b/lite/proxy/verifier.go index b7c11f18e..429c54b2d 100644 --- a/lite/proxy/verifier.go +++ b/lite/proxy/verifier.go @@ -14,7 +14,7 @@ func NewVerifier(chainID, rootDir string, client lclient.SignStatusClient, logge logger.Info("lite/proxy/NewVerifier()...", "chainID", chainID, "rootDir", rootDir, "client", client) memProvider := lite.NewDBProvider("trusted.mem", dbm.NewMemDB()).SetLimit(cacheSize) - lvlProvider := lite.NewDBProvider("trusted.lvl", dbm.NewDB("trust-base", dbm.LevelDBBackend, rootDir)) + lvlProvider := lite.NewDBProvider("trusted.lvl", dbm.NewDB("trust-base", dbm.GoLevelDBBackend, rootDir)) trust := lite.NewMultiProvider( memProvider, lvlProvider, From a076b4820219313c7b388df10c8664042d8711dc Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 7 May 2019 14:06:20 +0400 Subject: [PATCH 200/259] libs/db: boltdb: use slice instead of sync.Map (#3633) for storing batch keys and values in boltDBBatch. NOTE: batch does not have to be safe for concurrent access. Delete may be slow, but given it should not be used often, it's ok. Fixes #3631 --- libs/db/boltdb.go | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index f53d2785c..6bf474c86 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -8,7 +8,6 @@ import ( "fmt" "os" "path/filepath" - "sync" "github.com/etcd-io/bbolt" ) @@ -150,36 +149,58 @@ func (bdb *BoltDB) Stats() map[string]string { // boltDBBatch stores key values in sync.Map and dumps them to the underlying // DB upon Write call. type boltDBBatch struct { - buffer *sync.Map - db *BoltDB + buffer []struct { + k []byte + v []byte + } + db *BoltDB } // NewBatch returns a new batch. func (bdb *BoltDB) NewBatch() Batch { return &boltDBBatch{ - buffer: &sync.Map{}, - db: bdb, + buffer: make([]struct { + k []byte + v []byte + }, 0), + db: bdb, } } +// It is safe to modify the contents of the argument after Set returns but not +// before. func (bdb *boltDBBatch) Set(key, value []byte) { - bdb.buffer.Store(string(key), value) + bdb.buffer = append(bdb.buffer, struct { + k []byte + v []byte + }{ + key, value, + }) } +// It is safe to modify the contents of the argument after Delete returns but +// not before. func (bdb *boltDBBatch) Delete(key []byte) { - bdb.buffer.Delete(string(key)) + for i, elem := range bdb.buffer { + if bytes.Equal(elem.k, key) { + // delete without preserving order + bdb.buffer[i] = bdb.buffer[len(bdb.buffer)-1] + bdb.buffer = bdb.buffer[:len(bdb.buffer)-1] + return + } + } } // NOTE: the operation is synchronous (see BoltDB for reasons) func (bdb *boltDBBatch) Write() { err := bdb.db.db.Batch(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) - var putErr error - bdb.buffer.Range(func(key, value interface{}) bool { - putErr = b.Put([]byte(key.(string)), value.([]byte)) - return putErr == nil // stop if putErr is not nil - }) - return putErr + for _, elem := range bdb.buffer { + if putErr := b.Put(elem.k, elem.v); putErr != nil { + return putErr + } + } + return nil }) if err != nil { panic(err) From 6f1ccb6c4959be3a29ddcf9a3584bc9305a5e4c5 Mon Sep 17 00:00:00 2001 From: Zarko Milosevic Date: Fri, 10 May 2019 19:37:21 +0200 Subject: [PATCH 201/259] [ADR] ADR-36: blockchain reactor refactor spec (#3506) * Add blockchain reactor refactor ADR * docs: Correct markdown and go syntax in adr-036 * Update docs/architecture/adr-036-blockchain-reactor-refactor.md Co-Authored-By: milosevic * Add comments and address reviewer comments * Improve formating * Small improvements * adr-036 -> adr-040 --- .../adr-040-blockchain-reactor-refactor.md | 534 ++++++++++++++++++ docs/architecture/img/bc-reactor-refactor.png | Bin 0 -> 8105 bytes docs/architecture/img/bc-reactor.png | Bin 0 -> 44211 bytes docs/tendermint-core/configuration.md | 2 +- docs/tendermint-core/running-in-production.md | 2 +- 5 files changed, 536 insertions(+), 2 deletions(-) create mode 100644 docs/architecture/adr-040-blockchain-reactor-refactor.md create mode 100644 docs/architecture/img/bc-reactor-refactor.png create mode 100644 docs/architecture/img/bc-reactor.png diff --git a/docs/architecture/adr-040-blockchain-reactor-refactor.md b/docs/architecture/adr-040-blockchain-reactor-refactor.md new file mode 100644 index 000000000..520d55b5d --- /dev/null +++ b/docs/architecture/adr-040-blockchain-reactor-refactor.md @@ -0,0 +1,534 @@ +# ADR 040: Blockchain Reactor Refactor + +## Changelog + +19-03-2019: Initial draft + +## Context + +The Blockchain Reactor's high level responsibility is to enable peers who are far behind the current state of the +blockchain to quickly catch up by downloading many blocks in parallel from its peers, verifying block correctness, and +executing them against the ABCI application. We call the protocol executed by the Blockchain Reactor `fast-sync`. +The current architecture diagram of the blockchain reactor can be found here: + +![Blockchain Reactor Architecture Diagram](img/bc-reactor.png) + +The current architecture consists of dozens of routines and it is tightly depending on the `Switch`, making writing +unit tests almost impossible. Current tests require setting up complex dependency graphs and dealing with concurrency. +Note that having dozens of routines is in this case overkill as most of the time routines sits idle waiting for +something to happen (message to arrive or timeout to expire). Due to dependency on the `Switch`, testing relatively +complex network scenarios and failures (for example adding and removing peers) is very complex tasks and frequently lead +to complex tests with not deterministic behavior ([#3400]). Impossibility to write proper tests makes confidence in +the code low and this resulted in several issues (some are fixed in the meantime and some are still open): +[#3400], [#2897], [#2896], [#2699], [#2888], [#2457], [#2622], [#2026]. + +## Decision + +To remedy these issues we plan a major refactor of the blockchain reactor. The proposed architecture is largely inspired +by ADR-30 and is presented on the following diagram: +![Blockchain Reactor Refactor Diagram](img/bc-reactor-refactor.png) + +We suggest a concurrency architecture where the core algorithm (we call it `Controller`) is extracted into a finite +state machine. The active routine of the reactor is called `Executor` and is responsible for receiving and sending +messages from/to peers and triggering timeouts. What messages should be sent and timeouts triggered is determined mostly +by the `Controller`. The exception is `Peer Heartbeat` mechanism which is `Executor` responsibility. The heartbeat +mechanism is used to remove slow and unresponsive peers from the peer list. Writing of unit tests is simpler with +this architecture as most of the critical logic is part of the `Controller` function. We expect that simpler concurrency +architecture will not have significant negative effect on the performance of this reactor (to be confirmed by +experimental evaluation). + + +### Implementation changes + +We assume the following system model for "fast sync" protocol: + +* a node is connected to a random subset of all nodes that represents its peer set. Some nodes are correct and some + might be faulty. We don't make assumptions about ratio of faulty nodes, i.e., it is possible that all nodes in some + peer set are faulty. +* we assume that communication between correct nodes is synchronous, i.e., if a correct node `p` sends a message `m` to + a correct node `q` at time `t`, then `q` will receive message the latest at time `t+Delta` where `Delta` is a system + parameter that is known by network participants. `Delta` is normally chosen to be an order of magnitude higher than + the real communication delay (maximum) between correct nodes. Therefore if a correct node `p` sends a request message + to a correct node `q` at time `t` and there is no the corresponding reply at time `t + 2*Delta`, then `p` can assume + that `q` is faulty. Note that the network assumptions for the consensus reactor are different (we assume partially + synchronous model there). + +The requirements for the "fast sync" protocol are formally specified as follows: + +- `Correctness`: If a correct node `p` is connected to a correct node `q` for a long enough period of time, then `p` +- will eventually download all requested blocks from `q`. +- `Termination`: If a set of peers of a correct node `p` is stable (no new nodes are added to the peer set of `p`) for +- a long enough period of time, then protocol eventually terminates. +- `Fairness`: A correct node `p` sends requests for blocks to all peers from its peer set. + +As explained above, the `Executor` is responsible for sending and receiving messages that are part of the `fast-sync` +protocol. The following messages are exchanged as part of `fast-sync` protocol: + +``` go +type Message int +const ( + MessageUnknown Message = iota + MessageStatusRequest + MessageStatusResponse + MessageBlockRequest + MessageBlockResponse +) +``` +`MessageStatusRequest` is sent periodically to all peers as a request for a peer to provide its current height. It is +part of the `Peer Heartbeat` mechanism and a failure to respond timely to this message results in a peer being removed +from the peer set. Note that the `Peer Heartbeat` mechanism is used only while a peer is in `fast-sync` mode. We assume +here existence of a mechanism that gives node a possibility to inform its peers that it is in the `fast-sync` mode. + +``` go +type MessageStatusRequest struct { + SeqNum int64 // sequence number of the request +} +``` +`MessageStatusResponse` is sent as a response to `MessageStatusRequest` to inform requester about the peer current +height. + +``` go +type MessageStatusResponse struct { + SeqNum int64 // sequence number of the corresponding request + Height int64 // current peer height +} +``` + +`MessageBlockRequest` is used to make a request for a block and the corresponding commit certificate at a given height. + +``` go +type MessageBlockRequest struct { + Height int64 +} +``` + +`MessageBlockResponse` is a response for the corresponding block request. In addition to providing the block and the +corresponding commit certificate, it contains also a current peer height. + +``` go +type MessageBlockResponse struct { + Height int64 + Block Block + Commit Commit + PeerHeight int64 +} +``` + +In addition to sending and receiving messages, and `HeartBeat` mechanism, controller is also managing timeouts +that are triggered upon `Controller` request. `Controller` is then informed once a timeout expires. + +``` go +type TimeoutTrigger int +const ( + TimeoutUnknown TimeoutTrigger = iota + TimeoutResponseTrigger + TimeoutTerminationTrigger +) +``` + +The `Controller` can be modelled as a function with clearly defined inputs: + +* `State` - current state of the node. Contains data about connected peers and its behavior, pending requests, +* received blocks, etc. +* `Event` - significant events in the network. + +producing clear outputs: + +* `State` - updated state of the node, +* `MessageToSend` - signal what message to send and to which peer +* `TimeoutTrigger` - signal that timeout should be triggered. + + +We consider the following `Event` types: + +``` go +type Event int +const ( + EventUnknown Event = iota + EventStatusReport + EventBlockRequest + EventBlockResponse + EventRemovePeer + EventTimeoutResponse + EventTimeoutTermination +) +``` + +`EventStatusResponse` event is generated once `MessageStatusResponse` is received by the `Executor`. + +``` go +type EventStatusReport struct { + PeerID ID + Height int64 +} +``` + +`EventBlockRequest` event is generated once `MessageBlockRequest` is received by the `Executor`. + +``` go +type EventBlockRequest struct { + Height int64 + PeerID p2p.ID +} +``` +`EventBlockResponse` event is generated upon reception of `MessageBlockResponse` message by the `Executor`. + +``` go +type EventBlockResponse struct { + Height int64 + Block Block + Commit Commit + PeerID ID + PeerHeight int64 +} +``` +`EventRemovePeer` is generated by `Executor` to signal that the connection to a peer is closed due to peer misbehavior. + +``` go +type EventRemovePeer struct { + PeerID ID +} +``` +`EventTimeoutResponse` is generated by `Executor` to signal that a timeout triggered by `TimeoutResponseTrigger` has +expired. + +``` go +type EventTimeoutResponse struct { + PeerID ID + Height int64 +} +``` +`EventTimeoutTermination` is generated by `Executor` to signal that a timeout triggered by `TimeoutTerminationTrigger` +has expired. + +``` go +type EventTimeoutTermination struct { + Height int64 +} +``` + +`MessageToSend` is just a wrapper around `Message` type that contains id of the peer to which message should be sent. + +``` go +type MessageToSend struct { + PeerID ID + Message Message +} +``` + +The Controller state machine can be in two modes: `ModeFastSync` when +a node is trying to catch up with the network by downloading committed blocks, +and `ModeConsensus` in which it executes Tendermint consensus protocol. We +consider that `fast sync` mode terminates once the Controller switch to +`ModeConsensus`. + +``` go +type Mode int +const ( + ModeUnknown Mode = iota + ModeFastSync + ModeConsensus +) +``` +`Controller` is managing the following state: + +``` go +type ControllerState struct { + Height int64 // the first block that is not committed + Mode Mode // mode of operation + PeerMap map[ID]PeerStats // map of peer IDs to peer statistics + MaxRequestPending int64 // maximum height of the pending requests + FailedRequests []int64 // list of failed block requests + PendingRequestsNum int // total number of pending requests + Store []BlockInfo // contains list of downloaded blocks + Executor BlockExecutor // store, verify and executes blocks +} +``` + +`PeerStats` data structure keeps for every peer its current height and a list of pending requests for blocks. + +``` go +type PeerStats struct { + Height int64 + PendingRequest int64 // a request sent to this peer +} +``` + +`BlockInfo` data structure is used to store information (as part of block store) about downloaded blocks: from what peer + a block and the corresponding commit certificate are received. +``` go +type BlockInfo struct { + Block Block + Commit Commit + PeerID ID // a peer from which we received the corresponding Block and Commit +} +``` + +The `Controller` is initialized by providing an initial height (`startHeight`) from which it will start downloading +blocks from peers and the current state of the `BlockExecutor`. + +``` go +func NewControllerState(startHeight int64, executor BlockExecutor) ControllerState { + state = ControllerState {} + state.Height = startHeight + state.Mode = ModeFastSync + state.MaxRequestPending = startHeight - 1 + state.PendingRequestsNum = 0 + state.Executor = executor + initialize state.PeerMap, state.FailedRequests and state.Store to empty data structures + return state +} +``` + +The core protocol logic is given with the following function: + +``` go +func handleEvent(state ControllerState, event Event) (ControllerState, Message, TimeoutTrigger, Error) { + msg = nil + timeout = nil + error = nil + + switch state.Mode { + case ModeConsensus: + switch event := event.(type) { + case EventBlockRequest: + msg = createBlockResponseMessage(state, event) + return state, msg, timeout, error + default: + error = "Only respond to BlockRequests while in ModeConsensus!" + return state, msg, timeout, error + } + + case ModeFastSync: + switch event := event.(type) { + case EventBlockRequest: + msg = createBlockResponseMessage(state, event) + return state, msg, timeout, error + + case EventStatusResponse: + return handleEventStatusResponse(event, state) + + case EventRemovePeer: + return handleEventRemovePeer(event, state) + + case EventBlockResponse: + return handleEventBlockResponse(event, state) + + case EventResponseTimeout: + return handleEventResponseTimeout(event, state) + + case EventTerminationTimeout: + // Termination timeout is triggered in case of empty peer set and in case there are no pending requests. + // If this timeout expires and in the meantime no new peers are added or new pending requests are made + // then `fast-sync` mode terminates by switching to `ModeConsensus`. + // Note that termination timeout should be higher than the response timeout. + if state.Height == event.Height && state.PendingRequestsNum == 0 { state.State = ConsensusMode } + return state, msg, timeout, error + + default: + error = "Received unknown event type!" + return state, msg, timeout, error + } + } +} +``` + +``` go +func createBlockResponseMessage(state ControllerState, event BlockRequest) MessageToSend { + msgToSend = nil + if _, ok := state.PeerMap[event.PeerID]; !ok { peerStats = PeerStats{-1, -1} } + if state.Executor.ContainsBlockWithHeight(event.Height) && event.Height > peerStats.Height { + peerStats = event.Height + msg = BlockResponseMessage{ + Height: event.Height, + Block: state.Executor.getBlock(eventHeight), + Commit: state.Executor.getCommit(eventHeight), + PeerID: event.PeerID, + CurrentHeight: state.Height - 1, + } + msgToSend = MessageToSend { event.PeerID, msg } + } + state.PeerMap[event.PeerID] = peerStats + return msgToSend +} +``` + +``` go +func handleEventStatusResponse(event EventStatusResponse, state ControllerState) (ControllerState, MessageToSend, TimeoutTrigger, Error) { + if _, ok := state.PeerMap[event.PeerID]; !ok { + peerStats = PeerStats{ -1, -1 } + } else { + peerStats = state.PeerMap[event.PeerID] + } + + if event.Height > peerStats.Height { peerStats.Height = event.Height } + // if there are no pending requests for this peer, try to send him a request for block + if peerStats.PendingRequest == -1 { + msg = createBlockRequestMessages(state, event.PeerID, peerStats.Height) + // msg is nil if no request for block can be made to a peer at this point in time + if msg != nil { + peerStats.PendingRequests = msg.Height + state.PendingRequestsNum++ + // when a request for a block is sent to a peer, a response timeout is triggered. If no corresponding block is sent by the peer + // during response timeout period, then the peer is considered faulty and is removed from the peer set. + timeout = ResponseTimeoutTrigger{ msg.PeerID, msg.Height, PeerTimeout } + } else if state.PendingRequestsNum == 0 { + // if there are no pending requests and no new request can be placed to the peer, termination timeout is triggered. + // If termination timeout expires and we are still at the same height and there are no pending requests, the "fast-sync" + // mode is finished and we switch to `ModeConsensus`. + timeout = TerminationTimeoutTrigger{ state.Height, TerminationTimeout } + } + } + state.PeerMap[event.PeerID] = peerStats + return state, msg, timeout, error +} +``` + +``` go +func handleEventRemovePeer(event EventRemovePeer, state ControllerState) (ControllerState, MessageToSend, TimeoutTrigger, Error) { + if _, ok := state.PeerMap[event.PeerID]; ok { + pendingRequest = state.PeerMap[event.PeerID].PendingRequest + // if a peer is removed from the peer set, its pending request is declared failed and added to the `FailedRequests` list + // so it can be retried. + if pendingRequest != -1 { + add(state.FailedRequests, pendingRequest) + } + state.PendingRequestsNum-- + delete(state.PeerMap, event.PeerID) + // if the peer set is empty after removal of this peer then termination timeout is triggered. + if state.PeerMap.isEmpty() { + timeout = TerminationTimeoutTrigger{ state.Height, TerminationTimeout } + } + } else { error = "Removing unknown peer!" } + return state, msg, timeout, error +``` + +``` go +func handleEventBlockResponse(event EventBlockResponse, state ControllerState) (ControllerState, MessageToSend, TimeoutTrigger, Error) + if state.PeerMap[event.PeerID] { + peerStats = state.PeerMap[event.PeerID] + // when expected block arrives from a peer, it is added to the store so it can be verified and if correct executed after. + if peerStats.PendingRequest == event.Height { + peerStats.PendingRequest = -1 + state.PendingRequestsNum-- + if event.PeerHeight > peerStats.Height { peerStats.Height = event.PeerHeight } + state.Store[event.Height] = BlockInfo{ event.Block, event.Commit, event.PeerID } + // blocks are verified sequentially so adding a block to the store does not mean that it will be immediately verified + // as some of the previous blocks might be missing. + state = verifyBlocks(state) // it can lead to event.PeerID being removed from peer list + if _, ok := state.PeerMap[event.PeerID]; ok { + // we try to identify new request for a block that can be asked to the peer + msg = createBlockRequestMessage(state, event.PeerID, peerStats.Height) + if msg != nil { + peerStats.PendingRequests = msg.Height + state.PendingRequestsNum++ + // if request for block is made, response timeout is triggered + timeout = ResponseTimeoutTrigger{ msg.PeerID, msg.Height, PeerTimeout } + } else if state.PeerMap.isEmpty() || state.PendingRequestsNum == 0 { + // if the peer map is empty (the peer can be removed as block verification failed) or there are no pending requests + // termination timeout is triggered. + timeout = TerminationTimeoutTrigger{ state.Height, TerminationTimeout } + } + } + } else { error = "Received Block from wrong peer!" } + } else { error = "Received Block from unknown peer!" } + + state.PeerMap[event.PeerID] = peerStats + return state, msg, timeout, error +} +``` + +``` go +func handleEventResponseTimeout(event, state) { + if _, ok := state.PeerMap[event.PeerID]; ok { + peerStats = state.PeerMap[event.PeerID] + // if a response timeout expires and the peer hasn't delivered the block, the peer is removed from the peer list and + // the request is added to the `FailedRequests` so the block can be downloaded from other peer + if peerStats.PendingRequest == event.Height { + add(state.FailedRequests, pendingRequest) + delete(state.PeerMap, event.PeerID) + state.PendingRequestsNum-- + // if peer set is empty, then termination timeout is triggered + if state.PeerMap.isEmpty() { + timeout = TimeoutTrigger{ state.Height, TerminationTimeout } + } + } + } + return state, msg, timeout, error +} +``` + +``` go +func createBlockRequestMessage(state ControllerState, peerID ID, peerHeight int64) MessageToSend { + msg = nil + blockHeight = -1 + r = find request in state.FailedRequests such that r <= peerHeight // returns `nil` if there are no such request + // if there is a height in failed requests that can be downloaded from the peer send request to it + if r != nil { + blockNumber = r + delete(state.FailedRequests, r) + } else if state.MaxRequestPending < peerHeight { + // if height of the maximum pending request is smaller than peer height, then ask peer for next block + state.MaxRequestPending++ + blockHeight = state.MaxRequestPending // increment state.MaxRequestPending and then return the new value + } + + if blockHeight > -1 { msg = MessageToSend { peerID, MessageBlockRequest { blockHeight } } + return msg +} +``` + +``` go +func verifyBlocks(state State) State { + done = false + for !done { + block = state.Store[height] + if block != nil { + verified = verify block.Block using block.Commit // return `true` is verification succeed, 'false` otherwise + + if verified { + block.Execute() // executing block is costly operation so it might make sense executing asynchronously + state.Height++ + } else { + // if block verification failed, then it is added to `FailedRequests` and the peer is removed from the peer set + add(state.FailedRequests, height) + state.Store[height] = nil + if _, ok := state.PeerMap[block.PeerID]; ok { + pendingRequest = state.PeerMap[block.PeerID].PendingRequest + // if there is a pending request sent to the peer that is just to be removed from the peer set, add it to `FailedRequests` + if pendingRequest != -1 { + add(state.FailedRequests, pendingRequest) + state.PendingRequestsNum-- + } + delete(state.PeerMap, event.PeerID) + } + done = true + } + } else { done = true } + } + return state +} +``` + +In the proposed architecture `Controller` is not active task, i.e., it is being called by the `Executor`. Depending on +the return values returned by `Controller`,`Executor` will send a message to some peer (`msg` != nil), trigger a +timeout (`timeout` != nil) or deal with errors (`error` != nil). +In case a timeout is triggered, it will provide as an input to `Controller` the corresponding timeout event once +timeout expires. + + +## Status + +Draft. + +## Consequences + +### Positive + +- isolated implementation of the algorithm +- improved testability - simpler to prove correctness +- clearer separation of concerns - easier to reason + +### Negative + +### Neutral diff --git a/docs/architecture/img/bc-reactor-refactor.png b/docs/architecture/img/bc-reactor-refactor.png new file mode 100644 index 0000000000000000000000000000000000000000..4cd84a02f600154a1d59c34b1e7f2f09ea66167f GIT binary patch literal 8105 zcmeHsS6EZQ`({uP0TGZYh%~86krJ8#=^%ti?@bUxml9eif>NXkNbjLUKzaZPO{$_& z0!RtHNDnA2bp8Q<``_J*z1q8d_ToJAoNwkU^L}&AeDlt`XIiQhWK3iL0DwYWO-Tm; zAVvWIMAvQ*1b|eLIvc48ca0@a*>DlbfBnRH>~*)bC#} zMH{c*V*I0b<;}U4w+%xP*_P|n#MkT5KNOsv>Ll5!5DYsAt1=Us*{vriBES=BD#Bcz zT~i{6Xo)b_0RZLyFaE#6WL8r1zQ>EyY4I0!)zvNI8jX#X{X=`I=eNf*OETH9fV<&U zeNv8^imp#>oZ^{vr#E0}kM>-btIk#|IWlyUM3tCdF9-{s-EmKhE8sE~?etFjI|~0= zB|g3@`!I*R9=R&S+OSO*o*$HPUrWD_kiA%&geWVgro))Dg4_q`8O;l+L)p`+hQdfi z>^`*u?a4-iIRy);6i6KzX4x;>AMY3 zTtNsiTB@*xrFYu;F|c7F|HFZ@=F{6Tz-CfDVdp7RB)-q=Ig7i)41SSlq&Uyw zSoYmJNDeX#q;#CEn^Yr-(b5+0I~;DCNe03cr(B3lrSxUTic=La-?E}atme6xm>QMh zdvz9y+$s^QGAZ#hhDAkib?~li?NARcV|wqtG!+g`$lDcV8AyXhU7>0o-*-LKsA-#> zRuZKbe=@tUpJKqr;X<@oxUnXh)_7xHyL+)?th2S-z%w&yG}f$bEF!KC>~C10E05Ze z(w8Eq#R;G4{_H?a=}E5}RHm`)w0$uhSQtz?8+>6RWjgHW(cRl-4*vq8X`cHlOZvg{H!-$Z>f|(DK*CY z*tU+WAB?pZZ?^#sTnZxVT6xu-`n(}^Ap-vymJ3~0w{rU2geSl!97O8560qCZm+^f-srpipRQ z*FTp^QC@aBnoa9$*e$mbe_=oivm0XSb74iT_a+C{(6r5qfZp{l-ke&5UmDz%%uIA)5?bC<0ICo%f*(qm02fWc=NKLL1AGP;=R_FKcRBvmkTItnQIp^dCOOeu z0?^3@zF#=ranrso)1HT7UzHSN{pm-2Ri5KLcf#Ptob9(RdoLZ_(P_=&b|I`> z>$%wA#(37|7yn8QkU*@46IB=r&C`2Ni%k7=oP3BQVB-l2LR{7fx0N+AOmHy_SN$IO z#AGVbj*?T>0G;pb)BgTWqv8)5L;yE-VW%xjdP1V+3FY8gwix{2#!mXL`IS#Shy7$2 zS?abHakR+mv>BbnWB?cLk5wE)9(RIre0! zdB)fg2vI=whZOh=Zy)}qSPEjYhe6UoW}YH4D4sADX*D)1kGz2_qd0l1MQ!0Pkp2Fv zxoEn{PmYIM8WH;;VMQnJMjq^mr;_7bBge6gHr`v1_AyfIJcOTga>g_iOFTROG&Fq;i}*`Al7zM9qa zLXif&mU?{+QI^%0IuB(&>%C{(Wb)nsUohVBM9BTxS$x9Y_!Z1Z?1q1({Mqc8%Vnh2 zUzp(pMT8BmD;WBK=LTMUF&wk`FXh)o1mp`pg(;wt64hDPe(3K?Zw6nig>3vB%nDzy zhKmjWW{$| z?j%sY)lG)i&{CC_{L`-TIuzPl8na0o$}JwP48RzG2`ir4(A1~?Sc?!~P91V9rDA-! z)Pbvtvsjq1hK~)KP8zb~U|>Sr8w?^CmVFMX?4}i4GU#MzU^T?Jh}vW=WWMDby)D!3 z^iauEG&FW|Ueo=kKk&j@CGt_-cPRfL_H*}3G3`NRR9UfN*Z2yP-d>|`3!Dy7#wc)T z#boztH%*;CyrX zozJB3S2N|kD`(su4VwlLbr#Ze9q*M&d4W+Eh+f_I{IL@H$aiJJ5fl3>*B1QX z*`Sn9aD^^2qM2OAF1Sq3?_`vvnk|G<^}NJ#%m_aj+FY>08Gs$O!5niJWH+uEpmbG@ zXOBv>#?{oVY8VjB6Qi4F`jOjhUOp{8e??)gpWsXFIYEXkW!Gfgggz{ucFSc|vB2Ux zTX&jg6D>Cm-WuqB@CqrjzKNN}IpWXw!G~5dV}7!j=}C{ustX;ZtUeJx1=Z$0@M<(} z8E*Y8x>S<0^Z~`Y7o&@2;`E5#ADhNj4@w@#c!%{hiYqN4PC%z8rKVOZ75Lb>@EU>R zBc2H(Ut<)B>MGwGkH4aVmrWzCYnNa;B?20ZwV5W$90e$5 z-ARNk8cSq}-r=JsHKq~7J59!aRSe5Z-QSlDuqF5`|ulDk}nI%4VVcYs+oQrQ`0h^+8;@hItQCBUqn@Z$W>T=i^Zcz&#npVKcC zNVeiilHx2%ReFtE^@bVot^etpxh|%i)^m3@RSfV^{d3!vTaP`3sixejY+x2O?rNmE? zV2W@7pKi9s{(RafG4sZwe29_=LvPcsTJyy~4`%PWIV@-+wbWbd08Xb(fa(Sx>7xSp zV|`ug{5b-~z`=GyQF-b6p+2+$C^RTL%^RWGq+ZK!VG9u>6k<(vv(znrs3Z0#{Kd#O zj#7Bn2B$X}_h9m^16&Z9x0@;`b!16Ugje?Cmq(rc30?uz;*IQWxBl|FH~(l{@by(h z$t3E+tu{~C){5cxZw~bf2qADoUTfjxGhEm;j_k6Il=|7^&*73wM0YQQ<#5S-PSNEHc^{g3aEr8*s_I zMfGrFv^YzKXHCR1G56;DUoBCWpV*bB%0=uS`wNphDeHd#R`Dr3K(a|NJ%nmb$2I>* ze}IW~PP2B}kW-RbGn4ZYBv(|4XTXq{TNJ!oDUK@0j4e@bNm&>V`fGrxOL@FsOeY_( zr`;@?<3L;Hu%&y9O-KA(iKp}SbC4+D4BMi?rPzAr0#i!cTfM@0@-xhg8O%V0au$DR zML!ma9+~6r|0rJfu$Y+>jpaT|p)^E*^Lo;H&e^fU!MH&lhr}f1WKZ8fOed~e(DQb&ZcX_}8)gat9p*weZsg>LXN;prOKiah{O2k0jss`JsR@Z& z&Q}faD(yLRKlEkU^N9VIQpN<&)xLc7vM;bAH80lcSDIn8qT_|^U8}B%QZwe<2I)H2 z9@KB(N)Jf+chEnptDF5Ei3ygXF$@&;mNMR`hhIfI^zdQfh8=Qo2X8?NKK9&ked!|6 zeA&FUf#J0jkAGsG&aM5KV{dH;LSiOGk|dQ(`PR1EZ1-=P^4jQ7p0ICf8X&p}&G6Mw0)p76yFtn+Rve$6`6BAY5x#YV1y_~SY{)X?g z8LfPBh8Ia8+vS>sE(@0oO^G^a*aX=6*mttFbRmXMz0}@g$ssb&x%#txL3z!5H5{Cj zU+8`!mi%0##w}9$3ZiW2p5X59Hx$+PM62?gQr$OGCt3)I-FaM6cY5O ze@iY#3=T|l*@PW)VOq|5ro@71y1c+rbq&T!ja;F<$f*ux6nudwIGBMZh ztTb-!3POOywL0_q$IWAtf?v(5@L?UX^X4%m=&KpU>5IPHHC6=H`fh+2<4jWa@ zD3sGGg0`a1EPYZT5Nu2bv2Lg3`H@0YMnydO+jgr04Be_q8f{za;Pl5Ilw;1Fa%Yp<~ zENMithxUXX;tMO?D(Au}#JYar+!nwOG(1<}ordqivUHc{+LnCi$y~}d zQf?IPRYnbMhIv`){2uhDolbU*07C35Q#L8LkV;b<_`FfslbW2?eBI6o&TU@x8BXk> z5CX)o>)Md2=)-6Hx}EE&j8z|)vXRW1oUIZZ;$Ql}aIG1QkMiDb?=bk={HkJXp%gKt zt!e=5q5!A;th-mCB%@^cGH?QmYBbJQO-F^Q@U^O=rksv5hXz1Edh{|byzvfEazko6 z&YIv(Zuw1b>vm{JAo}oZb!QL0j7hOS;vy(+nE#TIO5nIC?bR9sb0zkBd( zV6HBAl=G|DDvvp@iNd3N&6Cx4jORtrcfw`dG>FCoWDjIW`KaeGImx`^8+I#kY2C{@ z@ar?YAS-FFS+%)D5Z61e`m(D00}Elz9`UaT6V#AMns+Azd>GC*3evn8?&v9Vc%Xnqpd1e19tmT6hA(H5WgxAQGGW@ol7boeGR=@Mg z3gcDq7by~Rl!)@GaZ7&Cg}-9RbX>(t@61HZSV4d8xI)JjWmZ9=mN?`+Tp+lld~yQ2 zvL;6TI$xlG<_FVjZS5oG-Vrlx!;@jjrV(6ggqNWfF*r|S5n@S(wv-TXBpWEI{BZu; zbO;f>7-48XHzuq#2=(!M&IQAyy^G1bqygmx=wUtA3P}{Afba)KAB9c(IP(+ zUylrN4ulIpw6#w~Zr1qRZGE@D6XLQ(E^S7ZzPMkWi|m0F*;aWO!)LWnrEY%uMSGSf zkl+YQ4lD*PjCxC*10{is#-u9@B~Uq=bT8K_A1)_0zl>x1JhFMj#*efUPs6vQhKy9C zJj`MVo7e-8xYHE>KDDK8Eru5ub&m)+0{m={-Dfi!n09__l*>6cwx1l`D6}F5f~Av9 zTDWGniVK)$D`+%&YPTP-VR8Ps0ZnRn|0rmGGoy0;;zj_T+_kgd!1awR-n`kT z^-<@id2rirE@aY=>31Kt7Ou97jT#4-49WH8IGsJ@e4=*K_Opf%ktWo8$oTmW{5K3HiO^o zEgp}MFBb_PZjMD4@Ca@1lD5p3h{{u{K^^XYLPZ-wFjQ}{MgR|yE7Ts}SK{@n&WAw> zXM9B=eb4{W8G~80QbJms(nUC&fefC3<^vu^<9bKhywGQ%;_lfl}Q07T70l` zKfT9rk%<)I^FgN9VyDixQkh%SFiHGvyk^rA z?dYNmPZb)ZR0+_?0UkfO|JzI#{o>4W?W;?o_k3}Vo9Rg>-)1OrQX&mW_DuRR*A1CS@RY~v*`h=9=rU?$AE(Io!c3Sj!N5fh~7-I1zj6t>32IuN7n9g zK{68szx3x-dl_ap-AVG%rSN?4*!2kt!jbvQl9*S#e=xKLSyjW)n=PSTkE(4YR}H*F z*qK`-SvBt-Ych7liNU9p%yWT;oveh4hOp}ki?8RY)hvUDyH2X6YN(vEK6;4ibwiyG ztaHq-&2bST;ogfIN+f+-2D^zFdDreQ49mt?Vj3sMs`i`I@z{iwaO$y*Q0Tf+-b6{I z+aHNt!43E)(AIf2!-$#8tvWZgh&mlMth<`^ZdX17*9G;lZ2qAZI8{OTc<2bB=`Fn} zc8@-b%`xEh*?togcy3SdDVS6q@7zMAOz6L;Z^#sXoQKu-XHZ>M6w`(JFiOvErz{P; zHxa2r|GcwCkj8dwooC|rG zL#5n0XXN59XptRY`pN6;N8xu6WwbD+kDGiuz<+MSsxo}MB{@mhuun0IFYXWTEjPvd zBR+a|C9mv6mOZ@i-t%JsDs=Uq26B;kaw09;V}KS>MyId1%zyRAjPeDe(|_#g@ciQ% zZe94CIik+s;PxlkD-%s0{Qkqs6#eG562H)SA)3XXsj+YVk9e9*ir)AGBxS>zV3H_- zyk@m?|LORG)oFueF?HH|Zb=F*8Ks@=wt#dYP|4U7TB`rGR=BK|fT5lt8I z3LfakC$ePoQ8{-(dq{?a+gtxZ{COpMmO62zvJxUeC|ou3@+a7BGBu2-FYzDy6Y`ZK ziY3hBf*s-y<1lT*4`*NIevVTjZQxcySOBg2TLQnuAnTs}3cbW>tN4#{#Q4VhvU{!x>I+p^H;#d>zOJayXHD=OsfUefZL5MLU zG5&_fjx5e@7Ytnw3_YeF}&=~xkEIc82IJ@;sYR^%_I zET?FXFuE5g>e62dC_hOftsC)d=5Xus8-f|T|g%f@Dlu5!8x)@E)5_0*OWZk$w(M{ma=iUh7 zsT9`DkJg%HplsNeR^NzAyoNVU{%m$}KAQhu$oE!ib}TZNfi6EiBE9*mtyH9>NkY)_ z%57|1x!`tx3i3e)M9-H%-R3g>dWE7My~g=ap2DA1@qjJIQC|TC3Hjt-x%g7ChG>i^ zV&C}v2TB#r`~fF`7ZD_7|#xg1fDusR*99jYVCH~*NYyBIva1fa)`@GVd zaJ9_kschtFYwh_$+Q$6_p#g{pKavs<78MYY(t9K-Eha22BJx03SXx+ErVxku-xQo( bZSC!R|IZ3f5A-kY-T~^$T1w>#Rw4fd6<|rR literal 0 HcmV?d00001 diff --git a/docs/architecture/img/bc-reactor.png b/docs/architecture/img/bc-reactor.png new file mode 100644 index 0000000000000000000000000000000000000000..f7fe0f8193d93ce7d0461e064acffc0426f38d55 GIT binary patch literal 44211 zcmeFYcQjnz_cuNwI?+oA6D0&eh!Q=BZj{kWh^V8B9-|~`)aWgUG6YegMjJ6g^j@Nl ziRi&Z9sM`t{rSE>&sxt~zdwHK`RBJDYgycL_ucL6z0Yf(d+u9pO=U7-Mq&T}K&Gnl z6bu01p|QVGgt*udjlri;*grSzL?Y|z>NYku_V@RnK7H!q;*ykSp%$k55m}zkNfEj$$PR z8ykOu!A74x&0}DQ=I2j!ajAd(`fE~BPkDK{yuAF;(UF^*TVP<|#KgqAckg6nWov3` z*4NkH@9MZ>mB4wSuKW}biN~yhy&!T^G4=oe7_zQ@aa6rRlCgsXo~j^4f}i+gB(x8f z)Qse@5&^1D<@9`Kw$2Gc3YzGYNZ5!dsNBtSf6N~h`aNOHe969T7NY$-<0JtHv>hWB$gTPMBS0ijqj!0esSGXJVX&Ihx9JPy6Z4$ukHHGmvA z06?-nkz=31iJ%v@LmWA_liY-vmSlf`kv3J#{IqpU6t0EQReVGan+hG#eDEOtSy*0J z`~YZKbHQAsvA)X92uUl(2e>)Es*_|oaA?pg4Qim1iZFat&K+eH@$xLITs6odrJB#A zWQ=11Y#k-@lZ7l<7hctP{`1~+#jic zaTFw$<@y|9PBtkv`o2Cj(3lL_O6{*oCOEI9eUh6;Q%4U+=N5k;4m%P6#!qnymfl+j1q~86T+k0&^=YpUg#gy(C7C%Y-X*o>b|L#o{0c08$-h zTS`1yc&N%wBR+K!nyg~&b0fc5lD1vG{S^PSWKbr}cI7y8tCni{$B$aFAUXAN(s_=% zP{7xh#3D+`jxc=G(BvCKG}4YgTpL>KX{t%KrcZxidJ>-QRU9DVXL5h6F^m6TsMOMPnc#xN(=o!heZ(eTG)+6wL9f|l~<$hP1cs+&-fmy z2?5ej<=XLd4;uEJG*kR{>e*@}lip4uAuQ>X^Z*LHxto1_(o9PP_2b{U8+v<=nej#N z7OC8*ujtiRJU3VGNX69(Q+W*Fx{-Y$N;Q2Yde2U$=}_x-It5-Z$yZR-+-MCeWNb)0 z@Ui|9!82UI(_3QgFV%;<^)huDXQUnN>5f|2@lzT^`Bg22V8o*>!JzL&U>E;0_Sg)tF3|zf!Et%jznnd~}=C4sv;(a&SkHzJ+ z0KqnYM^wF{cud(kC>#La2*ui2l37?0ZJ_l*T^CWQ;$JraEjL4DS#0+dvb*!Sn#6Db zEcgf@dCfsyDRrmnV6p+=Yk}D_jF&Wv?WRHs*wsH+L{5?!!1gN<(O}Y>%BWJmt;n9J zipfYYDRE}ZE32s;VcmTcgM$Mw>Y@#^P?;D1JXO(OPHg!xY2+@{u&QoeRH3JkYu!r( zs=Q0{3?ExTzvQFqbfGgU3AK8u0v~y>DSWUzqv53UZAcR~ye|w=!2yta$f2h8>=m8W zP41{+lvYgj4Wc|^K6dm>8ll4lSFbRTw0p zHVd~iIJtnQF9dzdtlVzv(K`S+ZZx#1%UrU3FF)WC%XpBw!vYxIi$p*BGd|71_e=-* z&T4dymZOJviHECP=w^vzak<`?3DAK{rSXzX+LR?h?8YmQzVPg#!n!yK!qb)Z+`ZtC z^^U>Fn#@}-3lN5rcPl4)^qA)!^_J;gu_r;!!tFz4ZS;bJ^h0F@Rd0Z1rs?DQtPzT#?<@QN^JtqQN?XTamd7i;eradUU55u1wQRq z>Sc`SRnJtfw>Qg36V``3lTOd6d4h8wHh5`*!Hbe z#=kTd5mE1~)aXZX&ckjlj(6~Ej6lVzt)65xfCH^%&4bF&CkD< zr3hp+p8${W_bXe;Ji#V(*D69xK2o%w@FV`Q)?SE4nF_{S$0R8!e4@c0H2c|$GXP9m z$bDWEd^-cJyc)y_y>}m=FeGlpWZ)R#$55=lFrCetOTUeR)Hfm3w-2dIPWC6b%V)i{ zFrdrURG?TIa~yP^o(N!+QmG(u9uw4?Hd|iaq9QfM15CBI*(wl^eQ<&mwN=>vshN4W zcoUpp|2{8smPK`Ej2cJcvMqpil!*MPXfpPxGE|^=`R-uFd~ERcm!*xYcX5RJ^_KYb z2d~lkm(pp~t<#;Rzc+6a7%tjJyvCEF=osSYd^Ib`X%QNwprcjRQY&P4YOqGd?k82viji%fG6Z6T6j*au7|)uk zPZ13kwKWmwc7@+;JXN>iYe#kRCRB8}>V(2-s9Fv>ev#WH_B3e!8^m~91D!C0<(zYmij(Q3r{_u zAW}Xd$-z>O;rFLO5XE#==?6w5MnzV=)yZsw7v>UraG?+OnH6fG{;nBJ3d|wIvUBnk z)!#=w=;%cHNRiAF5Z+hnlbKSV+$;>&+lDOlSM}$)1);;grfbvC>B-^95B4Xmbmg-j zK2ji?fbClIOgGMWprz|FyV9ZLEI4*OCD%3BFG`WQ^2ZHS2*Fq5mps=1A?L-&FtO8X zbUW_i1995xwOq@+xMFWfTio=aFQYHu{k`Hh*Jy?>a}NXzLnXB1etQ{ae)DgUe~f~@ zPRDl5PaYVqDgLeV(IQ2U=Wc7b6SCY*<=todyI^z@MfY(*@8g_O_tCzh{0mKPAPC&4 z{=Lk9Y@zh)R4@VH+jp$_TfO5NjnJ(!rE^MKPT^F^em2Xae|@bcx&7?|J&%cOx7z%bWTO z=R`E=S;e1`65si6GPT}~gVwvP!MFdV8+e~xYJ5s0c`KOxtKUEVn^fpIJIMa699SwI;XKmqT+`S^+{Tu&*W0^&9)5q)* zb-sQVuV->T8c$e$^9(ZwTu-gOpdF+*DR^KDFjfs507a zj%EIvyn<)Zn2kHMWEK=A>%+*}B%yC(&s0r&ZQU-M&i8R>C&>`GbOGWE=g@|}{VCGo zwOaQX%f~^^H&fVvl(V#q;(sHnqb(Asj1$Cp25x%s%3@rz&wzY7t5S;-rJJxY<3Mpy z#c&gekw2@|2>SkE_=?4naz_1`>z)?%|L`cY`a;m1b)OjX2*oR_@y)k@FZ`_4Se7{G zhl0Rapz3~Bbe0Xu^?q#xxL;IL^7ok%2c{lw+}?3gq_y!h4d! z8HdN41e~=t?SS{KuNE3bii=u?n-;zGnmjhYH88Yw3+_R-ShuXeopPI)(H8?2Y)!nv zE`PIB`SZ3*)`m(xc@p{&cgiFz`5C8IRu(E;v^+N;IRCIb8N?VW%NONdlgSsYEm)S) z28v_qFoYEH*jt@$FpzzEyF(nycxV_C8lh zm*>j7)7Ij?*F9aDB|zsY8nKLS7z7#4Lno8CXH1rr-ouf*T;F`cl>RoR_=H`pl|p{8 zIM2U2JoOFhBE8ObI$@^}iINp_80inSnS{v5krYIH;5x!8*qdG_$@qDkEq zC2#8~^php;Ia7X%{Lm{jC|Kz znRFbzyL{kNf?~j%vlQ#JjJ$t^=|F6yP0IJAd7Ro`yl9nY|I6YLkGaj+4LE@o<*Fe+vjx`g^MjGg>!DPV!r?(e_U=j;8uA zLwl#|O6R1h=+0bXnqb^q;G^sKXe4oK*}Z{@0%yb`&z*JJV_&RRyl(h=K(oZg=b83H zvqj?Aw*_7oBFaGu{@N+dHJ4(uwfBgR78NVp!){bbl+06xZng7ZQ6RvL5(|3)UkE8= z006U_*!~8rXC5k5udcZg2H#tZ!HW?$ zS*4JPBzgZiSkP5#s6flmWgX}F?Z;L)R@dnbFv1@+`pR&XpDcjX*N^BPUgrlvu+7!O z*U|TX$^Y?;?X&jDdfOBhz#+cfgXjG(+9zN9YMl$C*;6cwnURnNRNeSHY$E;w=?rB* zGh6ALT=z>g`H|*hnYME48+P}Tp~k4H#Ov|2Yp{vMFHjy2yBGC z61l)*xPij$lH&S-uBh$SPa7#~qGl_5g({ZU3xr|U;C+kb0$GD`8*}Hx94=oh&RA$% z=0h#h0K%VOf(0uH+ftNNs|}WWo76UV2xZe6gLIb-N(NiiMZxu+V)ol%uP0T8sC`lK z4Y~1&UOoY|Qk4S(KU;_Na<{4&LoX*l6z_=$fe%;kP^RlKhGV&AbnPEF&f1UmMN>G! zoSeX7iQD+qXF`#mi_!DUC3yrin=YnJ_qK@nm_#^(On@@OBK-akNTIO)$3E?){Jv+C zUuKQ@=3XwPl{;H2=v<7??+G*8RelYxCqGI#5g@xBsX$%DPSwQed7X{`2tm`V*# zhwg+`uGzk`uJ&XaFj)7y;nO^d=$X7Xr;<>&Px{kEQ*orJ5dVL{Cjnm_jP@@K>eO8~ z4;Rx?C>tlY9UTfC&Xb0}jO+-csg2j2CO6xy#l9r+CO{Cx_$gviw0o5y;4L+TB4fVj zE1q(El>X zVN9mmM`{gXiN2eMYe=)EPKZ^T0`M|FFjpz(~j&D=a=s*%Iog676pyvqfb4=DB3g{ z8c+tqxBSyZ4<%_(IByemWZqf3;opURO{Wh<_I905v<^BfldgWUt7B0wlY3h`|LWc@ z6@2ZoX#Pj}U7t$`{_4=A7En6?cvECdpAH2!;G;L6^+uZEu8@qf3~%!(4X@^N3eXj$m7m^WV5(P8)>=MGQ>Y zhtaG)FsIEzaMxg{h0Z4PkzM^~q~&(sA$M5ELluPwSDFqHO&+?RmM|p@j9N4B)v$!n zq1sBeF897_-yk5A^*G`wSNGZLEp+jCM6+IeP(!Xgp3{^FJKu6Lqgk>QF{R6UVZeL& z;h_sDG9sb$L+n5u0L@(<% zR-!wz1}w$T%00&P#c~gunN-YjB7ARmiRDM%VQ^ z4yYo%s_NHZ0`LGATYIiS5IG0SHe zx?XXTnWYXT9}L+G7;6!f!7d2tpqr)`qR;Tm0XHlS%bW7{PU&mP_jIuN;G+kQ4t~AE{Mm{+{vE#I4r#2>;&06pJA%!aXgzSzx zhirt%^m-?ILX^PuAus3k0^1|I1*nkfvFFF6>){fg5M4RXA;A{sC2ZJG{_=OONbBjcr1I9DTB9UQ-kq92?I-w$S-v`w7iHfsZ>`#kvHXj)E1@z(^k?wb`wEhx2k|V`fGjK^Ydm=ME%c2$_)n|?vvN4}lDz<1PM-cqxmXYu;*(b*qlHYXld6YiC zrWAg$7vlOVxMz_MTFH&DTm{c5(WLFq*MEdqpKwLaqqe@_%{LEB7C}l0a!P9|Y_ zEHD{k7ouX;^oz+OtPku`36b%k$i^B;TFcBNsk(u!``t^E$wdk!OvVv9$;DdYTWR`g zxY`p+Q(7ot{YnZG{IfN~_Q1jGs&n`E= zxvTadV;}JK=4IilS+?t>Ni>k~1C@%(U5IM9WWh6f+f7ND{F9mmNADIb19Dd4Ysf=iQp!oOoTKpWey^6SHBeUh_) zuN_)~Nbsy*d7E29!J`>u>IfW!oSUt#4-~KIi?{@EN@HKGT;=->RB`4TiAKFiQ4arm z`u9BTv&JUE@(hgHqKZa4aDBGi@5I-d) zaTH@9G1&tziP5k%ZH6-kf}1bFl-G#cjTvb>pQC|}asvXJibT+cz-ta|` zYp+i8%+ZCHdvOvY2jRQa>ExRo@?d%epZR61qXe$3HmCdDyDEmQqhwc*nh+Q)a%CWP zZLQ+Gy8VCg_W#+|;(y!qEXpr>5=`M60=`f@o^P*frv>oe)bw$4cEi3ue?7)*#$<-` z|A_?f|I*S{$no3wcrs$o5IAXRCe_j^7vI1>GWi&}N*04{jWsAZC2bk4Ge7SHZ2G%GpD~|<09ArlH?<*- zh4pXmwR62qT;F#H@z`(Ov4px2VRJ?pZd~+dK*$;G#$U5Oak9E5jqJcI5<5$o;xvwt{_?EZlX}m~CF}AGZo>Ju;X`*VlQrD=CSX@Z~}U)gXn(+Y(0RIS72dhMYrh^c8i7zD?AYkjiBWGIM~6S*gqZ^7PA|K zK(pkGKPdmJoewwtjl5i1>{Hmzo}Xl5JQXedg#s&tAMSh$dbJA@97B%8?tsufIrYcx zk`P01udYrC>uGwGb-pTpG@v;=sKf?JTmiqt#|ulnG|LsfDw)S+UvG`u2>6%3OfW{X zW^||ALh73?ZrtPzvCVkyIGQh4;zhYMrk%x{O+!IXA@d_uqWDJHU|xkyp`v=P*gcoT z=hAnfRrd@M>Ykq#6UHkwh3vMQJi$Ddbl@p;&;e%0?Y;20B$GdW&Wt6a73t}vsoYr` z9Y^q9u2}~^#yV9gdFCx~QxJ2|lGCs#@DtW0qInJ!QAT7yMuq#pmRq6Z^&bjE!y@M^ zm23-z@Ba!h#CrKe`_IA$o59RNPm--TO}xjF6f#EH^6DAvfX_lHyuZYJ?QaO+ph8kq zJ-9`pjy6LC=vqia$tS~<(v{^rnbt;1q$DU8`>|^3^Tgg(tS>-7`4;bC739>zSDZcF zQc3X-ls_Q!r6UA>QelNDEwzJsETL#AtQ7Lq(;^88%Fha7`p}+5aqKgU%*46=fTfUP ztX?K}e^gOFl(~NJCbW&o02`#-Sejd~WMVk-EpZot7FI3&``U0Mb#x_@6fRaN033N6 zidwvlCA)FoZTr^k%QfNPF)Z0s-a8>^+ao+BV?Z>PcV8h{DFyl6G%DTsH#XRCv7zZ2 z62A%EdCpBCW~FD=aaVr{NA}Qbpo{Mc(43`ABevwPN6Onqoy9xlq7BRaXu|H z_xG+PAP1pJP_7tB)w0QE&r(dk5A_TCW4F?0G-J*E1g&0desf{P%~8twWaUkp~G4QrZ8E6FLZ-LCVu zTQx6&BaLmCzo%5=HMPBtz=o8^59h9Kn(qrwKj|T&O#M$dCK^3E*UZ=7a8tV=BNdppR5ta3P%LGE^=V~Dc;ZdtfDkTz4Y*WSa_04RaJK*&}g_6Slxd<;J|hE z$vZ2oU`#8_;M*pBUT3;Q7F8@)K@Z`NG=UJpdbJ0w?0n%zHlnIwr*;8vr!PSp)dExz z!76;#>qr-cnxx**%lWLS+X7u%MO303_dZd;XEGA?(vkrJXEU&{3;i%teXr!gH)a+M za;34i!(2JewH~7)m^U8Y^^;EC*G-e%q~pnL<+&reIXhk``b%Fmx9uY9%H+^6ux6xJ1$tiG-tx`tMXc^Lqm)9MW^BC_pWK6rJA8zg z!g6gZw6$=~FC=^DY` zpczJdg)(FAb~NvLy#_nX+o*2tn{X$VCO=!Yv&;_7Rm+Cq(MEY-t1PO&d6Kp`i)Txv zRIMqyP`FaJ$dG}(-%qA5W~^LYRrU1_8+Qy*NAE1Sd1)wgXZtChVpl+7jD|B))YfE% z7fm2o4YsL@30K2xbIOaok?1MpUsDpJzepA@pWlm~^ujz0(5>4=#tXkVrY75PZs1^a zewsvS?W@H?csoMk8OT;6TdW+Y|EP^J5Hiy0#{agMoNSVp~MWS!$oZ>40A`}4R)@jfk6 z#*iOM=#E3sK`i=v|HMU(>hvyRY5)V07F+xyn!@vyI#x8lhft`6YhzQ;@4Pm5e62GN zHuY-wj03yH=TdOGS*+SWS}36D2m*o^lDr?W@H>5j}0bluA%_0O#BweNa+0TlQDhZUtPOI&8OTJS!K<<+JEzXnoQCWp;#hB5? z8qb0lE%20U4iS>G#?aDx&A*fyr{NtaY8y#XHXeRNYJ2t$*SdKMGFO@LaL51!J{`+V)`G+gnXk8HZP|IP7m5cAE1Q}@3 zF(rj45SN#h^=XA22SyLP9KH-{vbjri{`w&9AUtYG| zpEN(^rNrr)8k`!IjN5?rdH$3_jm>hdmTiQ14_tR#tO{kYH%Nw%~(jSUfjbOVl_*5J#T>Pm0`{OK>r~O)< zfa6?v{jECj8Zu8Rk-#5C9YQbNGLMEHreoET^{fGXEYO*5sNz1k;R#Mtpy9%9Wn5;N zP%aom@Hb?vedpNA{Y0$u$x6HchHN{b%1tpWMTsqllU=#k?}#)+Jf0T4W93H-QJc)aDPu%K&pR$7MHq!) z`kX0x#=yKyl`roOI*`_jf6B)wq-e*2$A2#V*crW`(WwS<$?fSLfh< z1k)H$)%rR>_y@cIt@8~TIZ-=8#-*?}t&A&TY@FYJ8(qwGoEZE=Z#W;=#{ zUs!MuK1%kJ2KO-$Q1L9*2*i{pU_MIDxm=bYpBpQzw@$kIUX(*vD9NO(>P$l^PIGVj zHvS{VeveKDnm-MeBF?qCkc_~ON_4`U5#=9=+p2nuOmalEvkjX~1UBr1UV3KwVCSJK zy*pJzylK{@JxUN-{hW0JtyiMNEjs?_GmCoP#$@WUN$0247ntuwS3V)yN3Mbf3e0Gk z${+=)pskDj5MON%x`vM$O6;#sDO8b^R&(z}$3Gs2)i+hzVGn!lM{b6*fjBQo383B&W4kzW-uiIP5ULRGX3wK8LH%_N53Tx z0|!vkbr#^YbGD2+(Ln*V?gr?-);H*aIa!6luktS z=3SDq;0V0wC+lb7W5yT!E+sb{mOTA<^X;i=1Zs z&XP&Us|ACMo2AZ%EB#y@WNQ&OSqhCN`>?y2lGC2g(kolUH#Yv1^l-!HwT9VK=vV`h zo-%X4lmmD9grarHwSyPxd8w~)oKTC%K=qe;HcPsws6F9;fYR~Hyt7-O5_xJU*t1{Z8>Wyi=!~yY_cy8jSrLd~^cq^Fp6x#a)l&IZNG(|Q%!Vyw2QD{}Vtb?{nLSnYA}(1h z+K%V!8s{jdDtm6v8M}T*G&CQPfsb4c>nOa}PO{!cx)9kH?@`sAg+B7Kin~%YP>OP- zk^Ri1zu9FV6uCVHEBn&eL36P^Hex(XE~TqHAxpdEY2J}S7TXk(z$K}Ejb4~YzK^iN zW3oYrO7dgomuk;BK{{kPhC104k?So=4%_6#g}-_@zrVc_;8NT7J;Dc3IV&Y4M~hNq zfE5jf0sF#3Ek@Ht6_;GP{lApx2AToW9{32uVE!O>TTaMIlJQC7z!GceY6@=m_AO5E z`^sZ^;oEZIM?c@Xdlvq--&Z}NwbLwDs`rXSt%601_Nd?0#*S3064xZ5xx)prWQ;2w zS2jfYrb&&@1W%>@PtyeE4Fr9cIxJ zZp-=yQ9KgG)UevhsfhhoQVUhApGITgP@Y)c_D8HKP8~CJ=I~sJ4>{%UV|NC|_60&M z~^FNKbREoZ+=!*$B0!TGQA`3e6P*JR-aVJ79 zAB{iSV;MTVA1txz)IMT;^Y%i>41F}cP-fko85KWN!>3T!5UM&&#tdWA-Z>>tgXd=j zhJL0)Y1s#P+*h$HsVy}o-6&S(-4LDkUKK%qfjt*V*mmnP4t->?uV{MOHmq)JFZQ&Ss8 zZU9QNDsKO5@fp5?ny&Gw%e&+t+pR)6K53l1Y;w_d0(NabRAEqtY_>7JSOb0Nn>@%gL@pgI?AQ+YHKYV6!6UZuAj?PyV{ z%izOLIs*R@dij(9;?~!4xHEnbc0cS_#nyQ2D%tt`Lw+U0mo zQ-GA-p1ktF~)b^pvtfs{}ySTXDBu%ij@esv*DGNc*eWy_L9r#?0lj)|LQzOGP}w@~3K#Y9olK!Y-Q-Pn=Bd@Sl6lj+>{L zILb)lC19;q81FpUZnM&-_9sM z_5MwAa()^QM79N89!H$+$*!Ke1~t+LoqGz*_2vczTyDmjW8`i5Ul$Q(`ur5-IEvzw z0Vd1R2mUM&-|wj<^~08f(UUjTs=Vha&P`?KR%4o6ajmvfWd*RROwl;am4B3ovTJ^( zb5l|`Z%M&i(lAwTkT6yIi_B*F;=i zOv!E!<%XcMr#m0ROLIFqA(E9dpT9!cp|ZC=URqbs;+(~VZGlGNL)N^rO;S&fTC)) zfJHqz0rs?Mp0r*5)XP1?_QjXo2W_LdZ9(c6p37%)7|S;YGQZi@xRnpaDvLXsGSH{T zR1o{i>U^`~pFDId5=P$3(L2*2l}U8hwv4S`*Zxdgt-GanmIY;IzuOHH1XNM zlIduLG0&BY6sMC$>+Hk57em1z)Pad-9OYbCcgy9S$_6%d2B~5$Pqt*S2P7(LWOi8N zfWH>VqS^$`3U@DoEE><=<`j8&4u@zgS&WF49x~NqGVAL96}7c@YQ4q^6&#Fw5fiYy zR0ikPkOQ&oohNI_b2SF3Cj$3(SAB-afgMM)myDrWvA3P?7mJ~Iwc${i!8UBG8|A>Z zvgHruWk!`1dgZP|NwuQZl|D;PeX3jb&XC9a^<@8^f=zg@yafV?pzpN*HWKTV=kp$P z5sMBmTQwCpi&w*>jN}Pl7!$P>zSxgN7e6tQ3=A1C3MxI!0<8Qm>c;xPbDbkmj?@>& z+oh2Eg0l;;B|C*CTR${bW6_s+hL;D3%hRJ@@&1BPX@-j=hCq~SG?1_jtwl19BR2P- zR6?Wv3-$l1T#Rp1E?=F)r`fy|P1}#}k==1o`_BZ43&`uif+ z6}GqIGb2l!0K$+1Dy8>5S8=v3EqYCQE)0* zrK31R+sdNPzfQ+mA^ujcvxONB%>J;eb0&DAbBsQW3(;?*yl@>n2nsq4le#=VZ5uoa z(%M3o{E@2ba9wrxp`JlS3vLt?HBFO;0Udati>1Q*JhOG7ZFbRjW!#!ISfQc>kNsYW zt{f``X!@=H7^{D9`q=v!-9FFO>_4&Y;H~&1py|Y^%W{Ao0zB zGjeS5Fcj>!2HE@$Vj|G0QD`1*L~nrxlWuIecg78j>x{W^hU44joznaA^&# zOa~Zr7msQn)gli{6ms9PckJRJd^=}Dp#vj6Seq5AiQeD!p1n@yQF9Pyv{)Jm$hfd3A~3(5a~7zbMqZf~=OJLU{i*vVJ*{ zPwK@|lq_3A9}U9JBRHwp&NP#R?pDE4R6OKgSC@M<&)&6o&fBXykz0xTFRktm&DYP6 z!pH`4(G~jB*qII@rUUU;z<;ePaw;QUUYBuw^rK`rtcTq0q52Q0aSj$zsd*CT@kWCe zYxgEfxs;=<256PXQ2&N{Z+$zUBztFoKK6FRpZl(`oQ3{OwjvxcS^Zh(qp8$=3rEQQ zLugjycnm#hzx%&)W|Oi3cc*yWpm<#6QFi)=t9W2f3{HE|x{8BzSa7W(zb$!ZNb8%T z!t+TG#fy7h?-6a!CnW#N9Pv1{rz4dBu~cdQ>e`MD#;Hielv{$m_RUCuSU{jW{qDH zd&W6^6}I?a29E79ane*J`0>{XX)fYHqy=Uk3MA)Cs)~n?OCCw2R8t3b?M0a%e@;#r zub>EIhc@7js5*D-e>N%AsE|X~dQI6R@+eNx0kWfDRk1H}P<2@SRxX(!H z`+ohGEZ!QOW1QFiovW(lYsT$ERBJbZE4N4)OJhxMa}u#OM)*S6A)dYHgs!QrlkNP> zE~CM@Z57PEg@ZE3iSd~|UCB`FR+!e#{gQ6=2%iSZdoXYHz|493ur}nKM;iZS)cfgG zyfwSYz2)6nf#;K=lOsB*r|~CS5DL4vd375rkGIV!@g2O+4?j zEp~I1g|mt2zu}qMq9?SU%N(}jJuy9#hR!xsNC=`c7JZp+`*ia-l)V8VaJr7~EC8KJ zj8L2XWsK5&GsmuSFP7rx9#8Nd6~tpq%j4{L7cxo>Qu=Y7sfnw054ErjBMmIz?!t8a z9_wiax_Cu!>#^^tKWfUVfK@VeGb=6B;#k^t+g4a9F79Hq0f3M_@%c}xC#ISB%n`R? zzw=HNu5oto+`7;gLo`%_s2xf5k=_t+`ieP`tl$hMq(adGQs&TL;j=@y>?vVz48i>- zhA7T7caTf2SNO#<`fd=9>8Hp4q(;59B5Eq}RV^{MDUxjr92x}oS>DDQ6>EkRx>%>* zm2yfaY8$T}0Z-)_bAldKytJqQhUPk)_pA|K!C~EDg_r~@_Ee*=OSQcjkN+w=iD~#? zL+eSzrlHs1=f+-VaO7q4?&)v66~p-P>|Of~z!BAm4=_%bhx?WoRQ6j8NbYBeR_}w3+6|vz>_(N| z-?sD!CL7ypLZ{SL)_n-%#_MPDdAy_!shC8Z-8N5;z9@YEFC+R32HFdi9G@w}^T@ar z+dm6-r9<9EWQZUh(E&4zsv2mweaft|Y%8OV(hfV}&pY-XqYTwVX&mZ>t<^W1!yTqS zeirh)Iw7jV5EdSnTtv(+QR<#xk|Q?QE}T>7bXHwQRwt6zet$l2?a7i7!UyC9T=QhG zetFyQ{bP2`yJrP$fGhBVwJAj@dNBUTrLY@n`W8Xdga%PdFP&WAnv^~mspflGqAQzk zCCL9OsBDJ0+RGo^*Bfc(2%Oh0ckO@dvVYo#1$lhu+MUskVwJ^jjT#rqPy1&gA7K$$ zmF259f|AA@Dfz?1u{asJm&1Cb390Vq=+_c4{L^Am#l7{|Z%G7o&h94!YFnc2E%bhJ zg>BOn$DZF(WC#ep;3wOBf*Apza#jAv`&$t!#NL$;I<3rD;a|uqLa`szd=$Zp1v)v4 zds1i8bzp98`5#0gw5*&nJgl6Q^SCYaB^Z0Q!%q>pqp9q7rw*E@`ebUzW(Awn1Iozi60l< z%8IKV++JVg!VPumH&^GwTyR<78t1&5+e*DHeJZrF>6@r?YJYfTiJnev zsrr0)OHPisiR_?zNtJ z(~>wD%`^CC?Cu$P9crk(_D=|9I<165QEU(s18 z@l-doS4~BQC5C@FDt~H+@VU@B4X=u>u)(Pd z|JWyh0lrUX8q9o4x<+%pK0hi%XA3WVqWs>)+ zN4tB~b5|yqr9d5Qr>&tbcta#@$Pu^R`^aoQ3OgJiBIy1@?PK-3K3e{Gv}z6>1+fs` zf%bRfYWr5jF`J#=%Po$`*4_HMmjlIDXuxohi!uc8VlXuKbiRnyccNib@R@`N7jsdm zSI{Xzunq>kw#8I%vYJURFL54+kD|}ta@~Q{e92$B^8^EQP|!6c-}-)!2n1Yf**rt&cIsfc6xk*^NCJoU>+*C&6Cr6_JckweFp zE&4r^SNQ{1MqrO6ZSn+JLC?YN4fJqLut3(&95fdRC@)zK6QI-bitB9HLUM%J0~-7UFULP4>~a#68_mN1 zANKw;tjXpH9EHIGND+}PB`Q^rDuPlKq)S(kE&|efhmfc=MT*j^kq#n7s?|lF zhKTeQKuCa;bK^tw_rKojz0UV@&c|$aXJ=+-X7`?*or!BboRNX1aS&?=*k&~I^o{dC zP9Wx%x`1^5-Md(=1BTm3YWQ~P-JS~w8J{`*c9c%$R`ByqL3X)H@$**?u6YZhn&@b& zw=Gyxz(u-2lVS@szwc!H0Zspo?GDr#R}%*7R!iOUMil~Q!Jz>Gl-yY3!v56q5;d`xd_miSVBh<`m_UPVwL;a%0N0TQ$ z8D^ZYx^lhL{vx=AJutD`E&v*(xg*nIrH%D1i_uYJg~YS;HU-MkFZbJ$7wB(>!I=1~ zt3yN$hGLzRCT~rC;?M|aNuyU`Vm?P;ov2{C;Nd5Z=r8HApsfF`_h#7rGuA|?qqOXv zN%B2;-mD+{2XocIbH>i|RK$|eDGRpo2fITI1}f`a&KJG= z3PQFXPfc9kkX$|wU4P-IK#8jA`I>7k+8%5)MX_PuVJ`&6G(Aap|MKI-7WQ+5+BJx3)}Y#%GS8@_4l2G zHx~3B1+<%*kUq?u6P411i@4Ao=m+#oTi|2@xFC$Nt(bO-KkZdcfQug3-o6d(oB$oP zAWxyxPliDs9Nqs*QwH2r5GdHMFo?fKAf7>~hbDQ=6)UB+S#cP5Q)QfN%6!Xs5@mIY z3VPbLh_wMyah}i^JsejTD3^`4DruYdI~hbC<|BGiSvGFoD#q^8?yjOX9(%g74xM6q zN6K);Z8h5EKxe%8+#x|Aa7fK)?-l*tNSp??sJfZ<9KmK^EbN@tdx+q)9wtrb+e0jk zq@jjUa)+MeN4FqD=XLbbqs}z31u^4p51m|2ZKcAF!(wyf8Dv}$SQV9PvT*+?8q`Oe zjg(#GN}ceTP?!DD)$H09psOC{!`-dn)Nw`0ST`<9!AgJp5Z~>4O}=S}1@Bt${s~gW z?$GR+RC{aJ7^9-Br3FA?3yZ9rs8hZ zx&_5Dw7?qsY^8i%K?B=JmD}D)k#%R3O7^aYLwmECTMaMb^53r68r#$90B`yp*qatY zKv5B9u$jOUD5dx}5Q=w6L*6gYMy0Ovlnr8bmiGgd7`6C1$Jg>aXqNAXLp$1rS|1<5 zV7g4lC}7;;@X%!Q8}14Jyb%@gP4o@oDy5mHCu-(QU~`TpqS(0QwjjkD!-1(jCWnL5fr3v1HEArq$K<7kCn zJLB&i)tDwA_fas96HFCMR8Y^hACb?$CEVBM8@bA)^JQI5?;?&;((w9$0rHw_^3CQU z^^F>LrTJ(Pk1-R}j5tpK4>D*J(hh=pFNg82J{80T-|hqQ~^VD_+ZM8=;!Wi_X}>fSb90jz859Mz531fRkTSbojc50{~n8VTA7duGEB=a&~9 z&MImotZ~Y5AWEn>OqlaY)(TjU3q*tN;Y<(@Xv3G`N($_EqK6;DfIVo35 z1z*$`^Z-mGczaF=t+W;1GZp~tRdVV@W=4E46oS65@^UhM>otYVek^wCpHD<3b}7yZ z8B{%(_i$O@>J3Zm1E>5%ZNuFfU~{{0j|Uc{Rq0IcjmPN3yuTq1uqQ7F3YY;U*H} zA}9r0C=>h;B_7~!xOgwIR%+A{nO?E3fprw13#lzI^mD!vp7UUk$?{F@8@k=MW#L=7 z5F|6fod^bWJ83-&4J9rfBB2c_NW%W=p%qbfk%%MUQ>+LWJj{|P4K-4vN9c8l8x%|i zCOH>_N0jZRf`>fG3f<%b)Y}rP9rCVRxjR?+L~^y$rA8B;(%>EZ(K@%UF1}BJ4Xi

&^AY%@fnr-Vaq?iD_YONp@w+o=j5^<}fkg^S;n6)u)vXHtLCV zPcU7~2mS7K74v%ksAA9o<)fLvDb>(jv!{~l7oJnGHRpf{fw##AF`wH~w`GPByh%1|wa&v4K)qJwjFwjRE%rq;<5{WP0`E zd3=+NqOH5?{GR7EQ*X6PFnP`UKklnu)^0zWsmmzIZ}Mr)YMOz7L=p+}dd6>w(`Sf> zAUN?5e^~wvxDO#_;M7a$j zHv-m&k`9`T2CLBdQ&jNZ{@1&DQ{8L4o^b-c6q={UZd2)Uu`_V~kwvNS{N&#DgoQ}s ztgF)i{_N)1cNG(VsIY%?GA3eF4=X8y~5|rS;rZ8rk z*xJi?#rx$tIDUcy)o45MI_na$ga-a%|7tVMKi$$!H#T)Y6f0P(5eOc!lR?Pf)#j*5=PT$YNA1Jffsy}Kj1rN z0%1Rz`WLiPZTc`I_IyjtE|)9e_NB~Q4#}Db|L4T5MQAe`9*80?3umZ=d;nqO!B5aZ8#Wr! z1D1=T_P{Y%C-u_37`wnA^)V<-DQyh!ov^4VZhLsDZ<`JUr}!WKB*rDkza%~$9FR&g zwyN++ImOIU=b6x}r!HL-Ypdjg*TR2x2}P@5JHQulZkgR{&C*cftFB!oC=M&lNFcUA zi3KG|t;%fBVSI(>p8SOseKbb5p5-Mn{e zaZjRdb^*+BFPHYa@K5lyErr-3#3Clf^YZo+e-4G8EjP5%Dtrpg`k`hIxo^1lv({4Y z9<%lvX5-X8T$Vi(WO*iwI=y`UEm~w<>Kfp-Lk8pf;4Q?gDQY4TTI|{@RtF?g($3m1 z3`GxJmZHg785Px`9>Sh145!^uK~@i5!HZ~fXuE1&@uXAu^2zo|;VO8>dnsBh3hYL0b{trrVuG5JyU}t7F8eZUgL)xSR|g0laOn z-{^lg$TvP6U-=oH!`9qa6U?1Pz1+f`H=M}pZ1iq`DbhBR=Mye(Z4Rt@1lu%Wt(NlH-4QlNB5Qa z{{GnJ>zh;P^F~YzWjUB*u$s-w+DyVn z*2n}f&@gA8F8_}k7hO_T{l9Gdy>*G-&2XRWX1>08X^+4H_~yh&7mFgrZt1FJZm&P}lS zpdE{?zwTn`{22pgJR8D#_G36~o)2d&2o~L{psDS$_5XVxnKjw`oV*?c#4lO9A;Lj3 zhS*y0td7hFh0*yEPNq~jrhk6+T3|7au`j*k zkp`QIqu{3_Z>^(&3S}A?ku1q$=-y0QaP9Ipl z{dz-jGuZRXd;kB%P5d&XAm1r3JFp}=u}g~&pD9o|GDt7Dmh5^-r_7Xf|Z)&8vq@+}%l3Z<~L08}9C;e{YLlB4PQ};2Oyf`AHU%PG_iqABLXn=!G z>O_vjy?`2lu9|OifIbw*xR?)G?87DFDe8T`B~91`iQ-;6Ya_{eT2-~BY*<*pjgIq%dOQ72Dv@JBq7NBq5%F!%G5)-1L59Jc zy|_F6ceS;03HRipEbq;!AuT1d(K6Ky8tFmne**bxw-U0Q@JwjSiM~p8myx(1NF5HH z*BU7Mt$l4k7e<#<`@3hf-U)}Bu|7z+h?75qiS~1$t-6JUAFw;0pd-9#{P(c5wSsShtKT-;uUY|N?HYcI#$yEE89kq`L>YOBe>bH) zRfo3A;t4tq(Uy8;x(Av~M*cKQ>d|*;jJZdPC;N3^Sz+bVmQfoq=*Y;h2MZzIn9~TN zduHFtr1*7ggTT*qa-fW*)QuUQlyYS}jHv(dZB}=e+f4h~LNzNvJ_1H-6DCc# z6tUnAes3@^)Pzp-v;1dhICj7|{|nik{5WyfoBLkUcf_xs777_|zoaaj^%+`aqIC0T z2r6an;oDa#vQU8iX6IVOP(&7dAHAY1pExWu8x2R48GF4f_{>P&Zqgf-=;d?3~ z-I_2?`2S${_Z<=WrgM(U@Oref(gm{(VOPg=c&)ZF(~Lmyx#FL3TG;<-eZ_9(faUr%l-wbkb&;%eBHApCHD{}iLL~oNJ9rUz zkGptazAzlS6N}EW!uKY#|1NZYwvUgq<;OuCd*IN<6!-AWOvsSFx<+neOLoyct{Oz% z#lQ^AP(h@;w2*#JILrZNzGJKOu~5RxbL`{J|6HV}vMxvzK3Jnma}&k!ddptz*pqd1 z%QGvWbJP|)eLXtuF;;bM(h@H?ZGT2qI9D*}GC97@nwKoI9)o5Vx4subdu zCARJpk4bLx?JKi$=hbiea=SO!o+mtv3FZMKUzKS1u@IQm2T}h7{QJSsL`%Nnh)>BO zOYhk1bH)jN+~4hgeW%;@yE=EK9`Hs7B%XK@{F)OyKz01&`&DGtF81 zKF1{+{`6ND{{*g*+hwL{$Hp{~4}fF-M9M{JpKW2Lxp@L`Vjjn`G8gU1N%ZhFT9oP? z2d+LyTnh3v6`fdj3OPog?u12ouXco+@hWK)yTiD@X@g^qCYqWQsru|9!O^kb*^P^Y z^~u|~s(7t*H`c!)u}?0(fIgrOLmOW{##!#%XQeB^jW^3NkVycDX8po10W;D>TuU&h z?zvZHF%D%<|HR5sMhx8)C~~!maj0fE3csV1aG3U2V^a2XW;mwk(h}TT&G7Dn+6%`} zIpQ@naKA_2R_&0Y|d9fycJLd`U!d zMwdfa^pTa)BnlI<=Q}me8S)&|3Xv8mbml;STxocMG#5Q}>>^p*WR@BpPMMfWEfxpE76A+wrCka}tR4P^YC6?2; z9?}F1uLy^>{TpLdS3XH)1V9~ z+}zmdZf4i3d)<3WQg`kiOLfb;Ti%A9kzn{7H-bMTP|K1y464eXIX{8+E z>#rOW5Il7GMJvUrAJY8CYU}P4bICVVUwin68!!$l#Zwbd|3lyGN!IP0{rT9!&%bN$ z>a*Rkx5*jkC}jUAW_+q%y=P48*~o-Jr~~9CMe8+r!GTUcXW;J+gz;cYLDGwe6M(Ky=i#7EoO_;QmDy|2iXe8YE6>X^9JBM%2DCkWy z02yjI56QPxd1eY^%Mfuib^w9}Qs%1_!S9?ibA|>r9EF_f)h}gPOE5?}=jk}1k~!cm zt~?xGY7-6k{%0LAVehy;(fgRDdu1iard!3HrWWx%tNu>NE*QX$6(M7KW4vIvN6l75 zp_TP0wWV|$_ZSA`p_g)$+p^!5_FryG39~v%_=o7PHGVDbc}^k7H=emjE77DPXUrA< zzWzyjwySxgOAudi7S9?kV49 z-O2}{=g2lTwOQlS&ATxbKMEb#`?@?~YBuP6AR|HzSMoo()m>`F?RI*-BJwEn z6v`PABKctX2YZiLXBejib z8v&+pGeI`W9I27NG*##voTgKR3xGsSOqIn6rfHX{pr*9X#q7)kk|h23aZ#q5pOmVA zxD0QrTD^q2y5~BQv~MqUeoE_nEp5p26izeXFn1eezcFAyx`EfvgN^nn-ZD6R(8Yh0 z=kv=alb&SNqSTQtCBbM8h2lR+olN`625?@{`b$t>D(s9uptRuhH(Jc?$eio-I$gEe zl~YS#`M!ePA>zx+O1enZ7U9qLpB7^uUVLZK z#xv4#Y{o!$uN3cI{c-xe)a~p0v7|_EO$MquQQjW<}HD;iFT z#uJVQTzi+wFMQIB90|JhpG6U;)KFvNh8mr_A@}@018B{{bYZU1Mf3gaA0hwZkF7%jNlAN^Oz>NrXt!z;)R;mzd!i+NQxeBbmA&t zLFMx6L>%+S`a2HM?f9;<09SAS~@WnNr@)%ZTarZ^>;`^eKt4|M*Zw4{(FU^88qc*}7NNI~VN{5<+W6&hj!?Rp@cYjnOcR1xn?|w&-ML1U zJO(%~`$!kl@r2H=81- z6iGvj1+tHzB^JjzmJVOB6LE0!Q=B4T&pr%Fx%Jn_*|UXX9p5oyl7wYwJ==|k*e@YS zt5sz(?Hi5KZeVCJBYq8T&crEurHAkSf@qV#t>j7q zc|tb4pSVu#;_afL3s#~RV%_rRqM_w@J}|xF?B@wV#VEsql%fjREiqPlbsA#J*0dIu zd&&;aIHdQ;Q1kLqh4tZ(&Vueos(P`ko8xnsS|Q zuWIn(sH(ZvK}!WA@1!9%1Ib9x5*$=k`^1Ni2(Rm57Uts$HSRR%5m$YFE;q&zW1TL= zb;Y;HxDEBwVSQv2h(h)0{cw+$oo48(8E2Fn^MH>AplE}BU~X^zw+}n@Xk84nv$VLs z(xARk`^H+e<=5|<3*0a^B6Uk7`ey5GY+Xm>W}Cf>0GP*l<-#D#&5_avhCBn>kPM6x$xoMC((WJUAn_t&)RYa zIYkAXm@xdTz^tkqM^?3~i-n~go}9C4$DC871mx|AwTkw+4`lC2mdEEMEheqe~z zeD4+wg^R;?_PD-BEWux=QpX=3R}+PN=* zG+pTURox*pA^t&Zn5VBObW3bL*nEZ)$l6i=1H&6w^tmPT5zcv^J4J>PNO>cgbFM{( z@C6*#opBOK5X9<91&fACAm_FhfIKn1Us?oSGVkqXP0c$U0l9hvc zz}p>q^}c{)u&Lz0u1dqad4cZd;~}nk@zvsr+=kmx5x~vbK~W;g;Ctuse>!^SvzymV zu%29oo8V@Hxk1(0?I(XQj1YJ!HeF{{(=Jatd0^SCr77yeD)6@mgFfiCy$s>dsEYpLV1b>sv=kIb>g^D z`ljZGC+D_*uu+>V^MI^Lq`XQ9<2QTN5C9UK&o=5p9iu}bpt%^;Vo=>kjM4(#Eant- zrvNgan16uAn>$Q(RoAE84zVIZ*iHDn-_rbIGiz&dE*e+>@}mV1hVe!w2gc2QT4Xn$ zp*>>w!~H)DKj|HP+-3DG@7NCBGqB^BIrsJAg%1c7=5xn5qzhggHV;UaF zYp(fD*vu$jA34O-e>onws~c~s?X8KFo8u_<2WgTBejul5E`hm#ciuEu#)I5Q=+-3s zO1&k`dbX&Aw8%BO@oPEEwI??qbvtVdB=!z!#ygF3W+mA;=FP|yg_7vX;$1{M=384dL}E?a;CqnVz&P z3ZM27c6PN=H4Ulm#&bl~%8gF`Jm%!8fTrmD*!9{W>y5fa2JKfJTIXfUi_P(Eq8R8# zk^h=IX-ULjyt^va(R5)jngPuT4jzE8;f2=nR|97)a}8si4U%JlohVWvE5nij@ppWue{G z&aYRo%>begX*$@>BFcYX1jD-{giZ)e*riaQ{DWmKPL#hONmKb-ULc#ov~_hwloq_V z_a1E-&0Ltd^SH?c|CliNXUuJE4XCo`Y$KED1Gg-e)4Mo6l-R_)(3I*DmDN`;==reM(3`V=(wwRq!$U z1GabrE1tqm(FmpSx2&zZpKp1Dq`xd?Tf!PsS8SV`C}|k}T6d9Bv}8Jq7qsFOel;%D z#=q{e;;}q5`NiL8R2XgG=ZyF%9#X;g!6+eXzLGroP|t4z%Zq~;IAPwuUEhAH>Gwrf zog~&{y77^7tcLNYf>EqCXW*O#s!Wv(G%B()=whi5jYz*cd^op;$WIpB;x86+4a$t$ zj@4mI_xz3nHYX0%t?xM5iB>fXXKQ(Xr7J<~W%zGI-_fg)5w`X|Wm>EJ-<$&nBXS9w z*K*F15cP>?GE`)Xcr(c4t9{z-VC<{>ZmG`{m&(KG$lakQZBk2G$)l#$Q|jQVySb;= z2Nm2MM1-Pp6VOJ@n7dW6PO#-E&;M-X&-{ct3Y?N}=xSmM2VC7IQD#oAHZDb-r7Xjf zd!;_nOTiNnTsn;(RCJyxJkBF@!jp|j`e>{hf3TJFp>*)>4|Er_nzKWp=yYk3NML;> z{bU-2o_BwDH8P)VgQXT}*Wi*;Vz4$S$#|?fmNzwJi{o?^^?q~TrzDAOm*%!d()!4c z{0WkVS>DjBj4W{}SL5?95+>OnR^52cYaEa}Rqa*uBzAbjJ1Aw6e^RMeS*HNn#2E=? zoCjY*1p*2FlZ7H(H%%Z?sjB#w54pZmyC)jNwe>68omAq?i8w2%0}+&3vd0DTi) zEP?OV<9DA5$NsU7dPKMJ0fsEyUFO^x!!=Wyk;%HDHs;PhTTUUgE@firmvFSgyNCzb z$}_sgGi#C$=UH8lx|GJ8f9(5P!t`60Tt>B9Zles4MXOX91V~Ll@zGV>%k|0S*(jSo zJ4zMYtmea5(!KmQ1sam2sw)|cR4Q^BJwwkf@g;5cIoYj-xi>FNTWQPHouSJrmNXsW zZAU7YVDa+gWjFp9d-=o0mv*_78X`O9PIkDb9UJY4pfmn`dMr7%sJUPD-xm<&-o@vs!(7#RzVGF<6)td^iz#M#p`J^nES!ZQ`gEz zKd0|&*14s=B&`C$Z5m;!4^6E@`13=uMMr=~J0jt}{3zB?w3+dSfS?bOb4c~Wcz`|_ zSk0q7EMVK;%&96EQEtm)Sv1az@;3|^pHy^!(IdnPUnDGXoLcW04E||I;=wnht7Fs8 zQnc?%MsL#~7)V>7?Rs7ecTd{!F;K)VhwiL{K60Op7_@FvWR7Q#toGh#{}OyT#q4M80lWrmvO$^poxA zV6Zi{SzE!}k|6%YgIZ}JW+jt0=|CwFq-R)UUm`6xVYfiNt*LsbL&G=NRjl|iiZH5N zW4AJMYwNK3{^8i9+thgwNz4wXS!xk*evZ1&oyWqt2$8w#w@b!BUl>YaBHnGc1GRjk zVzQP<>QxA-E9H212T^NLj^+^mqZ`FYokxD{>JHMSHD5@A4yp4yWH+;UG+BOzG~>Z^ ztw@7fdo8^(iJ0Nd+NR#o7UC_Zl8pR(=FH^jJGGmJpO#gh9V5_(a~s)S5b!_iEyvPg zf$R6EOKN1E4{*Nn`D4`wBbx#`cP$dE@0)vfc(UYRMPI;o@M-ADPPGa6e!%61Fj@5A z>#zG2o$ZscZWDL^s@eYZ+ZQ^}z*J4_t`YMoom3U=ab?Qxf`tH-yXn~f%s;&?adOIf zMVkIn^5v!?2mP$PUR7nD+1F_83nvR5@(kkcC{J|?-{-Ggcfu$;-5-|nd17dy#A&v= zD6Pa&GoR}E55aR4oy zGyw$Ldl|P^^XZPyE29dT2=0`q)!OeNjF+hwImK`~lXgFkO%Ee{^4hiFu=Y>zaH+2B zd}HtihBxBn8GMN>J6?%n3_3zn{d10L^!BX#}kafC*|y#FM`*7reYfnbO(l0TP!rOEwLAiZJp;^ zt&yv**D=VMphLwQ-hlMk&vhe917zw)e(uwCw!cn_DX0ie3Dh&22y_Lar-EHl>JWtv zu>~P1l^Uh<1_-E9x3wr}^}*hfG4Oq4zX^F=)$@sgfEFp}5$YhRuO6gs0OKAFFF+?~{R*sZ0iJ6A<|?xx89^-8KVzRT zip_}K=4P!*JGp?I{Wq2#TE~GzVugO5`#y0uWY{hDu`?UVo+utn&tM^LSE-2BYACqt za`p2qlFbiAu4ir9_dZ91o}7aa$}khFf3Z1)o9)kFW=w^2Y8hL68+Su5%S#~gnkVCt z5BfGev#y4hW12TZw^;Rr#Yu|0L2%nwK*p8Somb@JqYWPY_3C~2s=0RYW@5^r=%;SX zCzjh-3>Xwm)S{O@w(OrUjUls#*6p=N^#j9&zS~*)KxV7!f`227m3?BSycDYSC|hvbE(GH33!c)^k^QevpGL?$+z(?UNrJs zrHkRDY;pIM_1oASq)i>MP8$F>eBew1M-v#g(4JaXHAdWzM(0;{jF)cAiP3$rx=P1a zAUXJ0_cS^U{jJYxZJ@cT-|$u92J4yo*UT{Gj61VHT0u>|T>`QU2&UV8Is6SPRsn^y zavXgK?wxNj@-8U)F(bQf9JK;OtB%%go#fNzTj4+ntaOKJI8w{e-9UDAZGKG-!Y!F& z-Qo`EKTx35fn=(>eJq!<(Cq0qRTBmrWp^ez9}4+1E)_xm*TdM3^O$YqFN3Uhbz2x9hx1PrP+47@pQx6({PLc- zr-l3MZHm>}Roy)r@wru5b9;$NidK z2iz&7R_EqazTQ9#50w&quoPu3Z}S6fY* z2%+|d^QD%W)a&*9-CzxC{?Z(#ZE#O$Ehjho(dPNFV&#;eZu5Uh4zS>(BnQ7U(<$_- z=%@7^Ql2Y5jO+sfJT>0;4~=fxaLV^^OV#ZsGrl7O4sjPRjAs7SFH_Rn&$5_Mq4>TZ zCQ^9g6mXQeE&t)h%YBNu3z<4tXT8T~j}8tmJdU0RT<9zegAsDLNg-%~I3?8C?ge($UE4@4q#u3Ez%orU}}C91`%0@$Hbql|A%i z9dZh3s<}^ucnVFG77$P01#tc?fBj^a!rGybPN_0aGF>=v60>R5G-m~e->toGm|-k? z?gx+B(kVHh>LM1X!D5rguT%KuReQR5`e5JV%O${L4mfc|uN&_n1eM(D8Gdcub1(`(6{<4cv5MS^O~`Y&R_Xf0 zhv0aTSOs~#nw<4Vwr3?ypDZtP;Mms2RF%gZ$7B=Cp%Zd9L zmV~#s4Za!HU$MOTfGBZGh`myx_%iRfar?sihux4UuLF(( zmb%_(r?LkqW2iLUx|n4*9DJyTAR<4k?4=Nkz=R<&LS71Y;Wm`;8-+K(!f8R!&xY@*)(;{-wRgQf3t(e}fuGfA=lI&@CykVYkP&#%1`&N%x~i z>jPOLA~C?D^=rLFo^Rg1xqy)yMTp2i#wXOIndbG(>OivnpWdK%rW)yeWbbWwP#`F` zjOa#6;^y??oa4&y@z;4jW1@XVK4mmXXY$#gM78r;}nz7o#pGZ5>$ z`@fIhhfFniqC5u<%|Nu{`X!X_$6TKTbV#y5TFMm3} zE{R`$6Z?_)U8&k=Sh1-qQ`-r6ui&GKFApa+ij5Hu<~qf4(NJFEAz^Iu+g&UiNQAT( zynRS-3e>oQ;%(aW8lHWsBkatnLsgb~iN|23N-aE??#bJl4C^;TmNZN@2gmzPsNkCO&Rqs)aBcI&-&|lx((}3LRWZgDH zsNWZ#6=oSZX!UI-tEGFEJFN__XzxmvPYo&PJR;Pfi!}(VFlfEx;a%*o^5UY^#)$iE ztPL%)d9hpxvVR+M5>pR@1Nrc$f{BM?olmE_fxt>l|2I}Z06eTw!6zhteW%ptZPDtZ zr@KHsCgMwB?JYLr7h-Oq!qy=~@`gA$i$?pp|9^!fqfm8X$#rBie>mAUsXdb`Ch9!y z01`GZlpdrPQFFyAUsLoKe(jc(NFVMKuSDFK)awn?(-9&b?1HG*fCH?%g`PWZy}bYG z5Dijh8YgxSO;9bL=4vxOr7s5#Y8OUW5w3e)tRBpU6DYT~e3ZKEuib#wLJpo@D2&Gd zH`OYqn**!vLVISTkIFB3bUa+Jx&$PkZ{5x*St*~-xo`bYYSr>y*VxpVk=?duAB=?h zG=#%*)c18m4sH<_m6RZO5lw(i)8+9XC4x4D_Qc~tx|rQK)i zHn#ooIq)3K){rEgD^&R?+8}0=!a5CO3w-vR4#=PHa%AI{d`o{BR`~`vTxD~#J&XrA zk03vQ!j%@xdRSHA^`A^w2ocJ`f?r$!TFW+h7wDQI z^W@X-RugZ5`+fbeE;gf6D7{xe67K)sf>3ZmqL6<8{UER>3%^Sn z%P)N%=o#>9yFkafv$6i$XI?PoVqvvt{+eX*g-^SlqxhINfR?T0hIqs*K z%#jrr4iv5W{VXg<>xz=(+y5yF#po6`XRnZ!Q+nTxd+3p*T!!YNg3 zAbH@_SVs0_51YO(FVTxBjo6w4cJ7qfOgeo(yV1leh19*&9dY!4sy~ki^u9^qV`CpK zFXiy;zi#j3=8MQ1C1)6SvdMX(L0ka%Q|!;NRd4PY`g+c`*D^rvo`?q z;$~(JoUa#5Kc`L^Mvt-zb*}yleFmfvirKWr{kV~U4&-}n7Sy`dk8#G^>3l?P?x$2yc+c^jZHIRBXQ)+b7W_{MmR4h?7t3G`k$Ga>ejXDHRC=X?1%yMa z;l3QzSU%ZuYTZXMY+@-7#6e7Opnq}FkMNWKAy75Z>bajRNZHj#Q5qsDG3u~ZvBP>) zgOeT`Pa9IM_>THy;j|27;$4R{KX=F4;lAbHOEk&_0=KRw7R-Vd+eH+2K1mO5`0z)IVt)!Yq1gm~BI61++VIC=MCBc7qOZfVVYr=&j_D4@i zn4U082i!ULIF%yOBpn;2PSD{{J= zLc~22ZrF?pq_Fw}b+Wfp`Fq~I(#<<%wk6&ojd|jdtZoAuD){cCZM{#SXqw{2ehrg! zJ$_smI(If`uhBBL?PSHy*oii$o6c_bf?ylpxo{Z&^%$};hpc#^e9YjJ6n+ zvjQ(T2q>&!aBD;%W!nlelJ11QvWBYxBu1xZ_&12p2ov`cNE?L^)7a&A;SPsIT(aT7 zkY7=9ETluJKfm@UnEhnI`j-v&+1mh>Z_ok;lj6$v7H!LM0>ivu`1q=Ve+F>nQ`re2 zaNWXw^vIUnoPTTIn)?U4_v01iK)J*1+o+UEhPM|GaH^~(4cMfX_BdA-RqJ^_LQp}H zZei6Oj08upF;+96WTks&1D2&`5QY{zp)g|c$N_7ZgIB-;T5dXc)~Y-C!bFM`nTxJh zdCdnPaUC7PeVT&$H}KjGZ^l@1Dh$f?LHOYP*Uk$?Nx`pg6HPrpz_`ml5Ek%)5yI`P zh_)Vl!Wu75Svtq+D&mPm%^-uDjzi}elvPBc3!CpHcwX>?>U*J9uOmVqt&V;gGQ+mI zOgm-`m1tr$=M=eE@C#*H3tZ*0Ix?rIh=u`|)Xd0g2ax@(&H%NK+Y0=xAcT%iMYMSk znY-YxOFM#dvNgdGF^aaQE&pn9H`B&P@nOQ9a94|3S|{f=`@f1ro}I;!A(^A^Zz?US zZQ9f#aPB%uQARKB=7Se;f)n$`Gkaw|paCvgm%mb<^mV&`$kg|8)#%&)q+)G9ep%-2 zH#TKn=)c1s@Q8Sc6+y z13D!5Ls?`rsVIj&)Zd68PsMU=mBtz|>BsbnJx|_TvOCv0o65aU?jV zi1M1+ns+bDw9<_4Z`kS~>I}s=%&Cv(F69FYBknEu4j!1qRz`(cE$1gp-NuqVefVgx zeD^VSlm(%oE#fbWwCzm7upV*)qwWAKD&@m<#c>xE`xXqf3|nY=03e2xattz@9&ca6)Gm37u`OroztB)r;! zAE@a30${P}?FvNPJAGd%jesjIhDokwqfFllpF;Rqn71zd$IkzDM=~;bnq#vDOj$(| zlK-#yKU4qzNS>tF_6;D<0ZU9N2jwdDK9VM3H0%F6ih}D#ygr+MC&)*+br~qsbc0=4 zTI-64ub+=r>-c}aC(y7_v4RmG+uZ3^3&3LNs)^z5bW3Tv0G}2Po?yQF#UleRPhABJ z&<*xxw9#^ym6ixKkE&$we3SdH5M^+Q)2!=`ma7)>DtF3Ve{cx6%RKu56K9v$*{1^?%mOX|7mm!D@QgXROu8? zEmDvP*zn^%f0fh(3x8i`7Ii{1+>jx=!S0Q+H%(XTdQ6~Zy^f{I47R?^!yaVmuHOyJ!0zJ^(ROKF9=UnbjVk@O-bE zxOXox$LP|!t0(ZxSq)%Us-geHzF}Q{NQhPEqwZ1rY!wL@P|J<%YO!O7@;KA-z?s09 zY6n5k&BQ*SJR4a=o`pQ$96E0>3)jm`3G~AWW?im%q4uWu9WGH_;3QCpz9P3^r8mGw zFHcG*CF;myY53v_;G0bkBj*1!(~^if9h>HN30c<~>ji>}2bUt~(agetKdBK_N1;CIF^beMcot>n0{cCm;$Q`2FKi&2ZuRgI8DI z0&a9NQCnj{!l24sKcCB@G=NQI%%q;f;O#2EF#>;bo2!MTd=pdwMm^NGQV+GoX_&qB z0=^`lo*X6+F1Pr1s0ZUhkGOP;BQW^88TFOP+#wH5pgY^eJSO0LxgP@p>QBynOcCB{ z5V#=Ki9Gt*u300+`pU6TZRM3+8W68X7CB^Dgz$UUFr`8VMDvlw7Bt!3GSgt67wrFV zBs8g}+P*#)VsB+%Cu{&jbKVU$*sAB?dM+ym)U@=tI&P=J!53jL$pL^?(Ts5cN|hN% zr8sB$NF9N`EIM^@+Oo3tK;sB<-Y87Rv8ZFhd}RG?_3(@^WUN*xO=S?5Gr}4P zFwlc{zYrf+u|K z4ne3z)H}$^+cU!vF~QVf+MObdNr<##pv-mw#Jw}34ZByjQf z$|>6;*&xAU5mp~0uij}v*qjKU1y(b(!fG|XOs~paIIBstC`p&!OnX?$rMc*I&0z68 z32JzCt;?_OQb`bwyH)FmkDpKSnKdY0iG5 zxZVG&x9f~+a%uLDpwbi&q^Ji1N-t6cX#oN0y>~>g1cZnb0ZD>l3?Rrs1f+{J0clc0 z2^c^H1nE6MMCpVeC857h&iV4*-~HXMH(&B0+1;7h+1cHho%tuSI)KRX5uOH+C|8Px zFWS`ZLdmM}2fl*soc%5kMk-qfZLV)LR}dFj*k2nY_Pg5`(dtR-UFUPfiH3Sk7t3_D z_sKeKC>j4qW7d2sbkSl`e@q3*CUv^xwX<-r%uHUsR=ysCL&)=1y&&WwjPUC%711?Hc8FjJy8O9mbaU4a) ztIu8oWwld?tWYF*E+jLz^w#7ALXLhzv&`^?yC<9-3FUBCBX(#F#M_JSh1K9E+1Gg0 zK;HBR!r|Qy#juQ^Ng2!j`I$w=p|2UsyqR!+2`v-;Hh&vmI3N3Yu!O0_crhA(nrK|~ zX=3K2{6qx^P7$a(N#po%iT}YWN0~XLZgt@b6CVCGv_nTSGYJg|hB9Y2=wj(m%aVS| zoH(aDJ5pp7aFX_O`Kvd@4z%Yzw84iz;rc|$nJZ)F=wW8T*zphW5woftx4&rphAJZa zS!TAZQ#rv}r}n$Wu3+s(4y2@y@K;B+y4ilQQKyf6`qEJYU8=1 z1fc_yZargH^xag&!Fmb!bMwqw?n?6@EJi8eYQW53uvoXB$br|w#-JMyu92Zd>4>2B z!zi1d1J7^^&i~4u0YrE8i5XF@EbrQ+Xwa8+DFn$1-Grv?mebmY&}6B)bSYNgPuS`zlJa*@VyxHrc5w z%Ma&$XL=)!#Fy$$n$pk-iLUdDKk88oY_G1I$$2-yt7?F=M_&W&1gQ!7-4QHeJcq}n zSmxAizJpZYla?Y0|1%m8M(5uU?2a%jUz({FJ2;v;+b&b*7h2C%-Ns))D+7EVakFy_ z_R#_)vbpl@G?{g&aq^bn@8IF{IZ3RCrj30qt&dgz{&XQj(^(U7YccNKi;+)lw-*^a zrlkhm6%2wWuCK}Rj*)Cp;C@)4%$-8em}YJfP*HNefZ{-kyUI1oco*I&!H9hLw>mE+ zr0g`24fJNm$+VkyZ#_QqZ*AVAX*i|0r8SkIbvg<&U)B6qkFTv#(t1bmm+a>dy^RZS zwkvAlKkLiikWYSE*ve9z!&gD5#T6(LIJ5RNhf~+L}w|_meD?Uo@!Rq6;`=1cY z!4;zxIp7KftMxjh##^vl-hclnv262eS&)A@6uYddhU=H0356o8z z*@T9yjdl%1br=>sDnqKUZ}+rBgZvtn=c}?z`bQ*`%gt?f#Ay6fm65rw^?%n~Z^+GR z>OEAP&paQLX|1B1ayh76$y@t6=h3hIwOADGjJ7lHPi)loBFx(k@QA|x;BSL(8sGfSEzR7Qj%D4 zy9t3$c)NVL^h;Cf^Yw7emFSam<)CA}by|J8-%g3|tZnM{i8-X1Gc51Bn(qgdV}S#x z!-J_~tbOHdvf?)b$n;b4VChkhwpI06lFWfRUeGxOM zZX;SSFxI{t|9o9(F`ecKN@!gJ|Co&5lyxH}fe%?NgfP zz~ZJKzz-o$oWJumUR-m+CQC(gNCVn2xt+v1ry?lNz5f=iwS zTdr^t_Z@MqB@Z?yPXN%LSf=s;jMG;ew$f#Bh}k#($YzT($8+_j6W_jxY@7nYhDYyR z4^MSkktn(p5U4s&wNr&$IG&qu0f1X)=!t{mQ+k_L_wQp+GgPt$Fq`p^9cdMf47{ea zrQy4Nq2<~7319(yy|^nEo4<+tSZ&ls0U`qEh%FSq>A@S+Eb>M#=r4hIRz)YV~0qMRMhMz24 zc|M3|nmH_jOwh*_dwP+M$_CjeJ5Yl!<+C7Fo#*12XKDxRqW3iqtAta_7j0cqBJY%t zi$4_|r`u#oWh){DubGDKA4|g6s&ytZXHwU4C1C!ID@Ssz!ZI~MuhBv~FUs2D-qpf< zAz0z2=s=Y@pA$3&O~oO94MgCDyD))XIy#tWBP>@@3#Y;OYoC_|sB5t)dhpP&sE=Ka zwI=A9$Ujw+1MEpLA|2OK10I(VUWV#6^B{SLdI*wUhKRfaFX=b!Q+rFp5Fd>9bZqQu zB5ee-IE2GDWRtN>c##xZ*#ZgErD4zHdl-*zQPjkOzTJ z=yCj6Z1#F_8JziN0C8W)+tm&!Oi+=R_W2X+29+mIHcrz0Q~@Fet#`5Q5D$@q>iAHv zPb-B2G8vK1Gq){6l*!X)~)E@x_Ho8Td@9bQB`$h*79xZLiD z{Gm|u2EOLj3hF@B=0khXGUQFXg6c`%R&}>E3TnAgKOR2)$Nsswj<~(D@Il(EF=55s z=tj#T3rjWgS)qe3GWJAz!DwtYwC>4bA0_KaZJG=p0PJX^uE)<@5+PbUxSUL{jb2XI zDHi%R`f3?vOMRM3PRv7PJ(TI_I}`AS>_S!MtTHh?&$go~)~2`w0PI|Bl%_Dr@JkOS zIz2MFM=&=EIFMEO(9XVzUa zgnp7=^rl?G+rl%^YJS+~^SXAeZJ0iF$mr`uqi$Ie{t5BNYlnRy=H0Q1{nJ;@oB%HW z0?}j?eW@D6dr)1o6WlYb?g>@|7FUaAKW{gF)c9KW^f??HvyM^c{o+b>pbAD4<^=i9 zTs#XzC2V?kta25artS)ZJH{@^XYyf~(cq<&<%Eu0R7GGZGeb0ET zUQ+?DD+Tiqn{vhqyt(AA0$SBl98+WQNn{<%S2iokcdN6NQY?R%6>O_866t}T`Hp=Y z*?E4LA}#RFv4n>6n4SBxeP#i}sHq>t0&jIV}>+0zLI_!(NY+RI|~5Uh1rr|&PC{fNOGJaeo+lS+F<=jt~l4Q zjZ@k=M@(0HGbRP8IA=*Ex&4jucQKxOC;|E)mLj&qep2!ZHA>Hh@zVphMNqB&HHt7z z*O-0#EqeAA9;j`Ik&}!Uk-I23VqRxMA)~O%mk+J^%I83C#Jtk2;m17E9P51MiT5u% zyfUvdBfu^8fb+e=uvmEUHR(MiHRYsQ@Nto%^kqvG578s~$bxjt^X%Qo z5x=j<+#*(OH31S^BDMx{8_f@Hn@of{tJgmCjqj58XcxA6La*3=MN zR9UaBeN853>3+(XT~H0`x7|s2V}2JUYWcoO(?_(oF-oTB(KTEJQCb@zc%aRdZ$(sj zN3yvPHpcp7c?1;2)|%D{K;SNW7x`TmH^8pRJ-A172Hoq=?;d`R`+>o0r2-Kw7Vk7J z4~v#VF)RQ;rYHK?cmcpwx{Dvw2W*%C;IhE5z0A)AD^EWipjDWiC;q2BhqdbopzIko zScebI2nA;4&!_mI{rYtofQN$Lle+6udewp1M`J9?*5WdUA@o2!f9Vj9Z}PJ_3MQ6)~Gf%ofF9MDYLh)6{zmc{V3FOR@Gs-BPTfM0;T ztlV{JStV&X1sgdzRe3p8MFmM&SyfrtPkfu;EdEymU$__SZs`BrpiRrTfySEQO*7px I?OV_O1Bz9-`2YX_ literal 0 HcmV?d00001 diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index cdbdf1fe2..f24e76d6d 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -42,7 +42,7 @@ fast_sync = true # - EXPERIMENTAL # - may be faster is some use-cases (random reads - indexer) # - use boltdb build tag (go build -tags boltdb) -db_backend = "leveldb" +db_backend = "goleveldb" # Database directory db_dir = "data" diff --git a/docs/tendermint-core/running-in-production.md b/docs/tendermint-core/running-in-production.md index 1ec792831..9cb21fc54 100644 --- a/docs/tendermint-core/running-in-production.md +++ b/docs/tendermint-core/running-in-production.md @@ -8,7 +8,7 @@ key-value database. Unfortunately, this implementation of LevelDB seems to suffe install the real C-implementation of LevelDB and compile Tendermint to use that using `make build_c`. See the [install instructions](../introduction/install.md) for details. -Tendermint keeps multiple distinct LevelDB databases in the `$TMROOT/data`: +Tendermint keeps multiple distinct databases in the `$TMROOT/data`: - `blockstore.db`: Keeps the entire blockchain - stores blocks, block commits, and block meta data, each indexed by height. Used to sync new From 86cf8ee3f96fbff39ebf79d58e186201a2bd2222 Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Fri, 10 May 2019 19:45:50 +0200 Subject: [PATCH 202/259] p2p: PeerBehaviour implementation (#3539) (#3552) * p2p: initial implementation of peer behaviour * [p2p] re-use newMockPeer * p2p: add inline docs for peer_behaviour interface * [p2p] Align PeerBehaviour interface (#3558) * [p2p] make switchedPeerHebaviour private * [p2p] make storePeerHebaviour private * [p2p] Add CHANGELOG_PENDING entry for PeerBehaviour * [p2p] Adjustment naming for PeerBehaviour * [p2p] Add coarse lock around storedPeerBehaviour * [p2p]: Fix non-pointer methods in storedPeerBehaviour + Structs with embeded locks must specify all methods with pointer receivers to avoid creating a copy of the embeded lock. * [p2p] Thorough refactoring based on comments in #3552 + Decouple PeerBehaviour interface from Peer by parametrizing methods with `p2p.ID` instead of `p2p.Peer` + Setter methods wrapped in a write lock + Getter methods wrapped in a read lock + Getter methods on storedPeerBehaviour now take a `p2p.ID` + Getter methods return a copy of underlying stored behaviours + Add doc strings to public types and methods * [p2p] make structs public * [p2p] Test empty StoredPeerBehaviour * [p2p] typo fix * [p2p] add TestStoredPeerBehaviourConcurrency + Add a test which uses StoredPeerBehaviour in multiple goroutines to ensure thread-safety. * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour_test.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * Update p2p/peer_behaviour.go Co-Authored-By: brapse * [p2p] field ordering convention * p2p: peer behaviour refactor + Change naming of reporting behaviour to `Report` + Remove the responsibility of distinguishing between the categories of good and bad behaviour and instead focus on reporting behaviour. * p2p: rename PeerReporter -> PeerBehaviourReporter --- CHANGELOG_PENDING.md | 2 + p2p/peer_behaviour.go | 92 +++++++++++++++++++++++++++++ p2p/peer_behaviour_test.go | 115 +++++++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 p2p/peer_behaviour.go create mode 100644 p2p/peer_behaviour_test.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 0067c8c15..fbb87eef1 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -31,6 +31,8 @@ - [node] \#2659 Add `node.Mempool()` method, which allows you to access mempool ### IMPROVEMENTS: +- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer +- [p2p] [\#3552](https://github.com/tendermint/tendermint/pull/3552) Add PeerBehaviour Interface (@brapse) - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC - [cli] \#3585 Add option to not clear address book with unsafe reset (@climber73) - [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `-config=` option to `testnet` cmd (@gregdhill) diff --git a/p2p/peer_behaviour.go b/p2p/peer_behaviour.go new file mode 100644 index 000000000..8d27805c3 --- /dev/null +++ b/p2p/peer_behaviour.go @@ -0,0 +1,92 @@ +package p2p + +import ( + "errors" + "sync" +) + +// PeerBehaviour are types of reportable behaviours about peers. +type PeerBehaviour int + +const ( + PeerBehaviourBadMessage = iota + PeerBehaviourMessageOutOfOrder + PeerBehaviourVote + PeerBehaviourBlockPart +) + +// PeerBehaviourReporter provides an interface for reactors to report the behaviour +// of peers synchronously to other components. +type PeerBehaviourReporter interface { + Report(peerID ID, behaviour PeerBehaviour) error +} + +// SwitchPeerBehaviouReporter reports peer behaviour to an internal Switch +type SwitchPeerBehaviourReporter struct { + sw *Switch +} + +// Return a new SwitchPeerBehaviourReporter instance which wraps the Switch. +func NewSwitchPeerBehaviourReporter(sw *Switch) *SwitchPeerBehaviourReporter { + return &SwitchPeerBehaviourReporter{ + sw: sw, + } +} + +// Report reports the behaviour of a peer to the Switch +func (spbr *SwitchPeerBehaviourReporter) Report(peerID ID, behaviour PeerBehaviour) error { + peer := spbr.sw.Peers().Get(peerID) + if peer == nil { + return errors.New("Peer not found") + } + + switch behaviour { + case PeerBehaviourVote, PeerBehaviourBlockPart: + spbr.sw.MarkPeerAsGood(peer) + case PeerBehaviourBadMessage: + spbr.sw.StopPeerForError(peer, "Bad message") + case PeerBehaviourMessageOutOfOrder: + spbr.sw.StopPeerForError(peer, "Message out of order") + default: + return errors.New("Unknown behaviour") + } + + return nil +} + +// MockPeerBehaviourReporter serves a mock concrete implementation of the +// PeerBehaviourReporter interface used in reactor tests to ensure reactors +// report the correct behaviour in manufactured scenarios. +type MockPeerBehaviourReporter struct { + mtx sync.RWMutex + pb map[ID][]PeerBehaviour +} + +// NewMockPeerBehaviourReporter returns a PeerBehaviourReporter which records all reported +// behaviours in memory. +func NewMockPeerBehaviourReporter() *MockPeerBehaviourReporter { + return &MockPeerBehaviourReporter{ + pb: map[ID][]PeerBehaviour{}, + } +} + +// Report stores the PeerBehaviour produced by the peer identified by peerID. +func (mpbr *MockPeerBehaviourReporter) Report(peerID ID, behaviour PeerBehaviour) { + mpbr.mtx.Lock() + defer mpbr.mtx.Unlock() + mpbr.pb[peerID] = append(mpbr.pb[peerID], behaviour) +} + +// GetBehaviours returns all behaviours reported on the peer identified by peerID. +func (mpbr *MockPeerBehaviourReporter) GetBehaviours(peerID ID) []PeerBehaviour { + mpbr.mtx.RLock() + defer mpbr.mtx.RUnlock() + if items, ok := mpbr.pb[peerID]; ok { + result := make([]PeerBehaviour, len(items)) + copy(result, items) + + return result + } else { + return []PeerBehaviour{} + } +} diff --git a/p2p/peer_behaviour_test.go b/p2p/peer_behaviour_test.go new file mode 100644 index 000000000..03ea4969f --- /dev/null +++ b/p2p/peer_behaviour_test.go @@ -0,0 +1,115 @@ +package p2p + +import ( + "net" + "sync" + "testing" +) + +// TestMockPeerBehaviour tests the MockPeerBehaviour' ability to store reported +// peer behaviour in memory indexed by the peerID +func TestMockPeerBehaviourReporter(t *testing.T) { + peer := newMockPeer(net.IP{127, 0, 0, 1}) + pr := NewMockPeerBehaviourReporter() + + behaviours := pr.GetBehaviours(peer.ID()) + if len(behaviours) != 0 { + t.Errorf("Expected to have no behaviours reported") + } + + pr.Report(peer.ID(), PeerBehaviourBadMessage) + behaviours = pr.GetBehaviours(peer.ID()) + if len(behaviours) != 1 { + t.Errorf("Expected the peer have one reported behaviour") + } + + if behaviours[0] != PeerBehaviourBadMessage { + t.Errorf("Expected PeerBehaviourBadMessage to have been reported") + } +} + +type scriptedBehaviours struct { + PeerID ID + Behaviours []PeerBehaviour +} + +type scriptItem struct { + PeerID ID + Behaviour PeerBehaviour +} + +func equalBehaviours(a []PeerBehaviour, b []PeerBehaviour) bool { + if len(a) != len(b) { + return false + } + same := make([]PeerBehaviour, len(a)) + + for i, aBehaviour := range a { + for _, bBehaviour := range b { + if aBehaviour == bBehaviour { + same[i] = aBehaviour + } + } + } + + return len(same) == len(a) +} + +// TestPeerBehaviourConcurrency constructs a scenario in which +// multiple goroutines are using the same MockPeerBehaviourReporter instance. +// This test reproduces the conditions in which MockPeerBehaviourReporter will +// be used within a Reactor Receive method tests to ensure thread safety. +func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { + behaviourScript := []scriptedBehaviours{ + {"1", []PeerBehaviour{PeerBehaviourVote}}, + {"2", []PeerBehaviour{PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote}}, + {"3", []PeerBehaviour{PeerBehaviourBlockPart, PeerBehaviourVote, PeerBehaviourBlockPart, PeerBehaviourVote}}, + {"4", []PeerBehaviour{PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote}}, + {"5", []PeerBehaviour{PeerBehaviourBlockPart, PeerBehaviourVote, PeerBehaviourBlockPart, PeerBehaviourVote}}, + } + + var receiveWg sync.WaitGroup + pr := NewMockPeerBehaviourReporter() + scriptItems := make(chan scriptItem) + done := make(chan int) + numConsumers := 3 + for i := 0; i < numConsumers; i++ { + receiveWg.Add(1) + go func() { + defer receiveWg.Done() + for { + select { + case pb := <-scriptItems: + pr.Report(pb.PeerID, pb.Behaviour) + case <-done: + return + } + } + }() + } + + var sendingWg sync.WaitGroup + sendingWg.Add(1) + go func() { + defer sendingWg.Done() + for _, item := range behaviourScript { + for _, reason := range item.Behaviours { + scriptItems <- scriptItem{item.PeerID, reason} + } + } + }() + + sendingWg.Wait() + + for i := 0; i < numConsumers; i++ { + done <- 1 + } + + receiveWg.Wait() + + for _, items := range behaviourScript { + if !equalBehaviours(pr.GetBehaviours(items.PeerID), items.Behaviours) { + t.Errorf("Expected peer %s to have behaved \n", items.PeerID) + } + } +} From c21b4fcc9309d199afcb0f7f1e1dd5b0f093490f Mon Sep 17 00:00:00 2001 From: yutianwu Date: Tue, 21 May 2019 14:12:40 +0800 Subject: [PATCH 203/259] fix bug of proxy --- CHANGELOG_PENDING.md | 1 + lite/proxy/proxy.go | 103 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fbb87eef1..fe946a301 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -59,3 +59,4 @@ * `Mempool#Update` and `BlockExecutor#Commit` now accept `[]*abci.ResponseDeliverTx` - list of `DeliverTx` responses, which should match `block.Txs` +- [rpc] \#3669 Handlers in proxy routes miss context parameter diff --git a/lite/proxy/proxy.go b/lite/proxy/proxy.go index d3c16d4a1..2b327c61f 100644 --- a/lite/proxy/proxy.go +++ b/lite/proxy/proxy.go @@ -4,13 +4,16 @@ import ( "context" "net/http" - amino "github.com/tendermint/go-amino" - "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/go-amino" + cmn "github.com/tendermint/tendermint/libs/common" + "github.com/tendermint/tendermint/libs/log" rpcclient "github.com/tendermint/tendermint/rpc/client" "github.com/tendermint/tendermint/rpc/core" ctypes "github.com/tendermint/tendermint/rpc/core/types" rpcserver "github.com/tendermint/tendermint/rpc/lib/server" + rpctypes "github.com/tendermint/tendermint/rpc/lib/types" + "github.com/tendermint/tendermint/types" ) const ( @@ -66,21 +69,93 @@ func RPCRoutes(c rpcclient.Client) map[string]*rpcserver.RPCFunc { "unsubscribe_all": rpcserver.NewWSRPCFunc(c.(Wrapper).UnsubscribeAllWS, ""), // info API - "status": rpcserver.NewRPCFunc(c.Status, ""), - "blockchain": rpcserver.NewRPCFunc(c.BlockchainInfo, "minHeight,maxHeight"), - "genesis": rpcserver.NewRPCFunc(c.Genesis, ""), - "block": rpcserver.NewRPCFunc(c.Block, "height"), - "commit": rpcserver.NewRPCFunc(c.Commit, "height"), - "tx": rpcserver.NewRPCFunc(c.Tx, "hash,prove"), - "validators": rpcserver.NewRPCFunc(c.Validators, "height"), + "status": rpcserver.NewRPCFunc(makeStatusFunc(c), ""), + "blockchain": rpcserver.NewRPCFunc(makeBlockchainInfoFunc(c), "minHeight,maxHeight"), + "genesis": rpcserver.NewRPCFunc(makeGenesisFunc(c), ""), + "block": rpcserver.NewRPCFunc(makeBlockFunc(c), "height"), + "commit": rpcserver.NewRPCFunc(makeCommitFunc(c), "height"), + "tx": rpcserver.NewRPCFunc(makeTxFunc(c), "hash,prove"), + "validators": rpcserver.NewRPCFunc(makeValidatorsFunc(c), "height"), // broadcast API - "broadcast_tx_commit": rpcserver.NewRPCFunc(c.BroadcastTxCommit, "tx"), - "broadcast_tx_sync": rpcserver.NewRPCFunc(c.BroadcastTxSync, "tx"), - "broadcast_tx_async": rpcserver.NewRPCFunc(c.BroadcastTxAsync, "tx"), + "broadcast_tx_commit": rpcserver.NewRPCFunc(makeBroadcastTxCommitFunc(c), "tx"), + "broadcast_tx_sync": rpcserver.NewRPCFunc(makeBroadcastTxSyncFunc(c), "tx"), + "broadcast_tx_async": rpcserver.NewRPCFunc(makeBroadcastTxAsyncFunc(c), "tx"), // abci API - "abci_query": rpcserver.NewRPCFunc(c.ABCIQuery, "path,data"), - "abci_info": rpcserver.NewRPCFunc(c.ABCIInfo, ""), + "abci_query": rpcserver.NewRPCFunc(makeABCIQueryFunc(c), "path,data"), + "abci_info": rpcserver.NewRPCFunc(makeABCIInfoFunc(c), ""), + } +} + +func makeStatusFunc(c rpcclient.Client) func(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { + return func(ctx *rpctypes.Context) (*ctypes.ResultStatus, error) { + return c.Status() + } +} + +func makeBlockchainInfoFunc(c rpcclient.Client) func(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { + return func(ctx *rpctypes.Context, minHeight, maxHeight int64) (*ctypes.ResultBlockchainInfo, error) { + return c.BlockchainInfo(minHeight, maxHeight) + } +} + +func makeGenesisFunc(c rpcclient.Client) func(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { + return func(ctx *rpctypes.Context) (*ctypes.ResultGenesis, error) { + return c.Genesis() + } +} + +func makeBlockFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) { + return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultBlock, error) { + return c.Block(height) + } +} + +func makeCommitFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) { + return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultCommit, error) { + return c.Commit(height) + } +} + +func makeTxFunc(c rpcclient.Client) func(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { + return func(ctx *rpctypes.Context, hash []byte, prove bool) (*ctypes.ResultTx, error) { + return c.Tx(hash, prove) + } +} + +func makeValidatorsFunc(c rpcclient.Client) func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultValidators, error) { + return func(ctx *rpctypes.Context, height *int64) (*ctypes.ResultValidators, error) { + return c.Validators(height) + } +} + +func makeBroadcastTxCommitFunc(c rpcclient.Client) func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { + return c.BroadcastTxCommit(tx) + } +} + +func makeBroadcastTxSyncFunc(c rpcclient.Client) func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + return c.BroadcastTxSync(tx) + } +} + +func makeBroadcastTxAsyncFunc(c rpcclient.Client) func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + return func(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadcastTx, error) { + return c.BroadcastTxAsync(tx) + } +} + +func makeABCIQueryFunc(c rpcclient.Client) func(ctx *rpctypes.Context, path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { + return func(ctx *rpctypes.Context, path string, data cmn.HexBytes) (*ctypes.ResultABCIQuery, error) { + return c.ABCIQuery(path, data) + } +} + +func makeABCIInfoFunc(c rpcclient.Client) func(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) { + return func(ctx *rpctypes.Context) (*ctypes.ResultABCIInfo, error) { + return c.ABCIInfo() } } From fcce9ed4db5fd3334230b3645c5198f31fa686e9 Mon Sep 17 00:00:00 2001 From: yutianwu Date: Tue, 21 May 2019 14:23:50 +0800 Subject: [PATCH 204/259] fix lint --- lite/proxy/proxy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lite/proxy/proxy.go b/lite/proxy/proxy.go index 2b327c61f..80343a531 100644 --- a/lite/proxy/proxy.go +++ b/lite/proxy/proxy.go @@ -4,7 +4,7 @@ import ( "context" "net/http" - "github.com/tendermint/go-amino" + amino "github.com/tendermint/go-amino" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" From f1f243d74965c556a119e7c9326af152daf5f0f9 Mon Sep 17 00:00:00 2001 From: Roman Shtylman Date: Tue, 21 May 2019 14:05:56 -0700 Subject: [PATCH 205/259] p2p/pex: consult seeds in crawlPeersRoutine (#3647) * p2p/pex: consult seeds in crawlPeersRoutine This changeset alters the startup behavior for crawlPeersRoutine. Previously the routine would crawl a random selection of peers on startup. For a new seed node, there are no peers. As a result, new seed nodes are unable to bootstrap themselves with a list of peers until another node with a list of peers connects to the seed. If this node relies on the seed node for peers, then the two will not discover more peers. This changeset makes the startup behavior for crawlPeersRoutine connect to any seed nodes. Upon connecting, a request for peers will be sent to the seed node thus helping bootstrap our seed node. * p2p/pex: Adjust error message for no peers Co-Authored-By: Ethan Buchman --- p2p/pex/pex_reactor.go | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 957dbf802..551ed903d 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -145,7 +145,7 @@ func (r *PEXReactor) OnStart() error { if err != nil { return err } else if numOnline == 0 && r.book.Empty() { - return errors.New("Address book is empty, and could not connect to any seed nodes") + return errors.New("Address book is empty and couldn't resolve any seed nodes") } r.seedAddrs = seedAddrs @@ -573,7 +573,7 @@ func (r *PEXReactor) checkSeeds() (numOnline int, netAddrs []*p2p.NetAddress, er return 0, nil, errors.Wrap(e, "seed node configuration has error") } } - return + return numOnline, netAddrs, nil } // randomly dial seeds until we connect to one or exhaust them @@ -608,8 +608,13 @@ func (r *PEXReactor) AttemptsToDial(addr *p2p.NetAddress) int { // Seed/Crawler Mode causes this node to quickly disconnect // from peers, except other seed nodes. func (r *PEXReactor) crawlPeersRoutine() { - // Do an initial crawl - r.crawlPeers(r.book.GetSelection()) + // If we have any seed nodes, consult them first + if len(r.seedAddrs) > 0 { + r.dialSeeds() + } else { + // Do an initial crawl + r.crawlPeers(r.book.GetSelection()) + } // Fire periodically ticker := time.NewTicker(crawlPeerPeriod) From cfd42be0fe39204b6fa3f799ee40b964e27ca0f6 Mon Sep 17 00:00:00 2001 From: Aleksandr Bezobchuk Date: Wed, 22 May 2019 20:02:00 -0400 Subject: [PATCH 206/259] Improve error and tests --- crypto/multisig/multisignature.go | 15 +++-- crypto/multisig/threshold_pubkey_test.go | 74 ++++++++++++++++++------ 2 files changed, 67 insertions(+), 22 deletions(-) diff --git a/crypto/multisig/multisignature.go b/crypto/multisig/multisignature.go index 0d1796890..1e3bef4e1 100644 --- a/crypto/multisig/multisignature.go +++ b/crypto/multisig/multisignature.go @@ -1,7 +1,8 @@ package multisig import ( - "errors" + "fmt" + "strings" "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/multisig/bitarray" @@ -53,13 +54,19 @@ func (mSig *Multisignature) AddSignature(sig []byte, index int) { mSig.Sigs[newSigIndex] = sig } -// AddSignatureFromPubKey adds a signature to the multisig, -// at the index in keys corresponding to the provided pubkey. +// AddSignatureFromPubKey adds a signature to the multisig, at the index in +// keys corresponding to the provided pubkey. func (mSig *Multisignature) AddSignatureFromPubKey(sig []byte, pubkey crypto.PubKey, keys []crypto.PubKey) error { index := getIndex(pubkey, keys) if index == -1 { - return errors.New("provided key didn't exist in pubkeys") + keysStr := make([]string, len(keys)) + for i, k := range keys { + keysStr[i] = fmt.Sprintf("%X", k.Bytes()) + } + + return fmt.Errorf("provided key %X doesn't exist in pubkeys: \n%s", pubkey.Bytes(), strings.Join(keysStr, "\n")) } + mSig.AddSignature(sig, index) return nil } diff --git a/crypto/multisig/threshold_pubkey_test.go b/crypto/multisig/threshold_pubkey_test.go index 2d2632abd..d1d7e803c 100644 --- a/crypto/multisig/threshold_pubkey_test.go +++ b/crypto/multisig/threshold_pubkey_test.go @@ -36,30 +36,68 @@ func TestThresholdMultisigValidCases(t *testing.T) { for tcIndex, tc := range cases { multisigKey := NewPubKeyMultisigThreshold(tc.k, tc.pubkeys) multisignature := NewMultisig(len(tc.pubkeys)) + for i := 0; i < tc.k-1; i++ { signingIndex := tc.signingIndices[i] - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.False(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig passed when i < k, tc %d, i %d", tcIndex, i) - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.Equal(t, i+1, len(multisignature.Sigs), - "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.False( + t, + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig passed when i < k, tc %d, i %d", tcIndex, i, + ) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.Equal( + t, + i+1, + len(multisignature.Sigs), + "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, + ) } - require.False(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig passed with k - 1 sigs, tc %d", tcIndex) - multisignature.AddSignatureFromPubKey(tc.signatures[tc.signingIndices[tc.k]], tc.pubkeys[tc.signingIndices[tc.k]], tc.pubkeys) - require.True(t, multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig failed after k good signatures, tc %d", tcIndex) + + require.False( + t, + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig passed with k - 1 sigs, tc %d", tcIndex, + ) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[tc.signingIndices[tc.k]], tc.pubkeys[tc.signingIndices[tc.k]], tc.pubkeys), + ) + require.True( + t, + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig failed after k good signatures, tc %d", tcIndex, + ) + for i := tc.k + 1; i < len(tc.signingIndices); i++ { signingIndex := tc.signingIndices[i] - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.Equal(t, tc.passAfterKSignatures[i-tc.k-1], - multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), - "multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i) - multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys) - require.Equal(t, i+1, len(multisignature.Sigs), - "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.Equal( + t, + tc.passAfterKSignatures[i-tc.k-1], + multisigKey.VerifyBytes(tc.msg, multisignature.Marshal()), + "multisig didn't verify as expected after k sigs, tc %d, i %d", tcIndex, i, + ) + require.NoError( + t, + multisignature.AddSignatureFromPubKey(tc.signatures[signingIndex], tc.pubkeys[signingIndex], tc.pubkeys), + ) + require.Equal( + t, + i+1, + len(multisignature.Sigs), + "adding a signature for the same pubkey twice increased signature count by 2, tc %d", tcIndex, + ) } } } From 3ef9e453b71fb20c8cb173be4b3ee2b4ef8431c1 Mon Sep 17 00:00:00 2001 From: yutianwu Date: Thu, 23 May 2019 14:26:11 +0800 Subject: [PATCH 207/259] update change log --- CHANGELOG_PENDING.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fe946a301..709065f78 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -59,4 +59,5 @@ * `Mempool#Update` and `BlockExecutor#Commit` now accept `[]*abci.ResponseDeliverTx` - list of `DeliverTx` responses, which should match `block.Txs` -- [rpc] \#3669 Handlers in proxy routes miss context parameter +- [lite] \#3669 Add context parameter to RPC Handlers in proxy routes (@yutianwu) + From 5997e75c84f643a65cc411331e1f47201ebb5772 Mon Sep 17 00:00:00 2001 From: Carlos Flores Date: Thu, 23 May 2019 09:56:57 -0700 Subject: [PATCH 208/259] fix integration script (#3667) --- CHANGELOG_PENDING.md | 2 +- docs/networks/terraform-and-ansible.md | 16 +++++++++------- networks/remote/integration.sh | 6 ++++-- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 709065f78..8c89e0130 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -59,5 +59,5 @@ * `Mempool#Update` and `BlockExecutor#Commit` now accept `[]*abci.ResponseDeliverTx` - list of `DeliverTx` responses, which should match `block.Txs` +- [networks] fixes ansible integration script (@carlosflrs) - [lite] \#3669 Add context parameter to RPC Handlers in proxy routes (@yutianwu) - diff --git a/docs/networks/terraform-and-ansible.md b/docs/networks/terraform-and-ansible.md index c08ade17a..122591be0 100644 --- a/docs/networks/terraform-and-ansible.md +++ b/docs/networks/terraform-and-ansible.md @@ -62,16 +62,18 @@ There are several roles that are self-explanatory: First, we configure our droplets by specifying the paths for tendermint (`BINARY`) and the node files (`CONFIGDIR`). The latter expects any number of directories named `node0, node1, ...` and so on (equal to the -number of droplets created). For this example, we use pre-created files -from [this -directory](https://github.com/tendermint/tendermint/tree/master/docs/examples). -To create your own files, use either the `tendermint testnet` command or -review [manual deployments](./deploy-testnets.md). +number of droplets created). -Here's the command to run: +To create the node files run: ``` -ansible-playbook -i inventory/digital_ocean.py -l sentrynet config.yml -e BINARY=$GOPATH/src/github.com/tendermint/tendermint/build/tendermint -e CONFIGDIR=$GOPATH/src/github.com/tendermint/tendermint/docs/examples +tendermint testnet +``` + +Then, to configure our droplets run: + +``` +ansible-playbook -i inventory/digital_ocean.py -l sentrynet config.yml -e BINARY=$GOPATH/src/github.com/tendermint/tendermint/build/tendermint -e CONFIGDIR=$GOPATH/src/github.com/tendermint/tendermint/networks/remote/ansible/mytestnet ``` Voila! All your droplets now have the `tendermint` binary and required diff --git a/networks/remote/integration.sh b/networks/remote/integration.sh index 8150aad48..fb1b0b327 100644 --- a/networks/remote/integration.sh +++ b/networks/remote/integration.sh @@ -30,7 +30,6 @@ go get $REPO cd $GOPATH/src/$REPO ## build -git checkout zach/ansible make get_tools make get_vendor_deps make build @@ -84,8 +83,11 @@ ip3=$(strip $ip3) # all the ansible commands are also directory specific cd $GOPATH/src/github.com/tendermint/tendermint/networks/remote/ansible +# create config dirs +tendermint testnet + ansible-playbook -i inventory/digital_ocean.py -l sentrynet install.yml -ansible-playbook -i inventory/digital_ocean.py -l sentrynet config.yml -e BINARY=$GOPATH/src/github.com/tendermint/tendermint/build/tendermint -e CONFIGDIR=$GOPATH/src/github.com/tendermint/tendermint/docs/examples +ansible-playbook -i inventory/digital_ocean.py -l sentrynet config.yml -e BINARY=$GOPATH/src/github.com/tendermint/tendermint/build/tendermint -e CONFIGDIR=$GOPATH/src/github.com/tendermint/tendermint/networks/remote/ansible/mytestnet sleep 10 From bcf10d5bae37cd05373bc37c84e6baf818bf0e87 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 28 May 2019 03:39:58 +0900 Subject: [PATCH 209/259] p2p: peer state init too late and pex message too soon (#3634) * fix peer state init to late Peer does not have a state yet. We set it in AddPeer. We need an new interface before mconnection is started * pex message to soon fix reconnection pex send too fast, error is caused lastReceivedRequests is still not deleted when a peer reconnected * add test case for initpeer * add prove case * remove potentially infinite loop * Update consensus/reactor.go Co-Authored-By: guagualvcha * Update consensus/reactor_test.go Co-Authored-By: guagualvcha * document Reactor interface better * refactor TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet * fix merge conflicts * blockchain: remove peer's ID from the pool in InitPeer Refs #3338 * pex: resetPeersRequestsInfo both upon InitPeer and RemovePeer * ensure RemovePeer is always called before InitPeer by removing the peer from the switch last (after we've stopped it and removed from all reactors) * add some comments for ConsensusReactor#InitPeer * fix pex reactor * format code * fix spelling * update changelog * remove unused methods * do not clear lastReceivedRequests upon error only in RemovePeer * call InitPeer before we start the peer! * add a comment to InitPeer * write a test * use waitUntilSwitchHasAtLeastNPeers func * bring back timeouts * Test to ensure Receive panics if InitPeer has not been called --- CHANGELOG_PENDING.md | 5 +++ consensus/byzantine_test.go | 1 + consensus/reactor.go | 20 +++++++---- consensus/reactor_test.go | 44 +++++++++++++++++++++++++ evidence/reactor.go | 5 --- p2p/base_reactor.go | 24 ++++++++++++-- p2p/pex/pex_reactor.go | 24 +++++++------- p2p/pex/pex_reactor_test.go | 4 +-- p2p/switch.go | 17 ++++++++-- p2p/switch_test.go | 66 +++++++++++++++++++++++++++++++++++++ p2p/test_util.go | 2 +- 11 files changed, 180 insertions(+), 32 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8c89e0130..a25afbb19 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,6 +22,7 @@ * `MockMempool` moved to top-level mock package and renamed to `Mempool` - [libs/common] Removed `PanicSanity`, `PanicCrisis`, `PanicConsensus` and `PanicQ` - [node] Moved `GenesisDocProvider` and `DefaultGenesisDocProviderFunc` to state package +- [p2p] \#3346 Reactor#InitPeer method is added to Reactor interface * Blockchain Protocol @@ -50,6 +51,10 @@ - [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode to 16 (as a result, the node will stop retrying after a 35 hours time window) - [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests +- [consensus] \#3346 Create a peer state in consensus reactor before the peer + is started (@guagualvcha) +- [p2p] \#3338 Ensure RemovePeer is always called before InitPeer (upon a peer + reconnecting to our node) - [p2p] \#3362 make persistent prop independent of conn direction * `Switch#DialPeersAsync` now only takes a list of peers * `Switch#DialPeerWithAddress` now only takes an address diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index d181fb1a6..c2eb114dc 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -270,3 +270,4 @@ func (br *ByzantineReactor) RemovePeer(peer p2p.Peer, reason interface{}) { func (br *ByzantineReactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) { br.reactor.Receive(chID, peer, msgBytes) } +func (br *ByzantineReactor) InitPeer(peer p2p.Peer) p2p.Peer { return peer } diff --git a/consensus/reactor.go b/consensus/reactor.go index 937a12351..36e948f6d 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -155,16 +155,24 @@ func (conR *ConsensusReactor) GetChannels() []*p2p.ChannelDescriptor { } } -// AddPeer implements Reactor +// InitPeer implements Reactor by creating a state for the peer. +func (conR *ConsensusReactor) InitPeer(peer p2p.Peer) p2p.Peer { + peerState := NewPeerState(peer).SetLogger(conR.Logger) + peer.Set(types.PeerStateKey, peerState) + return peer +} + +// AddPeer implements Reactor by spawning multiple gossiping goroutines for the +// peer. func (conR *ConsensusReactor) AddPeer(peer p2p.Peer) { if !conR.IsRunning() { return } - // Create peerState for peer - peerState := NewPeerState(peer).SetLogger(conR.Logger) - peer.Set(types.PeerStateKey, peerState) - + peerState, ok := peer.Get(types.PeerStateKey).(*PeerState) + if !ok { + panic(fmt.Sprintf("peer %v has no state", peer)) + } // Begin routines for this peer. go conR.gossipDataRoutine(peer, peerState) go conR.gossipVotesRoutine(peer, peerState) @@ -177,7 +185,7 @@ func (conR *ConsensusReactor) AddPeer(peer p2p.Peer) { } } -// RemovePeer implements Reactor +// RemovePeer is a noop. func (conR *ConsensusReactor) RemovePeer(peer p2p.Peer, reason interface{}) { if !conR.IsRunning() { return diff --git a/consensus/reactor_test.go b/consensus/reactor_test.go index 9440b50c3..b237da6b5 100644 --- a/consensus/reactor_test.go +++ b/consensus/reactor_test.go @@ -23,6 +23,7 @@ import ( "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/mock" sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -243,6 +244,49 @@ func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) { }, css) } +func TestReactorReceiveDoesNotPanicIfAddPeerHasntBeenCalledYet(t *testing.T) { + N := 1 + css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + defer cleanup() + reactors, _, eventBuses := startConsensusNet(t, css, N) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + var ( + reactor = reactors[0] + peer = mock.NewPeer(nil) + msg = cdc.MustMarshalBinaryBare(&HasVoteMessage{Height: 1, Round: 1, Index: 1, Type: types.PrevoteType}) + ) + + reactor.InitPeer(peer) + + // simulate switch calling Receive before AddPeer + assert.NotPanics(t, func() { + reactor.Receive(StateChannel, peer, msg) + reactor.AddPeer(peer) + }) +} + +func TestReactorReceivePanicsIfInitPeerHasntBeenCalledYet(t *testing.T) { + N := 1 + css, cleanup := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter) + defer cleanup() + reactors, _, eventBuses := startConsensusNet(t, css, N) + defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses) + + var ( + reactor = reactors[0] + peer = mock.NewPeer(nil) + msg = cdc.MustMarshalBinaryBare(&HasVoteMessage{Height: 1, Round: 1, Index: 1, Type: types.PrevoteType}) + ) + + // we should call InitPeer here + + // simulate switch calling Receive before AddPeer + assert.Panics(t, func() { + reactor.Receive(StateChannel, peer, msg) + }) +} + // Test we record stats about votes and block parts from other peers. func TestReactorRecordsVotesAndBlockParts(t *testing.T) { N := 4 diff --git a/evidence/reactor.go b/evidence/reactor.go index bbbab3e96..76ea270d9 100644 --- a/evidence/reactor.go +++ b/evidence/reactor.go @@ -60,11 +60,6 @@ func (evR *EvidenceReactor) AddPeer(peer p2p.Peer) { go evR.broadcastEvidenceRoutine(peer) } -// RemovePeer implements Reactor. -func (evR *EvidenceReactor) RemovePeer(peer p2p.Peer, reason interface{}) { - // nothing to do -} - // Receive implements Reactor. // It adds any received evidence to the evpool. func (evR *EvidenceReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) { diff --git a/p2p/base_reactor.go b/p2p/base_reactor.go index be65d2f14..3bccabd64 100644 --- a/p2p/base_reactor.go +++ b/p2p/base_reactor.go @@ -5,23 +5,40 @@ import ( "github.com/tendermint/tendermint/p2p/conn" ) +// Reactor is responsible for handling incoming messages on one or more +// Channel. Switch calls GetChannels when reactor is added to it. When a new +// peer joins our node, InitPeer and AddPeer are called. RemovePeer is called +// when the peer is stopped. Receive is called when a message is received on a +// channel associated with this reactor. +// +// Peer#Send or Peer#TrySend should be used to send the message to a peer. type Reactor interface { cmn.Service // Start, Stop // SetSwitch allows setting a switch. SetSwitch(*Switch) - // GetChannels returns the list of channel descriptors. + // GetChannels returns the list of MConnection.ChannelDescriptor. Make sure + // that each ID is unique across all the reactors added to the switch. GetChannels() []*conn.ChannelDescriptor - // AddPeer is called by the switch when a new peer is added. + // InitPeer is called by the switch before the peer is started. Use it to + // initialize data for the peer (e.g. peer state). + // + // NOTE: The switch won't call AddPeer nor RemovePeer if it fails to start + // the peer. Do not store any data associated with the peer in the reactor + // itself unless you don't want to have a state, which is never cleaned up. + InitPeer(peer Peer) Peer + + // AddPeer is called by the switch after the peer is added and successfully + // started. Use it to start goroutines communicating with the peer. AddPeer(peer Peer) // RemovePeer is called by the switch when the peer is stopped (due to error // or other reason). RemovePeer(peer Peer, reason interface{}) - // Receive is called when msgBytes is received from peer. + // Receive is called by the switch when msgBytes is received from the peer. // // NOTE reactor can not keep msgBytes around after Receive completes without // copying. @@ -51,3 +68,4 @@ func (*BaseReactor) GetChannels() []*conn.ChannelDescriptor { return nil func (*BaseReactor) AddPeer(peer Peer) {} func (*BaseReactor) RemovePeer(peer Peer, reason interface{}) {} func (*BaseReactor) Receive(chID byte, peer Peer, msgBytes []byte) {} +func (*BaseReactor) InitPeer(peer Peer) Peer { return peer } diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index 551ed903d..eabbc4d61 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -204,6 +204,13 @@ func (r *PEXReactor) AddPeer(p Peer) { } } +// RemovePeer implements Reactor by resetting peer's requests info. +func (r *PEXReactor) RemovePeer(p Peer, reason interface{}) { + id := string(p.ID()) + r.requestsSent.Delete(id) + r.lastReceivedRequests.Delete(id) +} + func (r *PEXReactor) logErrAddrBook(err error) { if err != nil { switch err.(type) { @@ -216,13 +223,6 @@ func (r *PEXReactor) logErrAddrBook(err error) { } } -// RemovePeer implements Reactor. -func (r *PEXReactor) RemovePeer(p Peer, reason interface{}) { - id := string(p.ID()) - r.requestsSent.Delete(id) - r.lastReceivedRequests.Delete(id) -} - // Receive implements Reactor by handling incoming PEX messages. func (r *PEXReactor) Receive(chID byte, src Peer, msgBytes []byte) { msg, err := decodeMsg(msgBytes) @@ -303,7 +303,7 @@ func (r *PEXReactor) receiveRequest(src Peer) error { now := time.Now() minInterval := r.minReceiveRequestInterval() if now.Sub(lastReceived) < minInterval { - return fmt.Errorf("Peer (%v) sent next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", + return fmt.Errorf("peer (%v) sent next PEX request too soon. lastReceived: %v, now: %v, minInterval: %v. Disconnecting", src.ID(), lastReceived, now, @@ -314,14 +314,14 @@ func (r *PEXReactor) receiveRequest(src Peer) error { return nil } -// RequestAddrs asks peer for more addresses if we do not already -// have a request out for this peer. +// 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 } + r.Logger.Debug("Request addrs", "from", p) r.requestsSent.Set(id, struct{}{}) p.Send(PexChannel, cdc.MustMarshalBinaryBare(&pexRequestMessage{})) } @@ -332,7 +332,7 @@ func (r *PEXReactor) RequestAddrs(p Peer) { func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { id := string(src.ID()) if !r.requestsSent.Has(id) { - return errors.New("Unsolicited pexAddrsMessage") + return errors.New("unsolicited pexAddrsMessage") } r.requestsSent.Delete(id) diff --git a/p2p/pex/pex_reactor_test.go b/p2p/pex/pex_reactor_test.go index b16a0d919..f4b7cc265 100644 --- a/p2p/pex/pex_reactor_test.go +++ b/p2p/pex/pex_reactor_test.go @@ -144,7 +144,7 @@ func TestPEXReactorRequestMessageAbuse(t *testing.T) { sw.SetAddrBook(book) peer := mock.NewPeer(nil) - p2p.AddPeerToSwitch(sw, peer) + p2p.AddPeerToSwitchPeerSet(sw, peer) assert.True(t, sw.Peers().Has(peer.ID())) id := string(peer.ID()) @@ -174,7 +174,7 @@ func TestPEXReactorAddrsMessageAbuse(t *testing.T) { sw.SetAddrBook(book) peer := mock.NewPeer(nil) - p2p.AddPeerToSwitch(sw, peer) + p2p.AddPeerToSwitchPeerSet(sw, peer) assert.True(t, sw.Peers().Has(peer.ID())) id := string(peer.ID()) diff --git a/p2p/switch.go b/p2p/switch.go index 66e90bec5..31e0aa6e1 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -324,14 +324,20 @@ func (sw *Switch) StopPeerGracefully(peer Peer) { } func (sw *Switch) stopAndRemovePeer(peer Peer, reason interface{}) { - if sw.peers.Remove(peer) { - sw.metrics.Peers.Add(float64(-1)) - } sw.transport.Cleanup(peer) peer.Stop() + for _, reactor := range sw.reactors { reactor.RemovePeer(peer, reason) } + + // Removing a peer should go last to avoid a situation where a peer + // reconnect to our node and the switch calls InitPeer before + // RemovePeer is finished. + // https://github.com/tendermint/tendermint/issues/3338 + if sw.peers.Remove(peer) { + sw.metrics.Peers.Add(float64(-1)) + } } // reconnectToPeer tries to reconnect to the addr, first repeatedly @@ -739,6 +745,11 @@ func (sw *Switch) addPeer(p Peer) error { return nil } + // Add some data to the peer, which is required by reactors. + for _, reactor := range sw.reactors { + p = reactor.InitPeer(p) + } + // Start the peer's send/recv routines. // Must start it before adding it to the peer set // to prevent Start and Stop from being called concurrently. diff --git a/p2p/switch_test.go b/p2p/switch_test.go index 8485a759f..aa5ca78bf 100644 --- a/p2p/switch_test.go +++ b/p2p/switch_test.go @@ -12,6 +12,7 @@ import ( "regexp" "strconv" "sync" + "sync/atomic" "testing" "time" @@ -603,6 +604,71 @@ func TestSwitchAcceptRoutineErrorCases(t *testing.T) { }) } +// mockReactor checks that InitPeer never called before RemovePeer. If that's +// not true, InitCalledBeforeRemoveFinished will return true. +type mockReactor struct { + *BaseReactor + + // atomic + removePeerInProgress uint32 + initCalledBeforeRemoveFinished uint32 +} + +func (r *mockReactor) RemovePeer(peer Peer, reason interface{}) { + atomic.StoreUint32(&r.removePeerInProgress, 1) + defer atomic.StoreUint32(&r.removePeerInProgress, 0) + time.Sleep(100 * time.Millisecond) +} + +func (r *mockReactor) InitPeer(peer Peer) Peer { + if atomic.LoadUint32(&r.removePeerInProgress) == 1 { + atomic.StoreUint32(&r.initCalledBeforeRemoveFinished, 1) + } + + return peer +} + +func (r *mockReactor) InitCalledBeforeRemoveFinished() bool { + return atomic.LoadUint32(&r.initCalledBeforeRemoveFinished) == 1 +} + +// see stopAndRemovePeer +func TestSwitchInitPeerIsNotCalledBeforeRemovePeer(t *testing.T) { + // make reactor + reactor := &mockReactor{} + reactor.BaseReactor = NewBaseReactor("mockReactor", reactor) + + // make switch + sw := MakeSwitch(cfg, 1, "testing", "123.123.123", func(i int, sw *Switch) *Switch { + sw.AddReactor("mock", reactor) + return sw + }) + err := sw.Start() + require.NoError(t, err) + defer sw.Stop() + + // add peer + rp := &remotePeer{PrivKey: ed25519.GenPrivKey(), Config: cfg} + rp.Start() + defer rp.Stop() + _, err = rp.Dial(sw.NetAddress()) + require.NoError(t, err) + // wait till the switch adds rp to the peer set + time.Sleep(50 * time.Millisecond) + + // stop peer asynchronously + go sw.StopPeerForError(sw.Peers().Get(rp.ID()), "test") + + // simulate peer reconnecting to us + _, err = rp.Dial(sw.NetAddress()) + require.NoError(t, err) + // wait till the switch adds rp to the peer set + time.Sleep(50 * time.Millisecond) + + // make sure reactor.RemovePeer is finished before InitPeer is called + assert.False(t, reactor.InitCalledBeforeRemoveFinished()) +} + func BenchmarkSwitchBroadcast(b *testing.B) { s1, s2 := MakeSwitchPair(b, func(i int, sw *Switch) *Switch { // Make bar reactors of bar channels each diff --git a/p2p/test_util.go b/p2p/test_util.go index f8020924c..fa175aeb4 100644 --- a/p2p/test_util.go +++ b/p2p/test_util.go @@ -27,7 +27,7 @@ func (ni mockNodeInfo) NetAddress() (*NetAddress, error) { return ni.addr, ni func (ni mockNodeInfo) Validate() error { return nil } func (ni mockNodeInfo) CompatibleWith(other NodeInfo) error { return nil } -func AddPeerToSwitch(sw *Switch, peer Peer) { +func AddPeerToSwitchPeerSet(sw *Switch, peer Peer) { sw.peers.Add(peer) } From e7bf25844f1ae8374e84b09089a7591e1eed1840 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 27 May 2019 14:45:27 -0400 Subject: [PATCH 210/259] update PULL_REQUEST_TEMPLATE and CONTRIBUTING (#3655) * update PULL_REQUEST_TEMPLATE and CONTRIBUTING * Update .github/PULL_REQUEST_TEMPLATE.md Co-Authored-By: Thane Thomson * note ADRs --- .github/PULL_REQUEST_TEMPLATE.md | 9 ++++++++- CONTRIBUTING.md | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index d4f55392b..e1863c783 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,12 @@ - + + +* [ ] Referenced an issue explaining the need for the change * [ ] Updated all relevant documentation in docs * [ ] Updated all code comments where relevant * [ ] Wrote tests diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2320fb4c..82705f1e8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,6 +4,14 @@ Thank you for considering making contributions to Tendermint and related reposit Please follow standard github best practices: fork the repo, branch from the tip of develop, make some commits, and submit a pull request to develop. See the [open issues](https://github.com/tendermint/tendermint/issues) for things we need help with! +Before making a pull request, please open an issue describing the +change you would like to make. If an issue for your change already exists, +please comment on it that you will submit a pull request. Be sure to reference the issue in the opening +comment of your pull request. If your change is substantial, you will be asked +to write a more detailed design document in the form of an +Architectural Decision Record (ie. see [here](./docs/architecture/)) before submitting code +changes. + Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. ## Forking From a6ac611e771503f5dc331e765a60101dfa31195b Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Mon, 27 May 2019 15:33:41 -0400 Subject: [PATCH 211/259] tendermint testnet: Allow for better hostname control (#3661) * Allow testnet hostnames to be overridden This allows one to specify the `--hostname` flag multiple times, each time providing an additional custom hostname for a respective peer (validator or non-validator). This overrides any of the `--hostname-prefix` or `--starting-ip-address` flags. The string array approach is taken instead of the string slice approach (see the pflag docs: https://godoc.org/github.com/spf13/pflag#StringArray) because the string slice approach (a comma-separated string) doesn't allow for cleaner multi-line BASH scripts - where this feature is intended to be used. * Reorder conditional for clarity with simpler earlier return * Allow for specifying peer hostname suffix * Quote values in help strings for greater clarity * Fix command switch * Add CHANGELOG_PENDING entry for PR * Allow for unique monikers The current approach to generating monikers for testnet nodes assigns the local hostname of the machine on which the testnet config was generated to all nodes. This results in the same moniker for each and every node. This commit makes use of the supplied `--hostname-prefix` and `--hostname-suffix`, or `--hostname` parameters to generate unique monikers for each node. Alternatively, another parameter (`--random-monikers`) allows one to forcibly override all of the other options with random hexadecimal strings. * Update CHANGELOG_PENDING entry for new command line switch --- CHANGELOG_PENDING.md | 5 ++- cmd/tendermint/commands/testnet.go | 66 +++++++++++++++++++++++------- 2 files changed, 55 insertions(+), 16 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index a25afbb19..af4a51b32 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -35,8 +35,11 @@ - [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer - [p2p] [\#3552](https://github.com/tendermint/tendermint/pull/3552) Add PeerBehaviour Interface (@brapse) - [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC +- [cli] [\#3661](https://github.com/tendermint/tendermint/pull/3661) Add + `--hostname-suffix`, `--hostname` and `--random-monikers` options to `testnet` + cmd for greater peer address/identity generation flexibility. - [cli] \#3585 Add option to not clear address book with unsafe reset (@climber73) -- [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `-config=` option to `testnet` cmd (@gregdhill) +- [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `--config=` option to `testnet` cmd (@gregdhill) - [cs/replay] \#3460 check appHash for each block - [rpc] \#3362 `/dial_seeds` & `/dial_peers` return errors if addresses are incorrect (except when IP lookup fails) - [node] \#3362 returns an error if `persistent_peers` list is invalid (except when IP lookup fails) diff --git a/cmd/tendermint/commands/testnet.go b/cmd/tendermint/commands/testnet.go index b4b33e655..f1dd6f16e 100644 --- a/cmd/tendermint/commands/testnet.go +++ b/cmd/tendermint/commands/testnet.go @@ -27,8 +27,11 @@ var ( populatePersistentPeers bool hostnamePrefix string + hostnameSuffix string startingIPAddress string + hostnames []string p2pPort int + randomMonikers bool ) const ( @@ -50,11 +53,17 @@ func init() { TestnetFilesCmd.Flags().BoolVar(&populatePersistentPeers, "populate-persistent-peers", true, "Update config of each node with the list of persistent peers build using either hostname-prefix or starting-ip-address") TestnetFilesCmd.Flags().StringVar(&hostnamePrefix, "hostname-prefix", "node", - "Hostname prefix (node results in persistent peers list ID0@node0:26656, ID1@node1:26656, ...)") + "Hostname prefix (\"node\" results in persistent peers list ID0@node0:26656, ID1@node1:26656, ...)") + TestnetFilesCmd.Flags().StringVar(&hostnameSuffix, "hostname-suffix", "", + "Hostname suffix (\".xyz.com\" results in persistent peers list ID0@node0.xyz.com:26656, ID1@node1.xyz.com:26656, ...)") TestnetFilesCmd.Flags().StringVar(&startingIPAddress, "starting-ip-address", "", - "Starting IP address (192.168.0.1 results in persistent peers list ID0@192.168.0.1:26656, ID1@192.168.0.2:26656, ...)") + "Starting IP address (\"192.168.0.1\" results in persistent peers list ID0@192.168.0.1:26656, ID1@192.168.0.2:26656, ...)") + TestnetFilesCmd.Flags().StringArrayVar(&hostnames, "hostname", []string{}, + "Manually override all hostnames of validators and non-validators (use --hostname multiple times for multiple hosts)") TestnetFilesCmd.Flags().IntVar(&p2pPort, "p2p-port", 26656, "P2P Port") + TestnetFilesCmd.Flags().BoolVar(&randomMonikers, "random-monikers", false, + "Randomize the moniker for each generated node") } // TestnetFilesCmd allows initialisation of files for a Tendermint testnet. @@ -76,6 +85,13 @@ Example: } func testnetFiles(cmd *cobra.Command, args []string) error { + if len(hostnames) > 0 && len(hostnames) != (nValidators+nNonValidators) { + return fmt.Errorf( + "testnet needs precisely %d hostnames (number of validators plus non-validators) if --hostname parameter is used", + nValidators+nNonValidators, + ) + } + config := cfg.DefaultConfig() // overwrite default config if set and valid @@ -181,6 +197,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error { if populatePersistentPeers { config.P2P.PersistentPeers = persistentPeers } + config.Moniker = moniker(i) cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config) } @@ -190,21 +207,23 @@ func testnetFiles(cmd *cobra.Command, args []string) error { } func hostnameOrIP(i int) string { - if startingIPAddress != "" { - ip := net.ParseIP(startingIPAddress) - ip = ip.To4() - if ip == nil { - fmt.Printf("%v: non ipv4 address\n", startingIPAddress) - os.Exit(1) - } - - for j := 0; j < i; j++ { - ip[3]++ - } - return ip.String() + if len(hostnames) > 0 && i < len(hostnames) { + return hostnames[i] + } + if startingIPAddress == "" { + return fmt.Sprintf("%s%d%s", hostnamePrefix, i, hostnameSuffix) + } + ip := net.ParseIP(startingIPAddress) + ip = ip.To4() + if ip == nil { + fmt.Printf("%v: non ipv4 address\n", startingIPAddress) + os.Exit(1) } - return fmt.Sprintf("%s%d", hostnamePrefix, i) + for j := 0; j < i; j++ { + ip[3]++ + } + return ip.String() } func persistentPeersString(config *cfg.Config) (string, error) { @@ -220,3 +239,20 @@ func persistentPeersString(config *cfg.Config) (string, error) { } return strings.Join(persistentPeers, ","), nil } + +func moniker(i int) string { + if randomMonikers { + return randomMoniker() + } + if len(hostnames) > 0 && i < len(hostnames) { + return hostnames[i] + } + if startingIPAddress == "" { + return fmt.Sprintf("%s%d%s", hostnamePrefix, i, hostnameSuffix) + } + return randomMoniker() +} + +func randomMoniker() string { + return cmn.HexBytes(cmn.RandBytes(8)).String() +} From b9508ffecba0ded9b3a401f1acaba67e273ff053 Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Mon, 27 May 2019 21:44:56 +0200 Subject: [PATCH 212/259] [p2p] Peer behaviour test tweaks (#3662) * Peer behaviour test tweaks: Address the remaining test issues mentioned in ##3552 notably: + switch to p2p_test package + Use `Error` instead of `Errorf` when not using formatting + Add expected/got errors to `TestMockPeerBehaviourReporterConcurrency` test * Peer behaviour equal behaviours test + slices of PeerBehaviours should be compared as histograms to ensure they have the same set of PeerBehaviours at the same freequncy. * TestEqualPeerBehaviours: + Add tests for the equivalence between sets of PeerBehaviours --- p2p/peer_behaviour_test.go | 129 ++++++++++++++++++++++++++++--------- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/p2p/peer_behaviour_test.go b/p2p/peer_behaviour_test.go index 03ea4969f..1822844d6 100644 --- a/p2p/peer_behaviour_test.go +++ b/p2p/peer_behaviour_test.go @@ -1,58 +1,121 @@ -package p2p +package p2p_test import ( - "net" "sync" "testing" + + "github.com/tendermint/tendermint/p2p" ) // TestMockPeerBehaviour tests the MockPeerBehaviour' ability to store reported // peer behaviour in memory indexed by the peerID func TestMockPeerBehaviourReporter(t *testing.T) { - peer := newMockPeer(net.IP{127, 0, 0, 1}) - pr := NewMockPeerBehaviourReporter() + var peerID p2p.ID = "MockPeer" + pr := p2p.NewMockPeerBehaviourReporter() - behaviours := pr.GetBehaviours(peer.ID()) + behaviours := pr.GetBehaviours(peerID) if len(behaviours) != 0 { - t.Errorf("Expected to have no behaviours reported") + t.Error("Expected to have no behaviours reported") } - pr.Report(peer.ID(), PeerBehaviourBadMessage) - behaviours = pr.GetBehaviours(peer.ID()) + pr.Report(peerID, p2p.PeerBehaviourBadMessage) + behaviours = pr.GetBehaviours(peerID) if len(behaviours) != 1 { - t.Errorf("Expected the peer have one reported behaviour") + t.Error("Expected the peer have one reported behaviour") } - if behaviours[0] != PeerBehaviourBadMessage { - t.Errorf("Expected PeerBehaviourBadMessage to have been reported") + if behaviours[0] != p2p.PeerBehaviourBadMessage { + t.Error("Expected PeerBehaviourBadMessage to have been reported") } } type scriptedBehaviours struct { - PeerID ID - Behaviours []PeerBehaviour + PeerID p2p.ID + Behaviours []p2p.PeerBehaviour } type scriptItem struct { - PeerID ID - Behaviour PeerBehaviour + PeerID p2p.ID + Behaviour p2p.PeerBehaviour } -func equalBehaviours(a []PeerBehaviour, b []PeerBehaviour) bool { - if len(a) != len(b) { +// equalBehaviours returns true if a and b contain the same PeerBehaviours with +// the same freequency and otherwise false. +func equalBehaviours(a []p2p.PeerBehaviour, b []p2p.PeerBehaviour) bool { + aHistogram := map[p2p.PeerBehaviour]int{} + bHistogram := map[p2p.PeerBehaviour]int{} + + for _, behaviour := range a { + aHistogram[behaviour] += 1 + } + + for _, behaviour := range b { + bHistogram[behaviour] += 1 + } + + if len(aHistogram) != len(bHistogram) { return false } - same := make([]PeerBehaviour, len(a)) - for i, aBehaviour := range a { - for _, bBehaviour := range b { - if aBehaviour == bBehaviour { - same[i] = aBehaviour - } + for _, behaviour := range a { + if aHistogram[behaviour] != bHistogram[behaviour] { + return false } } - return len(same) == len(a) + for _, behaviour := range b { + if bHistogram[behaviour] != aHistogram[behaviour] { + return false + } + } + + return true +} + +// TestEqualPeerBehaviours tests that equalBehaviours can tell that two slices +// of peer behaviours can be compared for the behaviours they contain and the +// freequencies that those behaviours occur. +func TestEqualPeerBehaviours(t *testing.T) { + equals := []struct { + left []p2p.PeerBehaviour + right []p2p.PeerBehaviour + }{ + // Empty sets + {[]p2p.PeerBehaviour{}, []p2p.PeerBehaviour{}}, + // Single behaviours + {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, + // Equal Frequencies + {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}, + []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, + // Equal frequencies different orders + {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart}, + []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, + } + + for _, test := range equals { + if !equalBehaviours(test.left, test.right) { + t.Errorf("Expected %#v and %#v to be equal", test.left, test.right) + } + } + + unequals := []struct { + left []p2p.PeerBehaviour + right []p2p.PeerBehaviour + }{ + // Comparing empty sets to non empty sets + {[]p2p.PeerBehaviour{}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, + // Different behaviours + {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart}}, + // Same behaviour with different frequencies + {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, + []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, + } + + for _, test := range unequals { + if equalBehaviours(test.left, test.right) { + t.Errorf("Expected %#v and %#v to be unequal", test.left, test.right) + } + } } // TestPeerBehaviourConcurrency constructs a scenario in which @@ -61,15 +124,15 @@ func equalBehaviours(a []PeerBehaviour, b []PeerBehaviour) bool { // be used within a Reactor Receive method tests to ensure thread safety. func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { behaviourScript := []scriptedBehaviours{ - {"1", []PeerBehaviour{PeerBehaviourVote}}, - {"2", []PeerBehaviour{PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote}}, - {"3", []PeerBehaviour{PeerBehaviourBlockPart, PeerBehaviourVote, PeerBehaviourBlockPart, PeerBehaviourVote}}, - {"4", []PeerBehaviour{PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote, PeerBehaviourVote}}, - {"5", []PeerBehaviour{PeerBehaviourBlockPart, PeerBehaviourVote, PeerBehaviourBlockPart, PeerBehaviourVote}}, + {"1", []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, + {"2", []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, + {"3", []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, + {"4", []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, + {"5", []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, } var receiveWg sync.WaitGroup - pr := NewMockPeerBehaviourReporter() + pr := p2p.NewMockPeerBehaviourReporter() scriptItems := make(chan scriptItem) done := make(chan int) numConsumers := 3 @@ -108,8 +171,10 @@ func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { receiveWg.Wait() for _, items := range behaviourScript { - if !equalBehaviours(pr.GetBehaviours(items.PeerID), items.Behaviours) { - t.Errorf("Expected peer %s to have behaved \n", items.PeerID) + reported := pr.GetBehaviours(items.PeerID) + if !equalBehaviours(reported, items.Behaviours) { + t.Errorf("Expected peer %s to have behaved \nExpected: %#v \nGot %#v \n", + items.PeerID, items.Behaviours, reported) } } } From b522ad005242e633aa72ee672373043bdcedec2b Mon Sep 17 00:00:00 2001 From: Girish Ramnani Date: Wed, 29 May 2019 14:02:34 +0530 Subject: [PATCH 213/259] docs: fix minor typo (#3681) --- docs/app-dev/subscribing-to-events-via-websocket.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/app-dev/subscribing-to-events-via-websocket.md b/docs/app-dev/subscribing-to-events-via-websocket.md index d745769c3..890b061bf 100644 --- a/docs/app-dev/subscribing-to-events-via-websocket.md +++ b/docs/app-dev/subscribing-to-events-via-websocket.md @@ -2,7 +2,7 @@ Tendermint emits different events, to which you can subscribe via [Websocket](https://en.wikipedia.org/wiki/WebSocket). This can be useful -for third-party applications (for analysys) or inspecting state. +for third-party applications (for analysis) or inspecting state. [List of events](https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants) From 9dcee69ac203aa329f04cc6090d6444aee6564c4 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 31 May 2019 07:25:21 +0900 Subject: [PATCH 214/259] v0.31.6 changelog (#3688) * update changelog * Update changelog - less detail about internal stuff like the `mempool` - focus BUG FIX entries more on what was fixed rather than how * minor cleanup. - remove entry for GenesisDocProvider, it's being undone * minor fix --- CHANGELOG.md | 70 ++++++++++++++++++++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 52 +------------------------------- UPGRADING.md | 10 +++++++ types/block.go | 1 + 4 files changed, 82 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d498060b..4328d4b62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,75 @@ # Changelog +## v0.31.6 + +*May 31st, 2019* + +This release contains many fixes and improvements, primarily for p2p functionality. +It also fixes a security issue in the mempool package. + +With this release, Tendermint now supports [boltdb](https://github.com/etcd-io/bbolt), although +in experimental mode. Feel free to try and report to us any findings/issues. +Note also that the build tags for compiling CLevelDB have changed. + +Special thanks to external contributors on this release: +@guagualvcha, @james-ray, @gregdhill, @climber73, @yutianwu, +@carlosflrs, @defunctzombie, @leoluk, @needkane, @CrocdileChan + +### BREAKING CHANGES: + +* Go API + - [libs/common] Removed deprecated `PanicSanity`, `PanicCrisis`, + `PanicConsensus` and `PanicQ` + - [mempool, state] [\#2659](https://github.com/tendermint/tendermint/issues/2659) `Mempool` now an interface that lives in the mempool package. + See issue and PR for more details. + - [p2p] [\#3346](https://github.com/tendermint/tendermint/issues/3346) `Reactor#InitPeer` method is added to `Reactor` interface + - [types] [\#1648](https://github.com/tendermint/tendermint/issues/1648) `Commit#VoteSignBytes` signature was changed + +### FEATURES: +- [node] [\#2659](https://github.com/tendermint/tendermint/issues/2659) Add `node.Mempool()` method, which allows you to access mempool +- [libs/db] [\#3604](https://github.com/tendermint/tendermint/pull/3604) Add experimental support for bolt db (etcd's fork of bolt) (@CrocdileChan) + +### IMPROVEMENTS: +- [cli] [\#3585](https://github.com/tendermint/tendermint/issues/3585) Add `--keep-addr-book` option to `unsafe_reset_all` cmd to not + clear the address book (@climber73) +- [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add + `--config=` option to `testnet` cmd (@gregdhill) +- [cli] [\#3661](https://github.com/tendermint/tendermint/pull/3661) Add + `--hostname-suffix`, `--hostname` and `--random-monikers` options to `testnet` + cmd for greater peer address/identity generation flexibility. +- [crypto] [\#3672](https://github.com/tendermint/tendermint/issues/3672) Return more info in the `AddSignatureFromPubKey` error +- [cs/replay] [\#3460](https://github.com/tendermint/tendermint/issues/3460) Check appHash for each block +- [libs/db] [\#3611](https://github.com/tendermint/tendermint/issues/3611) Conditional compilation + * Use `cleveldb` tag instead of `gcc` to compile Tendermint with CLevelDB or + use `make build_c` / `make install_c` (full instructions can be found at + https://tendermint.com/docs/introduction/install.html#compile-with-cleveldb-support) + * Use `boltdb` tag to compile Tendermint with bolt db +- [node] [\#3362](https://github.com/tendermint/tendermint/issues/3362) Return an error if `persistent_peers` list is invalid (except + when IP lookup fails) +- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer (@guagualvcha) +- [p2p] [\#3531](https://github.com/tendermint/tendermint/issues/3531) Terminate session on nonce wrapping (@climber73) +- [pex] [\#3647](https://github.com/tendermint/tendermint/pull/3647) Dial seeds, if any, instead of crawling peers first (@defunctzombie) +- [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC +- [rpc] [\#3362](https://github.com/tendermint/tendermint/issues/3362) `/dial_seeds` & `/dial_peers` return errors if addresses are + incorrect (except when IP lookup fails) + +### BUG FIXES: +- [consensus] [\#3067](https://github.com/tendermint/tendermint/issues/3067) Fix replay from appHeight==0 with validator set changes (@james-ray) +- [consensus] [\#3304](https://github.com/tendermint/tendermint/issues/3304) Create a peer state in consensus reactor before the peer + is started (@guagualvcha) +- [lite] [\#3669](https://github.com/tendermint/tendermint/issues/3669) Add context parameter to RPC Handlers in proxy routes (@yutianwu) +- [mempool] [\#3322](https://github.com/tendermint/tendermint/issues/3322) When a block is committed, only remove committed txs from the mempool +that were valid (ie. `ResponseDeliverTx.Code == 0`) +- [p2p] [\#3338](https://github.com/tendermint/tendermint/issues/3338) Ensure `RemovePeer` is always called before `InitPeer` (upon a peer + reconnecting to our node) +- [p2p] [\#3532](https://github.com/tendermint/tendermint/issues/3532) Limit the number of attempts to connect to a peer in seed mode + to 16 (as a result, the node will stop retrying after a 35 hours time window) +- [p2p] [\#3362](https://github.com/tendermint/tendermint/issues/3362) Allow inbound peers to be persistent, including for seed nodes. +- [pex] [\#3603](https://github.com/tendermint/tendermint/pull/3603) Dial seeds when addrbook needs more addresses (@defunctzombie) + +### OTHERS: +- [networks] fixes ansible integration script (@carlosflrs) + ## v0.31.5 *April 16th, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index af4a51b32..9b8fc1cf0 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.31.6 +## v0.31.7 ** @@ -9,63 +9,13 @@ * Apps * Go API -- [mempool] \#2659 `Mempool` now an interface - * old `Mempool` implementation renamed to `CListMempool` - * `NewMempool` renamed to `NewCListMempool` - * `Option` renamed to `CListOption` - * unexpose `MempoolReactor.Mempool` - * `MempoolReactor` renamed to `Reactor` - * `NewMempoolReactor` renamed to `NewReactor` - * unexpose `TxID` method - * `TxInfo.PeerID` renamed to `SenderID` -- [state] \#2659 `Mempool` interface moved to mempool package - * `MockMempool` moved to top-level mock package and renamed to `Mempool` -- [libs/common] Removed `PanicSanity`, `PanicCrisis`, `PanicConsensus` and `PanicQ` -- [node] Moved `GenesisDocProvider` and `DefaultGenesisDocProviderFunc` to state package -- [p2p] \#3346 Reactor#InitPeer method is added to Reactor interface * Blockchain Protocol * P2P Protocol ### FEATURES: -- [node] \#2659 Add `node.Mempool()` method, which allows you to access mempool ### IMPROVEMENTS: -- [p2p] [\#3463](https://github.com/tendermint/tendermint/pull/3463) Do not log "Can't add peer's address to addrbook" error for a private peer -- [p2p] [\#3552](https://github.com/tendermint/tendermint/pull/3552) Add PeerBehaviour Interface (@brapse) -- [rpc] [\#3534](https://github.com/tendermint/tendermint/pull/3534) Add support for batched requests/responses in JSON RPC -- [cli] [\#3661](https://github.com/tendermint/tendermint/pull/3661) Add - `--hostname-suffix`, `--hostname` and `--random-monikers` options to `testnet` - cmd for greater peer address/identity generation flexibility. -- [cli] \#3585 Add option to not clear address book with unsafe reset (@climber73) -- [cli] [\#3160](https://github.com/tendermint/tendermint/issues/3160) Add `--config=` option to `testnet` cmd (@gregdhill) -- [cs/replay] \#3460 check appHash for each block -- [rpc] \#3362 `/dial_seeds` & `/dial_peers` return errors if addresses are incorrect (except when IP lookup fails) -- [node] \#3362 returns an error if `persistent_peers` list is invalid (except when IP lookup fails) -- [p2p] \#3531 Terminate session on nonce wrapping (@climber73) -- [libs/db] \#3611 Conditional compilation - * Use `cleveldb` tag instead of `gcc` to compile Tendermint with CLevelDB or - use `make build_c` / `make install_c` (full instructions can be found at - https://tendermint.com/docs/introduction/install.html#compile-with-cleveldb-support) - * Use `boltdb` tag to compile Tendermint with bolt db ### BUG FIXES: -- [p2p] \#3532 limit the number of attempts to connect to a peer in seed mode - to 16 (as a result, the node will stop retrying after a 35 hours time window) -- [consensus] \#2723, \#3451 and \#3317 Fix non-deterministic tests -- [consensus] \#3346 Create a peer state in consensus reactor before the peer - is started (@guagualvcha) -- [p2p] \#3338 Ensure RemovePeer is always called before InitPeer (upon a peer - reconnecting to our node) -- [p2p] \#3362 make persistent prop independent of conn direction - * `Switch#DialPeersAsync` now only takes a list of peers - * `Switch#DialPeerWithAddress` now only takes an address -- [consensus] \#3067 getBeginBlockValidatorInfo loads validators from stateDB instead of state (@james-ray) -- [pex] \#3603 Dial seeds when addrbook needs more addresses (@defunctzombie) -- [mempool] \#3322 Remove only valid (Code==0) txs on Update - * `Mempool#Update` and `BlockExecutor#Commit` now accept - `[]*abci.ResponseDeliverTx` - list of `DeliverTx` responses, which should - match `block.Txs` -- [networks] fixes ansible integration script (@carlosflrs) -- [lite] \#3669 Add context parameter to RPC Handlers in proxy routes (@yutianwu) diff --git a/UPGRADING.md b/UPGRADING.md index eccb954d3..ccd4f2d7e 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,16 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. +## v0.31.6 + +There are no breaking changes in this release except Go API of p2p and +mempool packages. Hovewer, if you're using cleveldb, you'll need to change +the compilation tag: + +Use `cleveldb` tag instead of `gcc` to compile Tendermint with CLevelDB or +use `make build_c` / `make install_c` (full instructions can be found at +https://tendermint.com/docs/introduction/install.html#compile-with-cleveldb-support) + ## v0.31.0 This release contains a breaking change to the behaviour of the pubsub system. diff --git a/types/block.go b/types/block.go index d793ce9dd..313eb6b75 100644 --- a/types/block.go +++ b/types/block.go @@ -584,6 +584,7 @@ func (commit *Commit) VoteSignBytes(chainID string, valIdx int) []byte { // memoizeHeightRound memoizes the height and round of the commit using // the first non-nil vote. +// Should be called before any attempt to access `commit.height` or `commit.round`. func (commit *Commit) memoizeHeightRound() { if len(commit.Precommits) == 0 { return From 0dd6b92a642b96cc07e37b49de262712a3c293ae Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Thu, 30 May 2019 18:40:17 -0400 Subject: [PATCH 215/259] Relocate GenesisDocProvider and DefaultGenesisDocProviderFunc (#3693) * Move GenesisDocProvider and DefaultGenesisDocProviderFunc GenesisDocProvider, being a provider of *types.GenesisDoc, makes sense to be part of the types package. DefaultGenesisDocProviderFunc, which relies on *config.Config to produce a types.GenesisDocProvider, makes sense being part of the config package. * Add aliases to avoid breaking node package API * Revert to original structure After discussion, it appears as though the best place for the relocated structures is still in the node package. This means that for the v0.31.6 release and into the future, there will be no changes two these two entities' APIs. --- node/node.go | 69 +++++++++++++++++++++++++++++++++++++++++++-- rpc/test/helpers.go | 6 ++-- state/store.go | 65 ------------------------------------------ 3 files changed, 68 insertions(+), 72 deletions(-) diff --git a/node/node.go b/node/node.go index 2d7b3b3bb..0d05deee1 100644 --- a/node/node.go +++ b/node/node.go @@ -65,6 +65,19 @@ func DefaultDBProvider(ctx *DBContext) (dbm.DB, error) { return dbm.NewDB(ctx.ID, dbType, ctx.Config.DBDir()), nil } +// GenesisDocProvider returns a GenesisDoc. +// It allows the GenesisDoc to be pulled from sources other than the +// filesystem, for instance from a distributed key-value store cluster. +type GenesisDocProvider func() (*types.GenesisDoc, error) + +// DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads +// the GenesisDoc from the config.GenesisFile() on the filesystem. +func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { + return func() (*types.GenesisDoc, error) { + return types.GenesisDocFromFile(config.GenesisFile()) + } +} + // NodeProvider takes a config and a logger and returns a ready to go Node. type NodeProvider func(*cfg.Config, log.Logger) (*Node, error) @@ -99,7 +112,7 @@ func DefaultNewNode(config *cfg.Config, logger log.Logger) (*Node, error) { privval.LoadOrGenFilePV(newPrivValKey, newPrivValState), nodeKey, proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), - sm.DefaultGenesisDocProviderFunc(config), + DefaultGenesisDocProviderFunc(config), DefaultDBProvider, DefaultMetricsProvider(config.Instrumentation), logger, @@ -466,7 +479,7 @@ func NewNode(config *cfg.Config, privValidator types.PrivValidator, nodeKey *p2p.NodeKey, clientCreator proxy.ClientCreator, - genesisDocProvider sm.GenesisDocProvider, + genesisDocProvider GenesisDocProvider, dbProvider DBProvider, metricsProvider MetricsProvider, logger log.Logger) (*Node, error) { @@ -476,7 +489,7 @@ func NewNode(config *cfg.Config, return nil, err } - state, genDoc, err := sm.LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider) + state, genDoc, err := LoadStateFromDBOrGenesisDocProvider(stateDB, genesisDocProvider) if err != nil { return nil, err } @@ -1002,6 +1015,56 @@ func makeNodeInfo( //------------------------------------------------------------------------------ +var ( + genesisDocKey = []byte("genesisDoc") +) + +// LoadStateFromDBOrGenesisDocProvider attempts to load the state from the +// database, or creates one using the given genesisDocProvider and persists the +// result to the database. On success this also returns the genesis doc loaded +// through the given provider. +func LoadStateFromDBOrGenesisDocProvider(stateDB dbm.DB, genesisDocProvider GenesisDocProvider) (sm.State, *types.GenesisDoc, error) { + // Get genesis doc + genDoc, err := loadGenesisDoc(stateDB) + if err != nil { + genDoc, err = genesisDocProvider() + if err != nil { + return sm.State{}, nil, err + } + // save genesis doc to prevent a certain class of user errors (e.g. when it + // was changed, accidentally or not). Also good for audit trail. + saveGenesisDoc(stateDB, genDoc) + } + state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) + if err != nil { + return sm.State{}, nil, err + } + return state, genDoc, nil +} + +// panics if failed to unmarshal bytes +func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { + b := db.Get(genesisDocKey) + if len(b) == 0 { + return nil, errors.New("Genesis doc not found") + } + var genDoc *types.GenesisDoc + err := cdc.UnmarshalJSON(b, &genDoc) + if err != nil { + panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, b)) + } + return genDoc, nil +} + +// panics if failed to marshal the given genesis document +func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { + b, err := cdc.MarshalJSON(genDoc) + if err != nil { + panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) + } + db.SetSync(genesisDocKey, b) +} + func createAndStartPrivValidatorSocketClient( listenAddr string, logger log.Logger, diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go index f5c923bf4..033015d11 100644 --- a/rpc/test/helpers.go +++ b/rpc/test/helpers.go @@ -8,10 +8,8 @@ import ( "strings" "time" - "github.com/tendermint/tendermint/libs/log" - "github.com/tendermint/tendermint/state" - abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/libs/log" cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" @@ -167,7 +165,7 @@ func NewTendermint(app abci.Application, opts *Options) *nm.Node { panic(err) } node, err := nm.NewNode(config, pv, nodeKey, papp, - state.DefaultGenesisDocProviderFunc(config), + nm.DefaultGenesisDocProviderFunc(config), nm.DefaultDBProvider, nm.DefaultMetricsProvider(config.Instrumentation), logger) diff --git a/state/store.go b/state/store.go index e1fb5ba60..0301bc7c3 100644 --- a/state/store.go +++ b/state/store.go @@ -1,11 +1,9 @@ package state import ( - "errors" "fmt" abci "github.com/tendermint/tendermint/abci/types" - cfg "github.com/tendermint/tendermint/config" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/types" @@ -19,23 +17,6 @@ const ( valSetCheckpointInterval = 100000 ) -var ( - genesisDocKey = []byte("genesisDoc") -) - -// GenesisDocProvider returns a GenesisDoc. -// It allows the GenesisDoc to be pulled from sources other than the -// filesystem, for instance from a distributed key-value store cluster. -type GenesisDocProvider func() (*types.GenesisDoc, error) - -// DefaultGenesisDocProviderFunc returns a GenesisDocProvider that loads -// the GenesisDoc from the config.GenesisFile() on the filesystem. -func DefaultGenesisDocProviderFunc(config *cfg.Config) GenesisDocProvider { - return func() (*types.GenesisDoc, error) { - return types.GenesisDocFromFile(config.GenesisFile()) - } -} - //------------------------------------------------------------------------ func calcValidatorsKey(height int64) []byte { @@ -84,52 +65,6 @@ func LoadStateFromDBOrGenesisDoc(stateDB dbm.DB, genesisDoc *types.GenesisDoc) ( return state, nil } -// LoadStateFromDBOrGenesisDocProvider attempts to load the state from the -// database, or creates one using the given genesisDocProvider and persists the -// result to the database. On success this also returns the genesis doc loaded -// through the given provider. -func LoadStateFromDBOrGenesisDocProvider(stateDB dbm.DB, genesisDocProvider GenesisDocProvider) (State, *types.GenesisDoc, error) { - // Get genesis doc - genDoc, err := loadGenesisDoc(stateDB) - if err != nil { - genDoc, err = genesisDocProvider() - if err != nil { - return State{}, nil, err - } - // save genesis doc to prevent a certain class of user errors (e.g. when it - // was changed, accidentally or not). Also good for audit trail. - saveGenesisDoc(stateDB, genDoc) - } - state, err := LoadStateFromDBOrGenesisDoc(stateDB, genDoc) - if err != nil { - return State{}, nil, err - } - return state, genDoc, nil -} - -// panics if failed to unmarshal bytes -func loadGenesisDoc(db dbm.DB) (*types.GenesisDoc, error) { - bytes := db.Get(genesisDocKey) - if len(bytes) == 0 { - return nil, errors.New("Genesis doc not found") - } - var genDoc *types.GenesisDoc - err := cdc.UnmarshalJSON(bytes, &genDoc) - if err != nil { - panic(fmt.Sprintf("Failed to load genesis doc due to unmarshaling error: %v (bytes: %X)", err, bytes)) - } - return genDoc, nil -} - -// panics if failed to marshal the given genesis document -func saveGenesisDoc(db dbm.DB, genDoc *types.GenesisDoc) { - bytes, err := cdc.MarshalJSON(genDoc) - if err != nil { - panic(fmt.Sprintf("Failed to save genesis doc due to marshaling error: %v", err)) - } - db.SetSync(genesisDocKey, bytes) -} - // LoadState loads the State from the database. func LoadState(db dbm.DB) State { return loadState(db, stateKey) From 21bfd7fba9abae059bfcd7781e7d3bcb786daf3d Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Mon, 3 Jun 2019 08:41:27 -0400 Subject: [PATCH 216/259] Add check for version/version.go TMCoreSemVer (#3704) --- scripts/release_management/bump-semver.py | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/release_management/bump-semver.py b/scripts/release_management/bump-semver.py index b13a10342..ce56d8d7c 100755 --- a/scripts/release_management/bump-semver.py +++ b/scripts/release_management/bump-semver.py @@ -8,6 +8,7 @@ import re import argparse +import sys def semver(ver): @@ -17,6 +18,18 @@ def semver(ver): return ver +def get_tendermint_version(): + """Extracts the current Tendermint version from version/version.go""" + pattern = re.compile(r"TMCoreSemVer = \"(?P([0-9.]+)+)\"") + with open("version/version.go", "rt") as version_file: + for line in version_file: + m = pattern.search(line) + if m: + return m.group('version') + + return None + + if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--version", help="Version number to bump, e.g.: v1.0.0", required=True, type=semver) @@ -34,4 +47,16 @@ if __name__ == "__main__": else: patch = int(patch) + 1 - print("{0}.{1}".format(majorminorprefix, patch)) + expected_version = "{0}.{1}".format(majorminorprefix, patch) + # if we're doing a release + if expected_version != "v0.0.0": + cur_version = get_tendermint_version() + if not cur_version: + print("Failed to obtain Tendermint version from version/version.go") + sys.exit(1) + expected_version_noprefix = expected_version.lstrip("v") + if expected_version_noprefix != "0.0.0" and expected_version_noprefix != cur_version: + print("Expected version/version.go#TMCoreSemVer to be {0}, but was {1}".format(expected_version_noprefix, cur_version)) + sys.exit(1) + + print(expected_version) From 048ac8d94b945698e3737d92cef80064d15faa07 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 3 Jun 2019 21:46:02 +0900 Subject: [PATCH 217/259] remove invalid transactions from the mempool (#3701) Otherwise, we'll be trying to include them in each consecutive block. The downside is that evil proposers will be able to drop valid transactions (see #3322). Reverts https://github.com/tendermint/tendermint/pull/3625 Fixes #3699 --- CHANGELOG_PENDING.md | 2 ++ mempool/clist_mempool.go | 27 +++++++++++++-------------- mempool/clist_mempool_test.go | 7 +++++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 9b8fc1cf0..4e83c2dbb 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -19,3 +19,5 @@ ### IMPROVEMENTS: ### BUG FIXES: +- [mempool] \#3699 Revert the change where we only remove valid transactions + from the mempool once a block is committed. diff --git a/mempool/clist_mempool.go b/mempool/clist_mempool.go index c6ff475aa..0d1f3c5b1 100644 --- a/mempool/clist_mempool.go +++ b/mempool/clist_mempool.go @@ -538,24 +538,23 @@ func (mem *CListMempool) Update( if deliverTxResponses[i].Code == abci.CodeTypeOK { // Add valid committed tx to the cache (if missing). _ = mem.cache.Push(tx) - - // Remove valid committed tx from the mempool. - if e, ok := mem.txsMap.Load(txKey(tx)); ok { - mem.removeTx(tx, e.(*clist.CElement), false) - } } else { // Allow invalid transactions to be resubmitted. mem.cache.Remove(tx) + } - // Don't remove invalid tx from the mempool. - // Otherwise evil proposer can drop valid txs. - // Example: - // 100 -> 101 -> 102 - // Block, proposed by evil proposer: - // 101 -> 102 - // Mempool (if you remove txs): - // 100 - // https://github.com/tendermint/tendermint/issues/3322. + // Remove committed tx from the mempool. + // + // Note an evil proposer can drop valid txs! + // Mempool before: + // 100 -> 101 -> 102 + // Block, proposed by an evil proposer: + // 101 -> 102 + // Mempool after: + // 100 + // https://github.com/tendermint/tendermint/issues/3322. + if e, ok := mem.txsMap.Load(txKey(tx)); ok { + mem.removeTx(tx, e.(*clist.CElement), false) } } diff --git a/mempool/clist_mempool_test.go b/mempool/clist_mempool_test.go index 3eb4990b6..bf2c61dd7 100644 --- a/mempool/clist_mempool_test.go +++ b/mempool/clist_mempool_test.go @@ -200,12 +200,15 @@ func TestMempoolUpdate(t *testing.T) { assert.Zero(t, mempool.Size()) } - // 3. Removes invalid transactions from the cache, but leaves them in the mempool (if present) + // 3. Removes invalid transactions from the cache and the mempool (if present) { err := mempool.CheckTx([]byte{0x03}, nil) require.NoError(t, err) mempool.Update(1, []types.Tx{[]byte{0x03}}, abciResponses(1, 1), nil, nil) - assert.Equal(t, 1, mempool.Size()) + assert.Zero(t, mempool.Size()) + + err = mempool.CheckTx([]byte{0x03}, nil) + assert.NoError(t, err) } } From 65a3dfe235b69fbba58f798a7d6522cd6f1244b1 Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Mon, 3 Jun 2019 11:34:21 -0400 Subject: [PATCH 218/259] changelog and version (#3709) --- CHANGELOG.md | 15 +++++++++++++++ CHANGELOG_PENDING.md | 4 +--- version/version.go | 2 +- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4328d4b62..068d99c3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Changelog +## v0.31.7 + +*June 3, 2019* + +This releases fixes a regression in the mempool introduced in v0.31.6. +The regression caused the invalid committed txs to be proposed in blocks over and +over again. + +### BUG FIXES: +- [mempool] \#3699 Remove all committed txs from the mempool. + This reverts the change from v0.31.6 where we only remove valid txs from the mempool. + Note this means malicious proposals can cause txs to be dropped from the + mempools of other nodes by including them in blocks before they are valid. + See \#3322. + ## v0.31.6 *May 31st, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 4e83c2dbb..f211a27f3 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,4 +1,4 @@ -## v0.31.7 +## v0.31.8 ** @@ -19,5 +19,3 @@ ### IMPROVEMENTS: ### BUG FIXES: -- [mempool] \#3699 Revert the change where we only remove valid transactions - from the mempool once a block is committed. diff --git a/version/version.go b/version/version.go index a7559d499..1a15717f7 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.5" + TMCoreSemVer = "0.31.7" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From 99c9a35982f2cf5522bc8f3eb95d5d29c8ccefaf Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 4 Jun 2019 10:05:22 +0900 Subject: [PATCH 219/259] docs: update RPC docs for /subscribe & /unsubscribe (#3705) Fixes #3678 Co-Authored-By: Ethan Buchman --- rpc/core/events.go | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/rpc/core/events.go b/rpc/core/events.go index 6bc5ecc7a..4b05e5ce1 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -50,7 +50,6 @@ import ( // https://godoc.org/github.com/tendermint/tendermint/libs/pubsub/query. // // ```go -// import "github.com/tendermint/tendermint/libs/pubsub/query" // import "github.com/tendermint/tendermint/types" // // client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket") @@ -59,15 +58,17 @@ import ( // // handle error // } // defer client.Stop() -// ctx, cancel := context.WithTimeout(context.Background(), timeout) +// ctx, cancel := context.WithTimeout(context.Background(), 1 * time.Second) // defer cancel() -// query := query.MustParse("tm.event = 'Tx' AND tx.height = 3") -// txs := make(chan interface{}) -// err = client.Subscribe(ctx, "test-client", query, txs) +// query := "tm.event = 'Tx' AND tx.height = 3" +// txs, err := client.Subscribe(ctx, "test-client", query) +// if err != nil { +// // handle error +// } // // go func() { // for e := range txs { -// fmt.Println("got ", e.(types.EventDataTx)) +// fmt.Println("got ", e.Data.(types.EventDataTx)) // } // }() // ``` @@ -154,7 +155,11 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er // // handle error // } // defer client.Stop() -// err = client.Unsubscribe("test-client", query) +// query := "tm.event = 'Tx' AND tx.height = 3" +// err = client.Unsubscribe(context.Background(), "test-client", query) +// if err != nil { +// // handle error +// } // ``` // // > The above command returns JSON structured like this: @@ -198,7 +203,10 @@ func Unsubscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultUnsubscribe // // handle error // } // defer client.Stop() -// err = client.UnsubscribeAll("test-client") +// err = client.UnsubscribeAll(context.Background(), "test-client") +// if err != nil { +// // handle error +// } // ``` // // > The above command returns JSON structured like this: From 96e132b4b0bd234bd3405c1909cdd40f19d2d259 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 4 Jun 2019 12:12:43 +0900 Subject: [PATCH 220/259] libs/db: remove deprecated `LevelDBBackend` const (#3632) --- CHANGELOG_PENDING.md | 3 +++ UPGRADING.md | 8 ++++++++ libs/db/c_level_db.go | 1 - libs/db/db.go | 4 ---- libs/db/go_level_db.go | 1 - 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f211a27f3..c22cb9daf 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -9,6 +9,9 @@ * Apps * Go API +- [libs/db] Removed deprecated `LevelDBBackend` const + * If you have `db_backend` set to `leveldb` in your config file, please + change it to `goleveldb` or `cleveldb`. * Blockchain Protocol diff --git a/UPGRADING.md b/UPGRADING.md index ccd4f2d7e..5a77e0729 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,6 +3,14 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. + +## v0.32.0 + +### Config Changes + +If you have `db_backend` set to `leveldb` in your config file, please change it +to `goleveldb` or `cleveldb`. + ## v0.31.6 There are no breaking changes in this release except Go API of p2p and diff --git a/libs/db/c_level_db.go b/libs/db/c_level_db.go index 0ac040d20..7538166b2 100644 --- a/libs/db/c_level_db.go +++ b/libs/db/c_level_db.go @@ -14,7 +14,6 @@ func init() { dbCreator := func(name string, dir string) (DB, error) { return NewCLevelDB(name, dir) } - registerDBCreator(LevelDBBackend, dbCreator, true) registerDBCreator(CLevelDBBackend, dbCreator, false) } diff --git a/libs/db/db.go b/libs/db/db.go index b68204420..d88df398c 100644 --- a/libs/db/db.go +++ b/libs/db/db.go @@ -9,10 +9,6 @@ type DBBackendType string // These are valid backend types. const ( - // LevelDBBackend is a legacy type. Defaults to goleveldb unless cleveldb - // build tag was used, in which it becomes cleveldb. - // Deprecated: Use concrete types (golevedb, cleveldb, etc.) - LevelDBBackend DBBackendType = "leveldb" // GoLevelDBBackend represents goleveldb (github.com/syndtr/goleveldb - most // popular implementation) // - pure go diff --git a/libs/db/go_level_db.go b/libs/db/go_level_db.go index 79ee5ccb9..8c20ccdde 100644 --- a/libs/db/go_level_db.go +++ b/libs/db/go_level_db.go @@ -15,7 +15,6 @@ func init() { dbCreator := func(name string, dir string) (DB, error) { return NewGoLevelDB(name, dir) } - registerDBCreator(LevelDBBackend, dbCreator, false) registerDBCreator(GoLevelDBBackend, dbCreator, false) } From 2a23eca36845b11f6f1755528750889a3ffcacb7 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 5 Jun 2019 10:36:16 +0900 Subject: [PATCH 221/259] docs: update /block_results RPC docs (#3708) Fixes #3616 --- CHANGELOG_PENDING.md | 2 ++ rpc/core/blocks.go | 34 ++++++++++++++++++++++------------ state/store.go | 6 +++--- 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c22cb9daf..f37904d1b 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -5,6 +5,8 @@ ### BREAKING CHANGES: * CLI/RPC/Config +- [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` -> + `results.deliver_tx`). See docs for details. * Apps diff --git a/rpc/core/blocks.go b/rpc/core/blocks.go index 40b6811db..16ebe9c56 100644 --- a/rpc/core/blocks.go +++ b/rpc/core/blocks.go @@ -339,7 +339,8 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // If no height is provided, it will fetch results for the latest block. // // Results are for the height of the block containing the txs. -// Thus response.results[5] is the results of executing getBlock(h).Txs[5] +// Thus response.results.deliver_tx[5] is the results of executing +// getBlock(h).Txs[5] // // ```shell // curl 'localhost:26657/block_results?height=10' @@ -360,17 +361,27 @@ func Commit(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultCommit, erro // // ```json // { -// "height": "10", -// "results": [ -// { -// "code": "0", -// "data": "CAFE00F00D" -// }, -// { -// "code": "102", -// "data": "" +// "jsonrpc": "2.0", +// "id": "", +// "result": { +// "height": "39", +// "results": { +// "deliver_tx": [ +// { +// "tags": [ +// { +// "key": "YXBwLmNyZWF0b3I=", +// "value": "Q29zbW9zaGkgTmV0b3dva28=" +// } +// ] +// } +// ], +// "end_block": { +// "validator_updates": null +// }, +// "begin_block": {} +// } // } -// ] // } // ``` func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockResults, error) { @@ -380,7 +391,6 @@ func BlockResults(ctx *rpctypes.Context, heightPtr *int64) (*ctypes.ResultBlockR return nil, err } - // load the results results, err := sm.LoadABCIResponses(stateDB, height) if err != nil { return nil, err diff --git a/state/store.go b/state/store.go index 0301bc7c3..f0bb9e142 100644 --- a/state/store.go +++ b/state/store.go @@ -115,9 +115,9 @@ func saveState(db dbm.DB, state State, key []byte) { // of the various ABCI calls during block processing. // It is persisted to disk for each height before calling Commit. type ABCIResponses struct { - DeliverTx []*abci.ResponseDeliverTx - EndBlock *abci.ResponseEndBlock - BeginBlock *abci.ResponseBeginBlock + DeliverTx []*abci.ResponseDeliverTx `json:"deliver_tx"` + EndBlock *abci.ResponseEndBlock `json:"end_block"` + BeginBlock *abci.ResponseBeginBlock `json:"begin_block"` } // NewABCIResponses returns a new ABCIResponses From c9ef824ddf623a873ca226b98ddb3539c1de1ebc Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Wed, 5 Jun 2019 11:22:00 +0200 Subject: [PATCH 222/259] p2p: Per channel metrics (#3666) (#3677) * Add `chID` label to sent/receive byte mtrics * add changelog pending entry --- CHANGELOG_PENDING.md | 1 + docs/tendermint-core/metrics.md | 56 ++++++++++++++++----------------- p2p/metrics.go | 4 +-- p2p/peer.go | 18 +++++++++-- 4 files changed, 46 insertions(+), 33 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f37904d1b..6c3ff0f95 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -22,5 +22,6 @@ ### FEATURES: ### IMPROVEMENTS: +- [p2p] \#3666 Add per channel telemtry to improve reactor observability ### BUG FIXES: diff --git a/docs/tendermint-core/metrics.md b/docs/tendermint-core/metrics.md index ad6d4c765..94313ddbb 100644 --- a/docs/tendermint-core/metrics.md +++ b/docs/tendermint-core/metrics.md @@ -14,34 +14,34 @@ Listen address can be changed in the config file (see The following metrics are available: -| **Name** | **Type** | **Since** | **Tags** | **Description** | -|-----------------------------------------|-----------|-----------|----------|-----------------------------------------------------------------| -| consensus\_height | Gauge | 0.21.0 | | Height of the chain | -| consensus\_validators | Gauge | 0.21.0 | | Number of validators | -| consensus\_validators\_power | Gauge | 0.21.0 | | Total voting power of all validators | -| consensus\_missing\_validators | Gauge | 0.21.0 | | Number of validators who did not sign | -| consensus\_missing\_validators\_power | Gauge | 0.21.0 | | Total voting power of the missing validators | -| consensus\_byzantine\_validators | Gauge | 0.21.0 | | Number of validators who tried to double sign | -| consensus\_byzantine\_validators\_power | Gauge | 0.21.0 | | Total voting power of the byzantine validators | -| consensus\_block\_interval\_seconds | Histogram | 0.21.0 | | Time between this and last block (Block.Header.Time) in seconds | -| consensus\_rounds | Gauge | 0.21.0 | | Number of rounds | -| consensus\_num\_txs | Gauge | 0.21.0 | | Number of transactions | -| consensus\_block\_parts | counter | on dev | peer\_id | number of blockparts transmitted by peer | -| consensus\_latest\_block\_height | gauge | on dev | | /status sync\_info number | -| consensus\_fast\_syncing | gauge | on dev | | either 0 (not fast syncing) or 1 (syncing) | -| consensus\_total\_txs | Gauge | 0.21.0 | | Total number of transactions committed | -| consensus\_block\_size\_bytes | Gauge | 0.21.0 | | Block size in bytes | -| p2p\_peers | Gauge | 0.21.0 | | Number of peers node's connected to | -| p2p\_peer\_receive\_bytes\_total | counter | on dev | peer\_id | number of bytes received from a given peer | -| p2p\_peer\_send\_bytes\_total | counter | on dev | peer\_id | number of bytes sent to a given peer | -| p2p\_peer\_pending\_send\_bytes | gauge | on dev | peer\_id | number of pending bytes to be sent to a given peer | -| p2p\_num\_txs | gauge | on dev | peer\_id | number of transactions submitted by each peer\_id | -| p2p\_pending\_send\_bytes | gauge | on dev | peer\_id | amount of data pending to be sent to peer | -| mempool\_size | Gauge | 0.21.0 | | Number of uncommitted transactions | -| mempool\_tx\_size\_bytes | histogram | on dev | | transaction sizes in bytes | -| mempool\_failed\_txs | counter | on dev | | number of failed transactions | -| mempool\_recheck\_times | counter | on dev | | number of transactions rechecked in the mempool | -| state\_block\_processing\_time | histogram | on dev | | time between BeginBlock and EndBlock in ms | +| **Name** | **Type** | **Since** | **Tags** | **Description** | +|-----------------------------------------|-----------|-----------|----------------|-----------------------------------------------------------------| +| consensus\_height | Gauge | 0.21.0 | | Height of the chain | +| consensus\_validators | Gauge | 0.21.0 | | Number of validators | +| consensus\_validators\_power | Gauge | 0.21.0 | | Total voting power of all validators | +| consensus\_missing\_validators | Gauge | 0.21.0 | | Number of validators who did not sign | +| consensus\_missing\_validators\_power | Gauge | 0.21.0 | | Total voting power of the missing validators | +| consensus\_byzantine\_validators | Gauge | 0.21.0 | | Number of validators who tried to double sign | +| consensus\_byzantine\_validators\_power | Gauge | 0.21.0 | | Total voting power of the byzantine validators | +| consensus\_block\_interval\_seconds | Histogram | 0.21.0 | | Time between this and last block (Block.Header.Time) in seconds | +| consensus\_rounds | Gauge | 0.21.0 | | Number of rounds | +| consensus\_num\_txs | Gauge | 0.21.0 | | Number of transactions | +| consensus\_block\_parts | counter | on dev | peer\_id | number of blockparts transmitted by peer | +| consensus\_latest\_block\_height | gauge | on dev | | /status sync\_info number | +| consensus\_fast\_syncing | gauge | on dev | | either 0 (not fast syncing) or 1 (syncing) | +| consensus\_total\_txs | Gauge | 0.21.0 | | Total number of transactions committed | +| consensus\_block\_size\_bytes | Gauge | 0.21.0 | | Block size in bytes | +| p2p\_peers | Gauge | 0.21.0 | | Number of peers node's connected to | +| p2p\_peer\_receive\_bytes\_total | counter | on dev | peer\_id, chID | number of bytes per channel received from a given peer | +| p2p\_peer\_send\_bytes\_total | counter | on dev | peer\_id, chID | number of bytes per channel sent to a given peer | +| p2p\_peer\_pending\_send\_bytes | gauge | on dev | peer\_id | number of pending bytes to be sent to a given peer | +| p2p\_num\_txs | gauge | on dev | peer\_id | number of transactions submitted by each peer\_id | +| p2p\_pending\_send\_bytes | gauge | on dev | peer\_id | amount of data pending to be sent to peer | +| mempool\_size | Gauge | 0.21.0 | | Number of uncommitted transactions | +| mempool\_tx\_size\_bytes | histogram | on dev | | transaction sizes in bytes | +| mempool\_failed\_txs | counter | on dev | | number of failed transactions | +| mempool\_recheck\_times | counter | on dev | | number of transactions rechecked in the mempool | +| state\_block\_processing\_time | histogram | on dev | | time between BeginBlock and EndBlock in ms | ## Useful queries diff --git a/p2p/metrics.go b/p2p/metrics.go index 3a6b9568a..675dd9c7c 100644 --- a/p2p/metrics.go +++ b/p2p/metrics.go @@ -47,13 +47,13 @@ func PrometheusMetrics(namespace string, labelsAndValues ...string) *Metrics { Subsystem: MetricsSubsystem, Name: "peer_receive_bytes_total", Help: "Number of bytes received from a given peer.", - }, append(labels, "peer_id")).With(labelsAndValues...), + }, append(labels, "peer_id", "chID")).With(labelsAndValues...), PeerSendBytesTotal: prometheus.NewCounterFrom(stdprometheus.CounterOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, Name: "peer_send_bytes_total", Help: "Number of bytes sent to a given peer.", - }, append(labels, "peer_id")).With(labelsAndValues...), + }, append(labels, "peer_id", "chID")).With(labelsAndValues...), PeerPendingSendBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ Namespace: namespace, Subsystem: MetricsSubsystem, diff --git a/p2p/peer.go b/p2p/peer.go index fab3b42d4..80be0db53 100644 --- a/p2p/peer.go +++ b/p2p/peer.go @@ -248,7 +248,11 @@ func (p *peer) Send(chID byte, msgBytes []byte) bool { } res := p.mconn.Send(chID, msgBytes) if res { - p.metrics.PeerSendBytesTotal.With("peer_id", string(p.ID())).Add(float64(len(msgBytes))) + labels := []string{ + "peer_id", string(p.ID()), + "chID", fmt.Sprintf("%#x", chID), + } + p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) } return res } @@ -263,7 +267,11 @@ func (p *peer) TrySend(chID byte, msgBytes []byte) bool { } res := p.mconn.TrySend(chID, msgBytes) if res { - p.metrics.PeerSendBytesTotal.With("peer_id", string(p.ID())).Add(float64(len(msgBytes))) + labels := []string{ + "peer_id", string(p.ID()), + "chID", fmt.Sprintf("%#x", chID), + } + p.metrics.PeerSendBytesTotal.With(labels...).Add(float64(len(msgBytes))) } return res } @@ -369,7 +377,11 @@ func createMConnection( // which does onPeerError. panic(fmt.Sprintf("Unknown channel %X", chID)) } - p.metrics.PeerReceiveBytesTotal.With("peer_id", string(p.ID())).Add(float64(len(msgBytes))) + labels := []string{ + "peer_id", string(p.ID()), + "chID", fmt.Sprintf("%#x", chID), + } + p.metrics.PeerReceiveBytesTotal.With(labels...).Add(float64(len(msgBytes))) reactor.Receive(chID, p, msgBytes) } From a7e8fbf3a7ccfe4ac9fafef6a94c2b9fc9d22d37 Mon Sep 17 00:00:00 2001 From: Haochuan Guo Date: Wed, 5 Jun 2019 17:23:53 +0800 Subject: [PATCH 223/259] rpc: Use Wrap instead of Errorf error (#3686) So we can have an error code when rpc fails. * Update CHANGELOG_PENDING.md Co-Authored-By: Anton Kaliaev * wrap correct error * format code --- CHANGELOG_PENDING.md | 2 ++ rpc/lib/client/http_client.go | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 6c3ff0f95..187caa0f5 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -24,4 +24,6 @@ ### IMPROVEMENTS: - [p2p] \#3666 Add per channel telemtry to improve reactor observability +* [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. + ### BUG FIXES: diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go index 824820fab..3b545a5dd 100644 --- a/rpc/lib/client/http_client.go +++ b/rpc/lib/client/http_client.go @@ -13,9 +13,10 @@ import ( "sync" "github.com/pkg/errors" - amino "github.com/tendermint/go-amino" - cmn "github.com/tendermint/tendermint/libs/common" + amino "github.com/tendermint/go-amino" + + cmn "github.com/tendermint/tendermint/libs/common" types "github.com/tendermint/tendermint/rpc/lib/types" ) @@ -303,10 +304,10 @@ func unmarshalResponseBytes(cdc *amino.Codec, responseBytes []byte, expectedID t response := &types.RPCResponse{} err = json.Unmarshal(responseBytes, response) if err != nil { - return nil, errors.Errorf("error unmarshalling rpc response: %v", err) + return nil, errors.Wrap(err, "error unmarshalling rpc response") } if response.Error != nil { - return nil, errors.Errorf("response error: %v", response.Error) + return nil, errors.Wrap(response.Error, "response error") } // From the JSON-RPC 2.0 spec: // id: It MUST be the same as the value of the id member in the Request Object. @@ -316,7 +317,7 @@ func unmarshalResponseBytes(cdc *amino.Codec, responseBytes []byte, expectedID t // Unmarshal the RawMessage into the result. err = cdc.UnmarshalJSON(response.Result, result) if err != nil { - return nil, errors.Errorf("error unmarshalling rpc response result: %v", err) + return nil, errors.Wrap(err, "error unmarshalling rpc response result") } return result, nil } @@ -328,7 +329,7 @@ func unmarshalResponseBytesArray(cdc *amino.Codec, responseBytes []byte, expecte ) err = json.Unmarshal(responseBytes, &responses) if err != nil { - return nil, errors.Errorf("error unmarshalling rpc response: %v", err) + return nil, errors.Wrap(err, "error unmarshalling rpc response") } // No response error checking here as there may be a mixture of successful // and unsuccessful responses. @@ -341,10 +342,10 @@ func unmarshalResponseBytesArray(cdc *amino.Codec, responseBytes []byte, expecte // From the JSON-RPC 2.0 spec: // id: It MUST be the same as the value of the id member in the Request Object. if err := validateResponseID(&response, expectedID); err != nil { - return nil, errors.Errorf("failed to validate response ID in response %d: %v", i, err) + return nil, errors.Wrapf(err, "failed to validate response ID in response %d", i) } if err := cdc.UnmarshalJSON(responses[i].Result, results[i]); err != nil { - return nil, errors.Errorf("error unmarshalling rpc response result: %v", err) + return nil, errors.Wrap(err, "error unmarshalling rpc response result") } } return results, nil From c1f264822abbcfb1099e55a404712a1a16dfdcd1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Thu, 6 Jun 2019 00:39:28 +0900 Subject: [PATCH 224/259] p2p: remove NewNetAddressStringWithOptionalID (#3711) Fixes #3521 The function NewNetAddressStringWithOptionalID is from a time when peer IDs were optional. They're not anymore. So this should be renamed to NewNetAddressString and should ensure the ID is provided. * update changelog * use NewNetAddress in transport tests * use NewNetAddress in TestTransportMultiplexAcceptMultiple --- CHANGELOG_PENDING.md | 1 + node/node.go | 2 +- p2p/netaddress.go | 41 ++++++++------------- p2p/netaddress_test.go | 57 ++++++++++------------------- p2p/pex/pex_reactor.go | 2 +- p2p/transport_test.go | 83 ++++++++++++------------------------------ 6 files changed, 62 insertions(+), 124 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 187caa0f5..8a8ad2795 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -14,6 +14,7 @@ - [libs/db] Removed deprecated `LevelDBBackend` const * If you have `db_backend` set to `leveldb` in your config file, please change it to `goleveldb` or `cleveldb`. +- [p2p] \#3521 Remove NewNetAddressStringWithOptionalID * Blockchain Protocol diff --git a/node/node.go b/node/node.go index 0d05deee1..46185847e 100644 --- a/node/node.go +++ b/node/node.go @@ -674,7 +674,7 @@ func (n *Node) OnStart() error { } // Start the transport. - addr, err := p2p.NewNetAddressStringWithOptionalID(n.config.P2P.ListenAddress) + addr, err := p2p.NewNetAddressString(p2p.IDAddressString(n.nodeKey.ID(), n.config.P2P.ListenAddress)) if err != nil { return err } diff --git a/p2p/netaddress.go b/p2p/netaddress.go index 73130ee5e..d11504525 100644 --- a/p2p/netaddress.go +++ b/p2p/netaddress.go @@ -65,36 +65,27 @@ func NewNetAddress(id ID, addr net.Addr) *NetAddress { // Also resolves the host if host is not an IP. // Errors are of type ErrNetAddressXxx where Xxx is in (NoID, Invalid, Lookup) func NewNetAddressString(addr string) (*NetAddress, error) { - spl := strings.Split(addr, "@") - if len(spl) < 2 { + addrWithoutProtocol := removeProtocolIfDefined(addr) + spl := strings.Split(addrWithoutProtocol, "@") + if len(spl) != 2 { return nil, ErrNetAddressNoID{addr} } - return NewNetAddressStringWithOptionalID(addr) -} -// NewNetAddressStringWithOptionalID returns a new NetAddress using the -// provided address in the form of "ID@IP:Port", where the ID is optional. -// Also resolves the host if host is not an IP. -func NewNetAddressStringWithOptionalID(addr string) (*NetAddress, error) { - addrWithoutProtocol := removeProtocolIfDefined(addr) - - var id ID - spl := strings.Split(addrWithoutProtocol, "@") - if len(spl) == 2 { - idStr := spl[0] - idBytes, err := hex.DecodeString(idStr) - if err != nil { - return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} - } - if len(idBytes) != IDByteLength { - return nil, ErrNetAddressInvalid{ - addrWithoutProtocol, - fmt.Errorf("invalid hex length - got %d, expected %d", len(idBytes), IDByteLength)} - } - - id, addrWithoutProtocol = ID(idStr), spl[1] + // get ID + idStr := spl[0] + idBytes, err := hex.DecodeString(idStr) + if err != nil { + return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} } + if len(idBytes) != IDByteLength { + return nil, ErrNetAddressInvalid{ + addrWithoutProtocol, + fmt.Errorf("invalid hex length - got %d, expected %d", len(idBytes), IDByteLength)} + } + var id ID + id, addrWithoutProtocol = ID(idStr), spl[1] + // get host and port host, portStr, err := net.SplitHostPort(addrWithoutProtocol) if err != nil { return nil, ErrNetAddressInvalid{addrWithoutProtocol, err} diff --git a/p2p/netaddress_test.go b/p2p/netaddress_test.go index e7b184a76..7afcab131 100644 --- a/p2p/netaddress_test.go +++ b/p2p/netaddress_test.go @@ -20,17 +20,23 @@ func TestNewNetAddress(t *testing.T) { }, "Calling NewNetAddress with UDPAddr should not panic in testing") } -func TestNewNetAddressStringWithOptionalID(t *testing.T) { +func TestNewNetAddressString(t *testing.T) { testCases := []struct { name string addr string expected string correct bool }{ - {"no node id, no protocol", "127.0.0.1:8080", "127.0.0.1:8080", true}, - {"no node id, tcp input", "tcp://127.0.0.1:8080", "127.0.0.1:8080", true}, - {"no node id, udp input", "udp://127.0.0.1:8080", "127.0.0.1:8080", true}, - {"malformed udp input", "udp//127.0.0.1:8080", "", false}, + {"no node id and no protocol", "127.0.0.1:8080", "", false}, + {"no node id w/ tcp input", "tcp://127.0.0.1:8080", "", false}, + {"no node id w/ udp input", "udp://127.0.0.1:8080", "", false}, + + {"no protocol", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, + {"tcp input", "tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, + {"udp input", "udp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, + {"malformed tcp input", "tcp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, + {"malformed udp input", "udp//deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, + // {"127.0.0:8080", false}, {"invalid host", "notahost", "", false}, {"invalid port", "127.0.0.1:notapath", "", false}, @@ -41,14 +47,13 @@ func TestNewNetAddressStringWithOptionalID(t *testing.T) { {"too short nodeId", "deadbeef@127.0.0.1:8080", "", false}, {"too short, not hex nodeId", "this-isnot-hex@127.0.0.1:8080", "", false}, {"not hex nodeId", "xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, - {"correct nodeId", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, {"too short nodeId w/tcp", "tcp://deadbeef@127.0.0.1:8080", "", false}, {"too short notHex nodeId w/tcp", "tcp://this-isnot-hex@127.0.0.1:8080", "", false}, {"notHex nodeId w/tcp", "tcp://xxxxbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "", false}, {"correct nodeId w/tcp", "tcp://deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, - {"no node id when expected", "tcp://@127.0.0.1:8080", "", false}, + {"no node id", "tcp://@127.0.0.1:8080", "", false}, {"no node id or IP", "tcp://@", "", false}, {"tcp no host, w/ port", "tcp://:26656", "", false}, {"empty", "", "", false}, @@ -59,7 +64,7 @@ func TestNewNetAddressStringWithOptionalID(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - addr, err := NewNetAddressStringWithOptionalID(tc.addr) + addr, err := NewNetAddressString(tc.addr) if tc.correct { if assert.Nil(t, err, tc.addr) { assert.Equal(t, tc.expected, addr.String()) @@ -71,28 +76,6 @@ func TestNewNetAddressStringWithOptionalID(t *testing.T) { } } -func TestNewNetAddressString(t *testing.T) { - testCases := []struct { - addr string - expected string - correct bool - }{ - {"127.0.0.1:8080", "127.0.0.1:8080", false}, - {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true}, - } - - for _, tc := range testCases { - addr, err := NewNetAddressString(tc.addr) - if tc.correct { - if assert.Nil(t, err, tc.addr) { - assert.Equal(t, tc.expected, addr.String()) - } - } else { - assert.NotNil(t, err, tc.addr) - } - } -} - func TestNewNetAddressStrings(t *testing.T) { addrs, errs := NewNetAddressStrings([]string{ "127.0.0.1:8080", @@ -115,12 +98,12 @@ func TestNetAddressProperties(t *testing.T) { local bool routable bool }{ - {"127.0.0.1:8080", true, true, false}, - {"ya.ru:80", true, false, true}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", true, true, false}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@ya.ru:80", true, false, true}, } for _, tc := range testCases { - addr, err := NewNetAddressStringWithOptionalID(tc.addr) + addr, err := NewNetAddressString(tc.addr) require.Nil(t, err) assert.Equal(t, tc.valid, addr.Valid()) @@ -136,15 +119,15 @@ func TestNetAddressReachabilityTo(t *testing.T) { other string reachability int }{ - {"127.0.0.1:8080", "127.0.0.1:8081", 0}, - {"ya.ru:80", "127.0.0.1:8080", 1}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8081", 0}, + {"deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@ya.ru:80", "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef@127.0.0.1:8080", 1}, } for _, tc := range testCases { - addr, err := NewNetAddressStringWithOptionalID(tc.addr) + addr, err := NewNetAddressString(tc.addr) require.Nil(t, err) - other, err := NewNetAddressStringWithOptionalID(tc.other) + other, err := NewNetAddressString(tc.other) require.Nil(t, err) assert.Equal(t, tc.reachability, addr.ReachabilityTo(other)) diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index eabbc4d61..e77fa8eaa 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -345,7 +345,7 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { if netAddr == nil { return errors.New("nil address in pexAddrsMessage") } - // TODO: extract validating logic from NewNetAddressStringWithOptionalID + // TODO: extract validating logic from NewNetAddressString // and put it in netAddr#Valid (#2722) na, err := p2p.NewNetAddressString(netAddr.String()) if err != nil { diff --git a/p2p/transport_test.go b/p2p/transport_test.go index 35fd9c66b..7580f0259 100644 --- a/p2p/transport_test.go +++ b/p2p/transport_test.go @@ -8,8 +8,6 @@ import ( "testing" "time" - "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/p2p/conn" ) @@ -39,6 +37,7 @@ func TestTransportMultiplexConnFilter(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) + id := mt.nodeKey.ID() MultiplexTransportConnFilters( func(_ ConnSet, _ net.Conn, _ []net.IP) error { return nil }, @@ -48,7 +47,7 @@ func TestTransportMultiplexConnFilter(t *testing.T) { }, )(mt) - addr, err := NewNetAddressStringWithOptionalID("127.0.0.1:0") + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } @@ -60,13 +59,9 @@ func TestTransportMultiplexConnFilter(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(id, mt.listener.Addr()) - _, err = addr.Dial() + _, err := addr.Dial() if err != nil { errc <- err return @@ -96,6 +91,7 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) + id := mt.nodeKey.ID() MultiplexTransportFilterTimeout(5 * time.Millisecond)(mt) MultiplexTransportConnFilters( @@ -105,7 +101,7 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { }, )(mt) - addr, err := NewNetAddressStringWithOptionalID("127.0.0.1:0") + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } @@ -117,13 +113,9 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(mt.listener.Addr().String()) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(id, mt.listener.Addr()) - _, err = addr.Dial() + _, err := addr.Dial() if err != nil { errc <- err return @@ -144,9 +136,7 @@ func TestTransportMultiplexConnFilterTimeout(t *testing.T) { func TestTransportMultiplexAcceptMultiple(t *testing.T) { mt := testSetupMultiplexTransport(t) - id, addr := mt.nodeKey.ID(), mt.listener.Addr().String() - laddr, err := NewNetAddressStringWithOptionalID(IDAddressString(id, addr)) - require.NoError(t, err) + laddr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) var ( seed = rand.New(rand.NewSource(time.Now().UnixNano())) @@ -232,11 +222,7 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { // Simulate slow Peer. go func() { - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) c, err := addr.Dial() if err != nil { @@ -283,13 +269,9 @@ func TestTransportMultiplexAcceptNonBlocking(t *testing.T) { }, ) ) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -329,13 +311,9 @@ func TestTransportMultiplexValidateNodeInfo(t *testing.T) { ) ) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -372,13 +350,9 @@ func TestTransportMultiplexRejectMissmatchID(t *testing.T) { PrivKey: ed25519.GenPrivKey(), }, ) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -415,12 +389,9 @@ func TestTransportMultiplexDialRejectWrongID(t *testing.T) { ) wrongID := PubKeyToID(ed25519.GenPrivKey().PubKey()) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(wrongID, mt.listener.Addr().String())) - if err != nil { - t.Fatalf("invalid address with ID: %v", err) - } + addr := NewNetAddress(wrongID, mt.listener.Addr()) - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { t.Logf("connection failed: %v", err) if err, ok := err.(ErrRejected); ok { @@ -448,13 +419,9 @@ func TestTransportMultiplexRejectIncompatible(t *testing.T) { }, ) ) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = dialer.Dial(*addr, peerConfig{}) + _, err := dialer.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -479,13 +446,9 @@ func TestTransportMultiplexRejectSelf(t *testing.T) { errc := make(chan error) go func() { - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(mt.nodeKey.ID(), mt.listener.Addr().String())) - if err != nil { - errc <- err - return - } + addr := NewNetAddress(mt.nodeKey.ID(), mt.listener.Addr()) - _, err = mt.Dial(*addr, peerConfig{}) + _, err := mt.Dial(*addr, peerConfig{}) if err != nil { errc <- err return @@ -609,7 +572,7 @@ func testSetupMultiplexTransport(t *testing.T) *MultiplexTransport { ) ) - addr, err := NewNetAddressStringWithOptionalID(IDAddressString(id, "127.0.0.1:0")) + addr, err := NewNetAddressString(IDAddressString(id, "127.0.0.1:0")) if err != nil { t.Fatal(err) } From 6a7d4182b457a274c75ec411df4de2adc90d4df2 Mon Sep 17 00:00:00 2001 From: Sean Braithwaite Date: Thu, 6 Jun 2019 22:33:39 +0200 Subject: [PATCH 225/259] p2p: Peerbehaviour follow up (#3653) (#3663) * Move peer behaviour into it's own package * refactor wip * Adjust API and fix tests * remove unused test struct * Better error message * Restructure: + Now behaviour is it's own package, we don't need to include PeerBehaviour in every type. + Split up behaviours and reporters into seperate files * doc string fixes * Fix minor typos * Update behaviour/reporter.go Co-Authored-By: Anton Kaliaev * Update behaviour/reporter.go Co-Authored-By: Anton Kaliaev --- behaviour/peer_behaviour.go | 49 ++++++++++ behaviour/reporter.go | 84 ++++++++++++++++ behaviour/reporter_test.go | 186 ++++++++++++++++++++++++++++++++++++ p2p/peer_behaviour.go | 92 ------------------ p2p/peer_behaviour_test.go | 180 ---------------------------------- 5 files changed, 319 insertions(+), 272 deletions(-) create mode 100644 behaviour/peer_behaviour.go create mode 100644 behaviour/reporter.go create mode 100644 behaviour/reporter_test.go delete mode 100644 p2p/peer_behaviour.go delete mode 100644 p2p/peer_behaviour_test.go diff --git a/behaviour/peer_behaviour.go b/behaviour/peer_behaviour.go new file mode 100644 index 000000000..36630f46b --- /dev/null +++ b/behaviour/peer_behaviour.go @@ -0,0 +1,49 @@ +package behaviour + +import ( + "github.com/tendermint/tendermint/p2p" +) + +// PeerBehaviour is a struct describing a behaviour a peer performed. +// `peerID` identifies the peer and reason characterizes the specific +// behaviour performed by the peer. +type PeerBehaviour struct { + peerID p2p.ID + reason interface{} +} + +type badMessage struct { + explanation string +} + +// BadMessage returns a badMessage PeerBehaviour. +func BadMessage(peerID p2p.ID, explanation string) PeerBehaviour { + return PeerBehaviour{peerID: peerID, reason: badMessage{explanation}} +} + +type messageOutOfOrder struct { + explanation string +} + +// MessageOutOfOrder returns a messagOutOfOrder PeerBehaviour. +func MessageOutOfOrder(peerID p2p.ID, explanation string) PeerBehaviour { + return PeerBehaviour{peerID: peerID, reason: badMessage{explanation}} +} + +type consensusVote struct { + explanation string +} + +// ConsensusVote returns a consensusVote PeerBehaviour. +func ConsensusVote(peerID p2p.ID, explanation string) PeerBehaviour { + return PeerBehaviour{peerID: peerID, reason: consensusVote{explanation}} +} + +type blockPart struct { + explanation string +} + +// BlockPart returns blockPart PeerBehaviour. +func BlockPart(peerID p2p.ID, explanation string) PeerBehaviour { + return PeerBehaviour{peerID: peerID, reason: blockPart{explanation}} +} diff --git a/behaviour/reporter.go b/behaviour/reporter.go new file mode 100644 index 000000000..f8a0693bf --- /dev/null +++ b/behaviour/reporter.go @@ -0,0 +1,84 @@ +package behaviour + +import ( + "errors" + "sync" + + "github.com/tendermint/tendermint/p2p" +) + +// Reporter provides an interface for reactors to report the behaviour +// of peers synchronously to other components. +type Reporter interface { + Report(behaviour PeerBehaviour) error +} + +// SwitchReporter reports peer behaviour to an internal Switch. +type SwitchReporter struct { + sw *p2p.Switch +} + +// NewSwitchReporter return a new SwitchReporter instance which wraps the Switch. +func NewSwitcReporter(sw *p2p.Switch) *SwitchReporter { + return &SwitchReporter{ + sw: sw, + } +} + +// Report reports the behaviour of a peer to the Switch. +func (spbr *SwitchReporter) Report(behaviour PeerBehaviour) error { + peer := spbr.sw.Peers().Get(behaviour.peerID) + if peer == nil { + return errors.New("peer not found") + } + + switch reason := behaviour.reason.(type) { + case consensusVote, blockPart: + spbr.sw.MarkPeerAsGood(peer) + case badMessage: + spbr.sw.StopPeerForError(peer, reason.explanation) + case messageOutOfOrder: + spbr.sw.StopPeerForError(peer, reason.explanation) + default: + return errors.New("unknown reason reported") + } + + return nil +} + +// MockReporter is a concrete implementation of the Reporter +// interface used in reactor tests to ensure reactors report the correct +// behaviour in manufactured scenarios. +type MockReporter struct { + mtx sync.RWMutex + pb map[p2p.ID][]PeerBehaviour +} + +// NewMockReporter returns a Reporter which records all reported +// behaviours in memory. +func NewMockReporter() *MockReporter { + return &MockReporter{ + pb: map[p2p.ID][]PeerBehaviour{}, + } +} + +// Report stores the PeerBehaviour produced by the peer identified by peerID. +func (mpbr *MockReporter) Report(behaviour PeerBehaviour) { + mpbr.mtx.Lock() + defer mpbr.mtx.Unlock() + mpbr.pb[behaviour.peerID] = append(mpbr.pb[behaviour.peerID], behaviour) +} + +// GetBehaviours returns all behaviours reported on the peer identified by peerID. +func (mpbr *MockReporter) GetBehaviours(peerID p2p.ID) []PeerBehaviour { + mpbr.mtx.RLock() + defer mpbr.mtx.RUnlock() + if items, ok := mpbr.pb[peerID]; ok { + result := make([]PeerBehaviour, len(items)) + copy(result, items) + + return result + } else { + return []PeerBehaviour{} + } +} diff --git a/behaviour/reporter_test.go b/behaviour/reporter_test.go new file mode 100644 index 000000000..eae94e7bd --- /dev/null +++ b/behaviour/reporter_test.go @@ -0,0 +1,186 @@ +package behaviour_test + +import ( + "sync" + "testing" + + bh "github.com/tendermint/tendermint/behaviour" + "github.com/tendermint/tendermint/p2p" +) + +// TestMockReporter tests the MockReporter's ability to store reported +// peer behaviour in memory indexed by the peerID. +func TestMockReporter(t *testing.T) { + var peerID p2p.ID = "MockPeer" + pr := bh.NewMockReporter() + + behaviours := pr.GetBehaviours(peerID) + if len(behaviours) != 0 { + t.Error("Expected to have no behaviours reported") + } + + badMessage := bh.BadMessage(peerID, "bad message") + pr.Report(badMessage) + behaviours = pr.GetBehaviours(peerID) + if len(behaviours) != 1 { + t.Error("Expected the peer have one reported behaviour") + } + + if behaviours[0] != badMessage { + t.Error("Expected Bad Message to have been reported") + } +} + +type scriptItem struct { + peerID p2p.ID + behaviour bh.PeerBehaviour +} + +// equalBehaviours returns true if a and b contain the same PeerBehaviours with +// the same freequencies and otherwise false. +func equalBehaviours(a []bh.PeerBehaviour, b []bh.PeerBehaviour) bool { + aHistogram := map[bh.PeerBehaviour]int{} + bHistogram := map[bh.PeerBehaviour]int{} + + for _, behaviour := range a { + aHistogram[behaviour] += 1 + } + + for _, behaviour := range b { + bHistogram[behaviour] += 1 + } + + if len(aHistogram) != len(bHistogram) { + return false + } + + for _, behaviour := range a { + if aHistogram[behaviour] != bHistogram[behaviour] { + return false + } + } + + for _, behaviour := range b { + if bHistogram[behaviour] != aHistogram[behaviour] { + return false + } + } + + return true +} + +// TestEqualPeerBehaviours tests that equalBehaviours can tell that two slices +// of peer behaviours can be compared for the behaviours they contain and the +// freequencies that those behaviours occur. +func TestEqualPeerBehaviours(t *testing.T) { + var ( + peerID p2p.ID = "MockPeer" + consensusVote = bh.ConsensusVote(peerID, "voted") + blockPart = bh.BlockPart(peerID, "blocked") + equals = []struct { + left []bh.PeerBehaviour + right []bh.PeerBehaviour + }{ + // Empty sets + {[]bh.PeerBehaviour{}, []bh.PeerBehaviour{}}, + // Single behaviours + {[]bh.PeerBehaviour{consensusVote}, []bh.PeerBehaviour{consensusVote}}, + // Equal Frequencies + {[]bh.PeerBehaviour{consensusVote, consensusVote}, + []bh.PeerBehaviour{consensusVote, consensusVote}}, + // Equal frequencies different orders + {[]bh.PeerBehaviour{consensusVote, blockPart}, + []bh.PeerBehaviour{blockPart, consensusVote}}, + } + unequals = []struct { + left []bh.PeerBehaviour + right []bh.PeerBehaviour + }{ + // Comparing empty sets to non empty sets + {[]bh.PeerBehaviour{}, []bh.PeerBehaviour{consensusVote}}, + // Different behaviours + {[]bh.PeerBehaviour{consensusVote}, []bh.PeerBehaviour{blockPart}}, + // Same behaviour with different frequencies + {[]bh.PeerBehaviour{consensusVote}, + []bh.PeerBehaviour{consensusVote, consensusVote}}, + } + ) + + for _, test := range equals { + if !equalBehaviours(test.left, test.right) { + t.Errorf("Expected %#v and %#v to be equal", test.left, test.right) + } + } + + for _, test := range unequals { + if equalBehaviours(test.left, test.right) { + t.Errorf("Expected %#v and %#v to be unequal", test.left, test.right) + } + } +} + +// TestPeerBehaviourConcurrency constructs a scenario in which +// multiple goroutines are using the same MockReporter instance. +// This test reproduces the conditions in which MockReporter will +// be used within a Reactor `Receive` method tests to ensure thread safety. +func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { + var ( + behaviourScript = []struct { + peerID p2p.ID + behaviours []bh.PeerBehaviour + }{ + {"1", []bh.PeerBehaviour{bh.ConsensusVote("1", "")}}, + {"2", []bh.PeerBehaviour{bh.ConsensusVote("2", ""), bh.ConsensusVote("2", ""), bh.ConsensusVote("2", "")}}, + {"3", []bh.PeerBehaviour{bh.BlockPart("3", ""), bh.ConsensusVote("3", ""), bh.BlockPart("3", ""), bh.ConsensusVote("3", "")}}, + {"4", []bh.PeerBehaviour{bh.ConsensusVote("4", ""), bh.ConsensusVote("4", ""), bh.ConsensusVote("4", ""), bh.ConsensusVote("4", "")}}, + {"5", []bh.PeerBehaviour{bh.BlockPart("5", ""), bh.ConsensusVote("5", ""), bh.BlockPart("5", ""), bh.ConsensusVote("5", "")}}, + } + ) + + var receiveWg sync.WaitGroup + pr := bh.NewMockReporter() + scriptItems := make(chan scriptItem) + done := make(chan int) + numConsumers := 3 + for i := 0; i < numConsumers; i++ { + receiveWg.Add(1) + go func() { + defer receiveWg.Done() + for { + select { + case pb := <-scriptItems: + pr.Report(pb.behaviour) + case <-done: + return + } + } + }() + } + + var sendingWg sync.WaitGroup + sendingWg.Add(1) + go func() { + defer sendingWg.Done() + for _, item := range behaviourScript { + for _, reason := range item.behaviours { + scriptItems <- scriptItem{item.peerID, reason} + } + } + }() + + sendingWg.Wait() + + for i := 0; i < numConsumers; i++ { + done <- 1 + } + + receiveWg.Wait() + + for _, items := range behaviourScript { + reported := pr.GetBehaviours(items.peerID) + if !equalBehaviours(reported, items.behaviours) { + t.Errorf("Expected peer %s to have behaved \nExpected: %#v \nGot %#v \n", + items.peerID, items.behaviours, reported) + } + } +} diff --git a/p2p/peer_behaviour.go b/p2p/peer_behaviour.go deleted file mode 100644 index 8d27805c3..000000000 --- a/p2p/peer_behaviour.go +++ /dev/null @@ -1,92 +0,0 @@ -package p2p - -import ( - "errors" - "sync" -) - -// PeerBehaviour are types of reportable behaviours about peers. -type PeerBehaviour int - -const ( - PeerBehaviourBadMessage = iota - PeerBehaviourMessageOutOfOrder - PeerBehaviourVote - PeerBehaviourBlockPart -) - -// PeerBehaviourReporter provides an interface for reactors to report the behaviour -// of peers synchronously to other components. -type PeerBehaviourReporter interface { - Report(peerID ID, behaviour PeerBehaviour) error -} - -// SwitchPeerBehaviouReporter reports peer behaviour to an internal Switch -type SwitchPeerBehaviourReporter struct { - sw *Switch -} - -// Return a new SwitchPeerBehaviourReporter instance which wraps the Switch. -func NewSwitchPeerBehaviourReporter(sw *Switch) *SwitchPeerBehaviourReporter { - return &SwitchPeerBehaviourReporter{ - sw: sw, - } -} - -// Report reports the behaviour of a peer to the Switch -func (spbr *SwitchPeerBehaviourReporter) Report(peerID ID, behaviour PeerBehaviour) error { - peer := spbr.sw.Peers().Get(peerID) - if peer == nil { - return errors.New("Peer not found") - } - - switch behaviour { - case PeerBehaviourVote, PeerBehaviourBlockPart: - spbr.sw.MarkPeerAsGood(peer) - case PeerBehaviourBadMessage: - spbr.sw.StopPeerForError(peer, "Bad message") - case PeerBehaviourMessageOutOfOrder: - spbr.sw.StopPeerForError(peer, "Message out of order") - default: - return errors.New("Unknown behaviour") - } - - return nil -} - -// MockPeerBehaviourReporter serves a mock concrete implementation of the -// PeerBehaviourReporter interface used in reactor tests to ensure reactors -// report the correct behaviour in manufactured scenarios. -type MockPeerBehaviourReporter struct { - mtx sync.RWMutex - pb map[ID][]PeerBehaviour -} - -// NewMockPeerBehaviourReporter returns a PeerBehaviourReporter which records all reported -// behaviours in memory. -func NewMockPeerBehaviourReporter() *MockPeerBehaviourReporter { - return &MockPeerBehaviourReporter{ - pb: map[ID][]PeerBehaviour{}, - } -} - -// Report stores the PeerBehaviour produced by the peer identified by peerID. -func (mpbr *MockPeerBehaviourReporter) Report(peerID ID, behaviour PeerBehaviour) { - mpbr.mtx.Lock() - defer mpbr.mtx.Unlock() - mpbr.pb[peerID] = append(mpbr.pb[peerID], behaviour) -} - -// GetBehaviours returns all behaviours reported on the peer identified by peerID. -func (mpbr *MockPeerBehaviourReporter) GetBehaviours(peerID ID) []PeerBehaviour { - mpbr.mtx.RLock() - defer mpbr.mtx.RUnlock() - if items, ok := mpbr.pb[peerID]; ok { - result := make([]PeerBehaviour, len(items)) - copy(result, items) - - return result - } else { - return []PeerBehaviour{} - } -} diff --git a/p2p/peer_behaviour_test.go b/p2p/peer_behaviour_test.go deleted file mode 100644 index 1822844d6..000000000 --- a/p2p/peer_behaviour_test.go +++ /dev/null @@ -1,180 +0,0 @@ -package p2p_test - -import ( - "sync" - "testing" - - "github.com/tendermint/tendermint/p2p" -) - -// TestMockPeerBehaviour tests the MockPeerBehaviour' ability to store reported -// peer behaviour in memory indexed by the peerID -func TestMockPeerBehaviourReporter(t *testing.T) { - var peerID p2p.ID = "MockPeer" - pr := p2p.NewMockPeerBehaviourReporter() - - behaviours := pr.GetBehaviours(peerID) - if len(behaviours) != 0 { - t.Error("Expected to have no behaviours reported") - } - - pr.Report(peerID, p2p.PeerBehaviourBadMessage) - behaviours = pr.GetBehaviours(peerID) - if len(behaviours) != 1 { - t.Error("Expected the peer have one reported behaviour") - } - - if behaviours[0] != p2p.PeerBehaviourBadMessage { - t.Error("Expected PeerBehaviourBadMessage to have been reported") - } -} - -type scriptedBehaviours struct { - PeerID p2p.ID - Behaviours []p2p.PeerBehaviour -} - -type scriptItem struct { - PeerID p2p.ID - Behaviour p2p.PeerBehaviour -} - -// equalBehaviours returns true if a and b contain the same PeerBehaviours with -// the same freequency and otherwise false. -func equalBehaviours(a []p2p.PeerBehaviour, b []p2p.PeerBehaviour) bool { - aHistogram := map[p2p.PeerBehaviour]int{} - bHistogram := map[p2p.PeerBehaviour]int{} - - for _, behaviour := range a { - aHistogram[behaviour] += 1 - } - - for _, behaviour := range b { - bHistogram[behaviour] += 1 - } - - if len(aHistogram) != len(bHistogram) { - return false - } - - for _, behaviour := range a { - if aHistogram[behaviour] != bHistogram[behaviour] { - return false - } - } - - for _, behaviour := range b { - if bHistogram[behaviour] != aHistogram[behaviour] { - return false - } - } - - return true -} - -// TestEqualPeerBehaviours tests that equalBehaviours can tell that two slices -// of peer behaviours can be compared for the behaviours they contain and the -// freequencies that those behaviours occur. -func TestEqualPeerBehaviours(t *testing.T) { - equals := []struct { - left []p2p.PeerBehaviour - right []p2p.PeerBehaviour - }{ - // Empty sets - {[]p2p.PeerBehaviour{}, []p2p.PeerBehaviour{}}, - // Single behaviours - {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, - // Equal Frequencies - {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}, - []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, - // Equal frequencies different orders - {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart}, - []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, - } - - for _, test := range equals { - if !equalBehaviours(test.left, test.right) { - t.Errorf("Expected %#v and %#v to be equal", test.left, test.right) - } - } - - unequals := []struct { - left []p2p.PeerBehaviour - right []p2p.PeerBehaviour - }{ - // Comparing empty sets to non empty sets - {[]p2p.PeerBehaviour{}, []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, - // Different behaviours - {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart}}, - // Same behaviour with different frequencies - {[]p2p.PeerBehaviour{p2p.PeerBehaviourVote}, - []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, - } - - for _, test := range unequals { - if equalBehaviours(test.left, test.right) { - t.Errorf("Expected %#v and %#v to be unequal", test.left, test.right) - } - } -} - -// TestPeerBehaviourConcurrency constructs a scenario in which -// multiple goroutines are using the same MockPeerBehaviourReporter instance. -// This test reproduces the conditions in which MockPeerBehaviourReporter will -// be used within a Reactor Receive method tests to ensure thread safety. -func TestMockPeerBehaviourReporterConcurrency(t *testing.T) { - behaviourScript := []scriptedBehaviours{ - {"1", []p2p.PeerBehaviour{p2p.PeerBehaviourVote}}, - {"2", []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, - {"3", []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, - {"4", []p2p.PeerBehaviour{p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote, p2p.PeerBehaviourVote}}, - {"5", []p2p.PeerBehaviour{p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote, p2p.PeerBehaviourBlockPart, p2p.PeerBehaviourVote}}, - } - - var receiveWg sync.WaitGroup - pr := p2p.NewMockPeerBehaviourReporter() - scriptItems := make(chan scriptItem) - done := make(chan int) - numConsumers := 3 - for i := 0; i < numConsumers; i++ { - receiveWg.Add(1) - go func() { - defer receiveWg.Done() - for { - select { - case pb := <-scriptItems: - pr.Report(pb.PeerID, pb.Behaviour) - case <-done: - return - } - } - }() - } - - var sendingWg sync.WaitGroup - sendingWg.Add(1) - go func() { - defer sendingWg.Done() - for _, item := range behaviourScript { - for _, reason := range item.Behaviours { - scriptItems <- scriptItem{item.PeerID, reason} - } - } - }() - - sendingWg.Wait() - - for i := 0; i < numConsumers; i++ { - done <- 1 - } - - receiveWg.Wait() - - for _, items := range behaviourScript { - reported := pr.GetBehaviours(items.PeerID) - if !equalBehaviours(reported, items.Behaviours) { - t.Errorf("Expected peer %s to have behaved \nExpected: %#v \nGot %#v \n", - items.PeerID, items.Behaviours, reported) - } - } -} From f46ed4aac888ce619b4b2f94fb6998fb5da76a1b Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Fri, 7 Jun 2019 07:22:02 +0000 Subject: [PATCH 226/259] libs/db: Fix the BoltDB Batch.Delete `Delete` should queue a delete operation that targets the entire database, and not just keys that happen to be in the current batch. --- CHANGELOG_PENDING.md | 1 + libs/db/boltdb.go | 44 +++++++++++++++++--------------------------- 2 files changed, 18 insertions(+), 27 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 8a8ad2795..286c1cb02 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -28,3 +28,4 @@ * [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. ### BUG FIXES: +- [libs/db] Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index 6bf474c86..6b44c5560 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -149,55 +149,45 @@ func (bdb *BoltDB) Stats() map[string]string { // boltDBBatch stores key values in sync.Map and dumps them to the underlying // DB upon Write call. type boltDBBatch struct { - buffer []struct { - k []byte - v []byte - } - db *BoltDB + db *BoltDB + ops []operation } // NewBatch returns a new batch. func (bdb *BoltDB) NewBatch() Batch { return &boltDBBatch{ - buffer: make([]struct { - k []byte - v []byte - }, 0), - db: bdb, + ops: nil, + db: bdb, } } // It is safe to modify the contents of the argument after Set returns but not // before. func (bdb *boltDBBatch) Set(key, value []byte) { - bdb.buffer = append(bdb.buffer, struct { - k []byte - v []byte - }{ - key, value, - }) + bdb.ops = append(bdb.ops, operation{opTypeSet, key, value}) } // It is safe to modify the contents of the argument after Delete returns but // not before. func (bdb *boltDBBatch) Delete(key []byte) { - for i, elem := range bdb.buffer { - if bytes.Equal(elem.k, key) { - // delete without preserving order - bdb.buffer[i] = bdb.buffer[len(bdb.buffer)-1] - bdb.buffer = bdb.buffer[:len(bdb.buffer)-1] - return - } - } + bdb.ops = append(bdb.ops, operation{opTypeDelete, key, nil}) } // NOTE: the operation is synchronous (see BoltDB for reasons) func (bdb *boltDBBatch) Write() { err := bdb.db.db.Batch(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) - for _, elem := range bdb.buffer { - if putErr := b.Put(elem.k, elem.v); putErr != nil { - return putErr + for _, op := range bdb.ops { + key := nonEmptyKey(nonNilBytes(op.key)) + switch op.opType { + case opTypeSet: + if putErr := b.Put(key, op.value); putErr != nil { + return putErr + } + case opTypeDelete: + if delErr := b.Delete(key); delErr != nil { + return delErr + } } } return nil From 319ecb3005fe319369f3be91db0fc8605ee2706b Mon Sep 17 00:00:00 2001 From: Yawning Angel Date: Fri, 7 Jun 2019 07:26:47 +0000 Subject: [PATCH 227/259] libs/db: Fix the BoltDB Get and Iterator BoltDB's accessors will return slices that are only valid for the lifetime of the transaction. This adds copies where required to prevent hard to debug crashes (among other things). --- CHANGELOG_PENDING.md | 1 + libs/db/boltdb.go | 12 +++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 286c1cb02..f96956e72 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -29,3 +29,4 @@ ### BUG FIXES: - [libs/db] Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) +- [libs/db] Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) diff --git a/libs/db/boltdb.go b/libs/db/boltdb.go index 6b44c5560..30501dd82 100644 --- a/libs/db/boltdb.go +++ b/libs/db/boltdb.go @@ -66,7 +66,9 @@ func (bdb *BoltDB) Get(key []byte) (value []byte) { key = nonEmptyKey(nonNilBytes(key)) err := bdb.db.View(func(tx *bbolt.Tx) error { b := tx.Bucket(bucket) - value = b.Get(key) + if v := b.Get(key); v != nil { + value = append([]byte{}, v...) + } return nil }) if err != nil { @@ -312,12 +314,16 @@ func (itr *boltDBIterator) Next() { func (itr *boltDBIterator) Key() []byte { itr.assertIsValid() - return itr.currentKey + return append([]byte{}, itr.currentKey...) } func (itr *boltDBIterator) Value() []byte { itr.assertIsValid() - return itr.currentValue + var value []byte + if itr.currentValue != nil { + value = append([]byte{}, itr.currentValue...) + } + return value } func (itr *boltDBIterator) Close() { From 8b7ca8fd99615cda2f91c385428fb671943888f9 Mon Sep 17 00:00:00 2001 From: Jack Zampolin Date: Sun, 9 Jun 2019 05:27:48 -0700 Subject: [PATCH 228/259] switch to go mod (#3613) * Update to using go mod from dep * Remove references to make get_vendor_deps * Specify go version * Set GO111MODULE=on and add -mod=readonly * Fix exported env * switch to using go1.12 everywhere * Fix test scripts * Typo: * Prepend GO111MODULE=on * remove dep cache * Revert "remove dep cache" This reverts commit 45117bda Signed-off-by: Ismail Khoffi * bring back the dependency cache and change it to cache modules instead of vendored deps; also: - bump version for dependency cache - bump version on pkg-cache (includes modules directory) Signed-off-by: Ismail Khoffi * remove some more traces of dep: - remove Gopkg.(toml | lock) - update contributing guidlines - set global default in circleci (GO111MODULE=on) Signed-off-by: Ismail Khoffi * global var failed for `test_cover` with `go: unknown environment setting GO111MODULE=true` although the var was `GO111MODULE: on` Signed-off-by: Ismail Khoffi * Changelog pending entry Signed-off-by: Ismail Khoffi * Add bbolt dependency to go.mod Signed-off-by: Ismail Khoffi * move -mod=readonly to build flags --- .circleci/config.yml | 43 +- CHANGELOG_PENDING.md | 5 + CONTRIBUTING.md | 11 +- DOCKER/Dockerfile.develop | 1 - DOCKER/Dockerfile.testing | 2 +- Gopkg.lock | 561 ------------------- Gopkg.toml | 97 ---- Makefile | 29 +- Vagrantfile | 2 +- abci/tests/test_app/test.sh | 6 +- appveyor.yml | 1 - docs/app-dev/abci-cli.md | 1 - docs/app-dev/getting-started.md | 1 - docs/introduction/install.md | 2 - docs/tools/benchmarking.md | 1 - docs/tools/monitoring.md | 1 - go.mod | 52 ++ go.sum | 118 ++++ libs/circle.yml | 2 +- networks/remote/integration.sh | 1 - scripts/dist.sh | 3 - scripts/get_tools.sh | 3 - scripts/install/install_tendermint_arm.sh | 1 - scripts/install/install_tendermint_bsd.sh | 1 - scripts/install/install_tendermint_osx.sh | 1 - scripts/install/install_tendermint_ubuntu.sh | 1 - test/app/counter_test.sh | 4 +- test/docker/Dockerfile | 3 +- tools/build/Makefile | 8 +- tools/tm-bench/Dockerfile.dev | 1 - tools/tm-bench/README.md | 1 - tools/tm-monitor/Dockerfile.dev | 1 - tools/tm-monitor/README.md | 1 - 33 files changed, 221 insertions(+), 745 deletions(-) delete mode 100644 Gopkg.lock delete mode 100644 Gopkg.toml create mode 100644 go.mod create mode 100644 go.sum diff --git a/.circleci/config.yml b/.circleci/config.yml index 7ad793549..04a03eb25 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,17 +26,12 @@ jobs: - checkout - restore_cache: keys: - - v3-pkg-cache + - v4-pkg-cache - run: name: tools command: | export PATH="$GOBIN:$PATH" make get_tools - - run: - name: dependencies - command: | - export PATH="$GOBIN:$PATH" - make get_vendor_deps - run: name: binaries command: | @@ -48,7 +43,7 @@ jobs: - bin - profiles - save_cache: - key: v3-pkg-cache + key: v4-pkg-cache paths: - /go/pkg - save_cache: @@ -62,7 +57,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: @@ -78,7 +73,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: @@ -87,12 +82,6 @@ jobs: set -ex export PATH="$GOBIN:$PATH" make lint - - run: - name: check_dep - command: | - set -ex - export PATH="$GOBIN:$PATH" - make check_dep test_abci_apps: <<: *defaults @@ -100,7 +89,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: @@ -117,7 +106,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: @@ -132,7 +121,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils @@ -147,7 +136,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: mkdir -p /tmp/logs @@ -157,7 +146,7 @@ jobs: for pkg in $(go list github.com/tendermint/tendermint/... | circleci tests split --split-by=timings); do id=$(basename "$pkg") - go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" + GO111MODULE=on go test -v -timeout 5m -mod=readonly -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log" done - persist_to_workspace: root: /tmp/workspace @@ -172,7 +161,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: @@ -219,7 +208,7 @@ jobs: - attach_workspace: at: /tmp/workspace - restore_cache: - key: v3-pkg-cache + key: v4-pkg-cache - restore_cache: key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - run: @@ -278,15 +267,15 @@ jobs: - run: name: Build dependencies command: | - make get_tools get_vendor_deps + make get_tools - persist_to_workspace: root: . paths: - "release-version.source" - save_cache: - key: v1-release-deps-{{ .Branch }}-{{ .Revision }} - paths: - - "vendor" + key: v2-release-deps-{{ checksum "go.sum" }} + paths: + - "/go/pkg/mod" build_artifacts: <<: *defaults @@ -295,7 +284,7 @@ jobs: - checkout - restore_cache: keys: - - v1-release-deps-{{ .Branch }}-{{ .Revision }} + - v2-release-deps-{{ checksum "go.sum" }} - attach_workspace: at: /tmp/workspace - run: diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index f96956e72..45ed56919 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -4,6 +4,11 @@ ### BREAKING CHANGES: +- \#3613 Switch from golang/dep to Go 1.11 Modules to resolve dependencies: + - it is recommended to switch to Go Modules if your project has tendermint + as a dependency + - read more on Modules here: https://github.com/golang/go/wiki/Modules + * CLI/RPC/Config - [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` -> `results.deliver_tx`). See docs for details. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 82705f1e8..e68e6d1ee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,7 +42,7 @@ Please don't make Pull Requests to `master`. ## Dependencies -We use [dep](https://github.com/golang/dep) to manage dependencies. +We use [go modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. That said, the master branch of every Tendermint repository should just build with `go get`, which means they should be kept up-to-date with their @@ -50,18 +50,17 @@ dependencies so we can get away with telling people they can just `go get` our software. Since some dependencies are not under our control, a third party may break our -build, in which case we can fall back on `dep ensure` (or `make -get_vendor_deps`). Even for dependencies under our control, dep helps us to +build, in which case we can fall back on `go mod tidy`. Even for dependencies under our control, go helps us to keep multiple repos in sync as they evolve. Anything with an executable, such as apps, tools, and the core, should use dep. -Run `dep status` to get a list of vendor dependencies that may not be +Run `go list -u -m all` to get a list of dependencies that may not be up-to-date. When updating dependencies, please only update the particular dependencies you -need. Instead of running `dep ensure -update`, which will update anything, +need. Instead of running `go get -u=patch`, which will update anything, specify exactly the dependency you want to update, eg. -`dep ensure -update github.com/tendermint/go-amino`. +`GO111MODULE=on go get -u github.com/tendermint/go-amino@master`. ## Vagrant diff --git a/DOCKER/Dockerfile.develop b/DOCKER/Dockerfile.develop index 5759e7658..943b21291 100644 --- a/DOCKER/Dockerfile.develop +++ b/DOCKER/Dockerfile.develop @@ -19,7 +19,6 @@ RUN mkdir -p /go/src/github.com/tendermint/tendermint && \ git clone https://github.com/tendermint/tendermint . && \ git checkout develop && \ make get_tools && \ - make get_vendor_deps && \ make install && \ cd - && \ rm -rf /go/src/github.com/tendermint/tendermint && \ diff --git a/DOCKER/Dockerfile.testing b/DOCKER/Dockerfile.testing index b82afe2a8..a658aeb10 100644 --- a/DOCKER/Dockerfile.testing +++ b/DOCKER/Dockerfile.testing @@ -1,4 +1,4 @@ -FROM golang:1.10.1 +FROM golang:1.12 # Grab deps (jq, hexdump, xxd, killall) diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 6d9236559..000000000 --- a/Gopkg.lock +++ /dev/null @@ -1,561 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d" - name = "github.com/beorn7/perks" - packages = ["quantile"] - pruneopts = "UT" - revision = "3a771d992973f24aa725d07868b467d1ddfceafb" - -[[projects]] - digest = "1:093bf93a65962e8191e3e8cd8fc6c363f83d43caca9739c906531ba7210a9904" - name = "github.com/btcsuite/btcd" - packages = ["btcec"] - pruneopts = "UT" - revision = "ed77733ec07dfc8a513741138419b8d9d3de9d2d" - -[[projects]] - digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf" - name = "github.com/btcsuite/btcutil" - packages = [ - "base58", - "bech32", - ] - pruneopts = "UT" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" - -[[projects]] - digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" - name = "github.com/davecgh/go-spew" - packages = ["spew"] - pruneopts = "UT" - revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" - version = "v1.1.1" - -[[projects]] - digest = "1:5f7414cf41466d4b4dd7ec52b2cd3e481e08cfd11e7e24fef730c0e483e88bb1" - name = "github.com/etcd-io/bbolt" - packages = ["."] - pruneopts = "UT" - revision = "63597a96ec0ad9e6d43c3fc81e809909e0237461" - version = "v1.3.2" - -[[projects]] - digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70" - name = "github.com/fortytw2/leaktest" - packages = ["."] - pruneopts = "UT" - revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e" - version = "v1.2.0" - -[[projects]] - digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd" - name = "github.com/fsnotify/fsnotify" - packages = ["."] - pruneopts = "UT" - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11" - name = "github.com/go-kit/kit" - packages = [ - "log", - "log/level", - "log/term", - "metrics", - "metrics/discard", - "metrics/internal/lv", - "metrics/prometheus", - ] - pruneopts = "UT" - revision = "4dc7be5d2d12881735283bcab7352178e190fc71" - version = "v0.6.0" - -[[projects]] - digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659" - name = "github.com/go-logfmt/logfmt" - packages = ["."] - pruneopts = "UT" - revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5" - version = "v0.3.0" - -[[projects]] - digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d" - name = "github.com/go-stack/stack" - packages = ["."] - pruneopts = "UT" - revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a" - version = "v1.8.0" - -[[projects]] - digest = "1:95e1006e41c641abd2f365dfa0f1213c04da294e7cd5f0bf983af234b775db64" - name = "github.com/gogo/protobuf" - packages = [ - "gogoproto", - "jsonpb", - "proto", - "protoc-gen-gogo/descriptor", - "sortkeys", - "types", - ] - pruneopts = "UT" - revision = "ba06b47c162d49f2af050fb4c75bcbc86a159d5c" - version = "v1.2.1" - -[[projects]] - digest = "1:239c4c7fd2159585454003d9be7207167970194216193a8a210b8d29576f19c9" - name = "github.com/golang/protobuf" - packages = [ - "proto", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/timestamp", - ] - pruneopts = "UT" - revision = "c823c79ea1570fb5ff454033735a8e68575d1d0f" - version = "v1.3.0" - -[[projects]] - branch = "master" - digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009" - name = "github.com/golang/snappy" - packages = ["."] - pruneopts = "UT" - revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" - -[[projects]] - digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e" - name = "github.com/gorilla/websocket" - packages = ["."] - pruneopts = "UT" - revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b" - version = "v1.2.0" - -[[projects]] - digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token", - ] - pruneopts = "UT" - revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241" - version = "v1.0.0" - -[[projects]] - digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be" - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - pruneopts = "UT" - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - digest = "1:a74b5a8e34ee5843cd6e65f698f3e75614f812ff170c2243425d75bc091e9af2" - name = "github.com/jmhodges/levigo" - packages = ["."] - pruneopts = "UT" - revision = "853d788c5c416eaaee5b044570784a96c7a26975" - version = "v1.0.0" - -[[projects]] - branch = "master" - digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72" - name = "github.com/kr/logfmt" - packages = ["."] - pruneopts = "UT" - revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0" - -[[projects]] - digest = "1:53e8c5c79716437e601696140e8b1801aae4204f4ec54a504333702a49572c4f" - name = "github.com/magiconair/properties" - packages = [ - ".", - "assert", - ] - pruneopts = "UT" - revision = "c2353362d570a7bfa228149c62842019201cfb71" - version = "v1.8.0" - -[[projects]] - digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc" - name = "github.com/matttproud/golang_protobuf_extensions" - packages = ["pbutil"] - pruneopts = "UT" - revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c" - version = "v1.0.1" - -[[projects]] - digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - pruneopts = "UT" - revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe" - version = "v1.1.2" - -[[projects]] - digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e" - name = "github.com/pelletier/go-toml" - packages = ["."] - pruneopts = "UT" - revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194" - version = "v1.2.0" - -[[projects]] - digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" - name = "github.com/pkg/errors" - packages = ["."] - pruneopts = "UT" - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - pruneopts = "UT" - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23" - name = "github.com/prometheus/client_golang" - packages = [ - "prometheus", - "prometheus/internal", - "prometheus/promhttp", - ] - pruneopts = "UT" - revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3" - version = "v0.9.1" - -[[projects]] - branch = "master" - digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4" - name = "github.com/prometheus/client_model" - packages = ["go"] - pruneopts = "UT" - revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f" - -[[projects]] - branch = "master" - digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4" - name = "github.com/prometheus/common" - packages = [ - "expfmt", - "internal/bitbucket.org/ww/goautoneg", - "model", - ] - pruneopts = "UT" - revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6" - -[[projects]] - branch = "master" - digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc" - name = "github.com/prometheus/procfs" - packages = [ - ".", - "internal/util", - "nfs", - "xfs", - ] - pruneopts = "UT" - revision = "185b4288413d2a0dd0806f78c90dde719829e5ae" - -[[projects]] - digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c" - name = "github.com/rcrowley/go-metrics" - packages = ["."] - pruneopts = "UT" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - -[[projects]] - digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758" - name = "github.com/rs/cors" - packages = ["."] - pruneopts = "UT" - revision = "9a47f48565a795472d43519dd49aac781f3034fb" - version = "v1.6.0" - -[[projects]] - digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd" - name = "github.com/spf13/afero" - packages = [ - ".", - "mem", - ] - pruneopts = "UT" - revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd" - version = "v1.1.2" - -[[projects]] - digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc" - name = "github.com/spf13/cast" - packages = ["."] - pruneopts = "UT" - revision = "8c9545af88b134710ab1cd196795e7f2388358d7" - version = "v1.3.0" - -[[projects]] - digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e" - name = "github.com/spf13/cobra" - packages = ["."] - pruneopts = "UT" - revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" - version = "v0.0.1" - -[[projects]] - digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - pruneopts = "UT" - revision = "4a4406e478ca629068e7768fc33f3f044173c0a6" - version = "v1.0.0" - -[[projects]] - digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2" - name = "github.com/spf13/pflag" - packages = ["."] - pruneopts = "UT" - revision = "298182f68c66c05229eb03ac171abe6e309ee79a" - version = "v1.0.3" - -[[projects]] - digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96" - name = "github.com/spf13/viper" - packages = ["."] - pruneopts = "UT" - revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" - version = "v1.0.0" - -[[projects]] - digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6" - name = "github.com/stretchr/testify" - packages = [ - "assert", - "require", - ] - pruneopts = "UT" - revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" - version = "v1.2.1" - -[[projects]] - branch = "master" - digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c" - name = "github.com/syndtr/goleveldb" - packages = [ - "leveldb", - "leveldb/cache", - "leveldb/comparer", - "leveldb/errors", - "leveldb/filter", - "leveldb/iterator", - "leveldb/journal", - "leveldb/memdb", - "leveldb/opt", - "leveldb/storage", - "leveldb/table", - "leveldb/util", - ] - pruneopts = "UT" - revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43" - -[[projects]] - digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a" - name = "github.com/tendermint/go-amino" - packages = ["."] - pruneopts = "UT" - revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885" - version = "v0.14.1" - -[[projects]] - branch = "master" - digest = "1:f4edb30d5ff238e2abba10457010f74cd55ae20bbda8c54db1a07155fa020490" - name = "golang.org/x/crypto" - packages = [ - "bcrypt", - "blowfish", - "chacha20poly1305", - "curve25519", - "ed25519", - "ed25519/internal/edwards25519", - "hkdf", - "internal/chacha20", - "internal/subtle", - "nacl/box", - "nacl/secretbox", - "openpgp/armor", - "openpgp/errors", - "poly1305", - "ripemd160", - "salsa20/salsa", - ] - pruneopts = "UT" - revision = "8dd112bcdc25174059e45e07517d9fc663123347" - -[[projects]] - digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1" - name = "golang.org/x/net" - packages = [ - "context", - "http/httpguts", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "netutil", - "trace", - ] - pruneopts = "UT" - revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f" - -[[projects]] - branch = "master" - digest = "1:6f86e2f2e2217cd4d74dec6786163cf80e4d2b99adb341ecc60a45113b844dca" - name = "golang.org/x/sys" - packages = [ - "cpu", - "unix", - ] - pruneopts = "UT" - revision = "7e31e0c00fa05cb5fbf4347b585621d6709e19a4" - -[[projects]] - digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18" - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable", - ] - pruneopts = "UT" - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a" - name = "google.golang.org/genproto" - packages = ["googleapis/rpc/status"] - pruneopts = "UT" - revision = "b69ba1387ce2108ac9bc8e8e5e5a46e7d5c72313" - -[[projects]] - digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "encoding/proto", - "grpclog", - "internal", - "internal/backoff", - "internal/channelz", - "internal/grpcrand", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport", - ] - pruneopts = "UT" - revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8" - version = "v1.13.0" - -[[projects]] - digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" - name = "gopkg.in/yaml.v2" - packages = ["."] - pruneopts = "UT" - revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" - version = "v2.2.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - input-imports = [ - "github.com/btcsuite/btcd/btcec", - "github.com/btcsuite/btcutil/base58", - "github.com/btcsuite/btcutil/bech32", - "github.com/etcd-io/bbolt", - "github.com/fortytw2/leaktest", - "github.com/go-kit/kit/log", - "github.com/go-kit/kit/log/level", - "github.com/go-kit/kit/log/term", - "github.com/go-kit/kit/metrics", - "github.com/go-kit/kit/metrics/discard", - "github.com/go-kit/kit/metrics/prometheus", - "github.com/go-logfmt/logfmt", - "github.com/gogo/protobuf/gogoproto", - "github.com/gogo/protobuf/jsonpb", - "github.com/gogo/protobuf/proto", - "github.com/gogo/protobuf/types", - "github.com/golang/protobuf/proto", - "github.com/golang/protobuf/ptypes/timestamp", - "github.com/gorilla/websocket", - "github.com/jmhodges/levigo", - "github.com/magiconair/properties/assert", - "github.com/pkg/errors", - "github.com/prometheus/client_golang/prometheus", - "github.com/prometheus/client_golang/prometheus/promhttp", - "github.com/rcrowley/go-metrics", - "github.com/rs/cors", - "github.com/spf13/cobra", - "github.com/spf13/viper", - "github.com/stretchr/testify/assert", - "github.com/stretchr/testify/require", - "github.com/syndtr/goleveldb/leveldb", - "github.com/syndtr/goleveldb/leveldb/errors", - "github.com/syndtr/goleveldb/leveldb/iterator", - "github.com/syndtr/goleveldb/leveldb/opt", - "github.com/tendermint/go-amino", - "golang.org/x/crypto/bcrypt", - "golang.org/x/crypto/chacha20poly1305", - "golang.org/x/crypto/curve25519", - "golang.org/x/crypto/ed25519", - "golang.org/x/crypto/hkdf", - "golang.org/x/crypto/nacl/box", - "golang.org/x/crypto/nacl/secretbox", - "golang.org/x/crypto/openpgp/armor", - "golang.org/x/crypto/ripemd160", - "golang.org/x/net/context", - "golang.org/x/net/netutil", - "google.golang.org/grpc", - "google.golang.org/grpc/credentials", - ] - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index e5abf051c..000000000 --- a/Gopkg.toml +++ /dev/null @@ -1,97 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true -# -########################################################### - -# Allow only patch releases for serialization libraries -[[constraint]] - name = "github.com/etcd-io/bbolt" - version = "v1.3.2" - -[[constraint]] - name = "github.com/tendermint/go-amino" - version = "~0.14.1" - -[[constraint]] - name = "github.com/gogo/protobuf" - version = "~1.2.1" - -[[constraint]] - name = "github.com/golang/protobuf" - version = "~1.3.0" - -# Allow only minor releases for other libraries -[[constraint]] - name = "github.com/go-kit/kit" - version = "^0.6.0" - -[[constraint]] - name = "github.com/gorilla/websocket" - version = "^1.2.0" - -[[constraint]] - name = "github.com/rs/cors" - version = "^1.6.0" - -[[constraint]] - name = "github.com/pkg/errors" - version = "^0.8.0" - -[[constraint]] - name = "github.com/spf13/cobra" - version = "^0.0.1" - -[[constraint]] - name = "github.com/spf13/viper" - version = "^1.0.0" - -[[constraint]] - name = "github.com/stretchr/testify" - version = "^1.2.1" - -[[constraint]] - name = "google.golang.org/grpc" - version = "^1.13.0" - -[[constraint]] - name = "github.com/fortytw2/leaktest" - version = "^1.2.0" - -[[constraint]] - name = "github.com/prometheus/client_golang" - version = "^0.9.1" - -[[constraint]] - name = "github.com/jmhodges/levigo" - version = "^1.0.0" - -################################### -## Repos which don't have releases. - -## - github.com/btcsuite/btcd -## - golang.org/x/crypto -## - github.com/btcsuite/btcutil -## - github.com/rcrowley/go-metrics -## - golang.org/x/net - -[prune] - go-tests = true - unused-packages = true diff --git a/Makefile b/Makefile index c5bde692d..1980ac861 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,5 @@ GOTOOLS = \ github.com/mitchellh/gox \ - github.com/golang/dep/cmd/dep \ github.com/golangci/golangci-lint/cmd/golangci-lint \ github.com/gogo/protobuf/protoc-gen-gogo \ github.com/square/certstrap @@ -8,13 +7,15 @@ GOBIN?=${GOPATH}/bin PACKAGES=$(shell go list ./...) OUTPUT?=build/tendermint +export GO111MODULE = on + INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf BUILD_TAGS?='tendermint' -BUILD_FLAGS = -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" +BUILD_FLAGS = -mod=readonly -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" all: check build test install -check: check_tools get_vendor_deps +check: check_tools ######################################## ### Build Tendermint @@ -29,10 +30,10 @@ build_race: CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o $(OUTPUT) ./cmd/tendermint install: - CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint + CGO_ENABLED=0 go install $(BUILD_FLAGS) -tags $(BUILD_TAGS) ./cmd/tendermint install_c: - CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint + CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) cleveldb" ./cmd/tendermint ######################################## ### Protobuf @@ -56,10 +57,10 @@ protoc_abci: abci/types/types.pb.go protoc_proto3types: types/proto3/block.pb.go build_abci: - @go build -i ./abci/cmd/... + @go build -mod=readonly -i ./abci/cmd/... install_abci: - @go install ./abci/cmd/... + @go install -mod=readonly ./abci/cmd/... ######################################## ### Distribution @@ -85,11 +86,6 @@ update_tools: @echo "--> Updating tools" ./scripts/get_tools.sh -#Update dependencies -get_vendor_deps: - @echo "--> Running dep" - @dep ensure - #For ABCI and libs get_protoc: @# https://github.com/google/protobuf/releases @@ -192,7 +188,6 @@ test_p2p: test_integrations: make build_docker_test_image make get_tools - make get_vendor_deps make install make test_cover make test_apps @@ -254,10 +249,6 @@ rpc-docs: cat rpc/core/slate_header.txt > $(DESTINATION) 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,' >> $(DESTINATION) -check_dep: - dep status >> /dev/null - !(grep -n branch Gopkg.toml) - ########################################################### ### Docker image @@ -270,7 +261,7 @@ build-docker: ### Local testnet using docker # Build linux binary on other platforms -build-linux: get_tools get_vendor_deps +build-linux: get_tools GOOS=linux GOARCH=amd64 $(MAKE) build build-docker-localnode: @@ -312,4 +303,4 @@ build-slate: # 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 build_abci dist install install_abci check_dep check_tools get_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint +.PHONY: check build build_race build_abci dist install install_abci check_tools get_tools update_tools draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint diff --git a/Vagrantfile b/Vagrantfile index da4f8ac3d..67de74297 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -57,6 +57,6 @@ Vagrant.configure("2") do |config| # get all deps and tools, ready to install/test su - vagrant -c 'source /home/vagrant/.bash_profile' - su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_vendor_deps' + su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools' SHELL end diff --git a/abci/tests/test_app/test.sh b/abci/tests/test_app/test.sh index 230c94163..c0bdace27 100755 --- a/abci/tests/test_app/test.sh +++ b/abci/tests/test_app/test.sh @@ -3,6 +3,8 @@ set -e # These tests spawn the counter app and server by execing the ABCI_APP command and run some simple client tests against it +export GO111MODULE=on + # Get the directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done @@ -13,13 +15,13 @@ cd "$DIR" echo "RUN COUNTER OVER SOCKET" # test golang counter -ABCI_APP="counter" go run ./*.go +ABCI_APP="counter" go run -mod=readonly ./*.go echo "----------------------" echo "RUN COUNTER OVER GRPC" # test golang counter via grpc -ABCI_APP="counter --abci=grpc" ABCI="grpc" go run ./*.go +ABCI_APP="counter --abci=grpc" ABCI="grpc" go run -mod=readonly ./*.go echo "----------------------" # test nodejs counter diff --git a/appveyor.yml b/appveyor.yml index 1ddf8fdd2..4aa8c2abb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -7,7 +7,6 @@ clone_folder: c:\go\path\src\github.com\tendermint\tendermint before_build: - cmd: set GOPATH=%GOROOT%\path - cmd: set PATH=%GOPATH%\bin;%PATH% -- cmd: make get_vendor_deps build_script: - cmd: make test test: off diff --git a/docs/app-dev/abci-cli.md b/docs/app-dev/abci-cli.md index 3e6cced87..b09b9a11b 100644 --- a/docs/app-dev/abci-cli.md +++ b/docs/app-dev/abci-cli.md @@ -16,7 +16,6 @@ cd $GOPATH/src/github.com/tendermint git clone https://github.com/tendermint/tendermint.git cd tendermint make get_tools -make get_vendor_deps make install_abci ``` diff --git a/docs/app-dev/getting-started.md b/docs/app-dev/getting-started.md index 5509a7012..9110761e2 100644 --- a/docs/app-dev/getting-started.md +++ b/docs/app-dev/getting-started.md @@ -28,7 +28,6 @@ Then run go get github.com/tendermint/tendermint cd $GOPATH/src/github.com/tendermint/tendermint make get_tools -make get_vendor_deps make install_abci ``` diff --git a/docs/introduction/install.md b/docs/introduction/install.md index e96135826..4f35ffef7 100644 --- a/docs/introduction/install.md +++ b/docs/introduction/install.md @@ -29,7 +29,6 @@ cd tendermint ``` make get_tools -make get_vendor_deps ``` ### Compile @@ -71,7 +70,6 @@ To upgrade, run ``` cd $GOPATH/src/github.com/tendermint/tendermint git pull origin master -make get_vendor_deps make install ``` diff --git a/docs/tools/benchmarking.md b/docs/tools/benchmarking.md index 67a472e4b..a30ab54ab 100644 --- a/docs/tools/benchmarking.md +++ b/docs/tools/benchmarking.md @@ -75,6 +75,5 @@ Each of the connections is handled via two separate goroutines. ## Development ``` -make get_vendor_deps make test ``` diff --git a/docs/tools/monitoring.md b/docs/tools/monitoring.md index fa3901dde..26b90ed70 100644 --- a/docs/tools/monitoring.md +++ b/docs/tools/monitoring.md @@ -88,6 +88,5 @@ websocket. ``` make get_tools -make get_vendor_deps make test ``` diff --git a/go.mod b/go.mod new file mode 100644 index 000000000..8fe1a124b --- /dev/null +++ b/go.mod @@ -0,0 +1,52 @@ +module github.com/tendermint/tendermint + +go 1.12 + +require ( + github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 + github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d + github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a + github.com/davecgh/go-spew v1.1.1 + github.com/etcd-io/bbolt v1.3.2 + github.com/fortytw2/leaktest v1.2.0 + github.com/fsnotify/fsnotify v1.4.7 + github.com/go-kit/kit v0.6.0 + github.com/go-logfmt/logfmt v0.3.0 + github.com/go-stack/stack v1.8.0 + github.com/gogo/protobuf v1.2.1 + github.com/golang/protobuf v1.3.0 + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db + github.com/gorilla/websocket v1.2.0 + github.com/hashicorp/hcl v1.0.0 + github.com/inconshreveable/mousetrap v1.0.0 + github.com/jmhodges/levigo v1.0.0 + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 + github.com/magiconair/properties v1.8.0 + github.com/matttproud/golang_protobuf_extensions v1.0.1 + github.com/mitchellh/mapstructure v1.1.2 + github.com/pelletier/go-toml v1.2.0 + github.com/pkg/errors v0.8.0 + github.com/pmezard/go-difflib v1.0.0 + github.com/prometheus/client_golang v0.9.1 + github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 + github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 + github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d + github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 + github.com/rs/cors v1.6.0 + github.com/spf13/afero v1.1.2 + github.com/spf13/cast v1.3.0 + github.com/spf13/cobra v0.0.1 + github.com/spf13/jwalterweatherman v1.0.0 + github.com/spf13/pflag v1.0.3 + github.com/spf13/viper v1.0.0 + github.com/stretchr/testify v1.2.2 + github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e + github.com/tendermint/go-amino v0.14.1 + golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 + golang.org/x/net v0.0.0-20180906233101-161cd47e91fd + golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a + golang.org/x/text v0.3.0 + google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 + google.golang.org/grpc v1.13.0 + gopkg.in/yaml.v2 v2.2.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 000000000..10e54c0fd --- /dev/null +++ b/go.sum @@ -0,0 +1,118 @@ +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= +github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= +github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0= +github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/go-kit/kit v0.6.0 h1:wTifptAGIyIuir4bRyN4h7+kAa2a4eepLYVmRe5qqQ8= +github.com/go-kit/kit v0.6.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= +github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= +github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= +github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1 h1:K47Rk0v/fkEfwfQet2KWhscE0cJzjgCCDBG2KHZoVno= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 h1:Cto4X6SVMWRPBkJ/3YHn1iDGDGc/Z+sW+AEMKHMVvN4= +github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d h1:GoAlyOgbOEIFdaDqxJVlbOQ1DtGmZWs/Qau0hIlk+WQ= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= +github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= +github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.1 h1:zZh3X5aZbdnoj+4XkaBxKfhO4ot82icYdhhREIAXIj8= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.0.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I= +github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e h1:91EeXI4y4ShkyzkMqZ7QP/ZTIqwXp3RuDu5WFzxcFAs= +github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= +github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= +github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU= +golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180710023853-292b43bbf7cb/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpdZNbLuQj6FOzCaZe3w+vhLjn5AcFA= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.13.0 h1:bHIbVsCwmvbArgCJmLdgOdHFXlKqTOVjbibbS19cXHc= +google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/libs/circle.yml b/libs/circle.yml index 390ffb039..2b7d1266c 100644 --- a/libs/circle.yml +++ b/libs/circle.yml @@ -15,7 +15,7 @@ dependencies: test: override: - - cd $PROJECT_PATH && make get_tools && make get_vendor_deps && bash ./test.sh + - cd $PROJECT_PATH && make get_tools && bash ./test.sh post: - cd "$PROJECT_PATH" && bash <(curl -s https://codecov.io/bash) -f coverage.txt - cd "$PROJECT_PATH" && mv coverage.txt "${CIRCLE_ARTIFACTS}" diff --git a/networks/remote/integration.sh b/networks/remote/integration.sh index fb1b0b327..c2d7c3a36 100644 --- a/networks/remote/integration.sh +++ b/networks/remote/integration.sh @@ -31,7 +31,6 @@ cd $GOPATH/src/$REPO ## build make get_tools -make get_vendor_deps make build # generate an ssh key diff --git a/scripts/dist.sh b/scripts/dist.sh index f999c5376..ac62f1099 100755 --- a/scripts/dist.sh +++ b/scripts/dist.sh @@ -31,9 +31,6 @@ XC_EXCLUDE=${XC_EXCLUDE:-" darwin/arm solaris/amd64 solaris/386 solaris/arm free # Make sure build tools are available. make get_tools -# Get VENDORED dependencies -make get_vendor_deps - # Build! # ldflags: -s Omit the symbol table and debug information. # -w Omit the DWARF symbol table. diff --git a/scripts/get_tools.sh b/scripts/get_tools.sh index dd9566917..d8c17df11 100755 --- a/scripts/get_tools.sh +++ b/scripts/get_tools.sh @@ -48,9 +48,6 @@ installFromGithub() { echo "" } -######################## COMMON TOOLS ######################################## -installFromGithub golang/dep 22125cfaa6ddc71e145b1535d4b7ee9744fefff2 cmd/dep - ######################## DEVELOPER TOOLS ##################################### installFromGithub gogo/protobuf 61dbc136cf5d2f08d68a011382652244990a53a9 protoc-gen-gogo diff --git a/scripts/install/install_tendermint_arm.sh b/scripts/install/install_tendermint_arm.sh index b260d8d07..085ba82f4 100644 --- a/scripts/install/install_tendermint_arm.sh +++ b/scripts/install/install_tendermint_arm.sh @@ -32,7 +32,6 @@ git checkout $BRANCH # XXX: uncomment if branch isn't master # git fetch origin $BRANCH make get_tools -make get_vendor_deps make install # the binary is located in $GOPATH/bin diff --git a/scripts/install/install_tendermint_bsd.sh b/scripts/install/install_tendermint_bsd.sh index b76b94855..294155d0e 100644 --- a/scripts/install/install_tendermint_bsd.sh +++ b/scripts/install/install_tendermint_bsd.sh @@ -47,7 +47,6 @@ cd "$GOPATH/src/$REPO" # build & install master git checkout $BRANCH gmake get_tools -gmake get_vendor_deps gmake install # the binary is located in $GOPATH/bin diff --git a/scripts/install/install_tendermint_osx.sh b/scripts/install/install_tendermint_osx.sh index b4107ab01..ee799f66a 100644 --- a/scripts/install/install_tendermint_osx.sh +++ b/scripts/install/install_tendermint_osx.sh @@ -37,5 +37,4 @@ git checkout $BRANCH # XXX: uncomment if branch isn't master # git fetch origin $BRANCH make get_tools -make get_vendor_deps make install diff --git a/scripts/install/install_tendermint_ubuntu.sh b/scripts/install/install_tendermint_ubuntu.sh index 3fe6ea8ed..2e5558ff6 100644 --- a/scripts/install/install_tendermint_ubuntu.sh +++ b/scripts/install/install_tendermint_ubuntu.sh @@ -41,7 +41,6 @@ git checkout $BRANCH # XXX: uncomment if branch isn't master # git fetch origin $BRANCH make get_tools -make get_vendor_deps make install # the binary is located in $GOPATH/bin diff --git a/test/app/counter_test.sh b/test/app/counter_test.sh index 868f8d037..a4f7c83b9 100755 --- a/test/app/counter_test.sh +++ b/test/app/counter_test.sh @@ -1,5 +1,7 @@ #! /bin/bash +export GO111MODULE=on + if [[ "$GRPC_BROADCAST_TX" == "" ]]; then GRPC_BROADCAST_TX="" fi @@ -38,7 +40,7 @@ if [[ "$GRPC_BROADCAST_TX" != "" ]]; then rm grpc_client fi echo "... building grpc_client" - go build -o grpc_client grpc_client.go + go build -mod=readonly -o grpc_client grpc_client.go fi function sendTx() { diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index 1a64d4173..77cc515e5 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.10 +FROM golang:1.12 # Add testing deps for curl RUN echo 'deb http://httpredir.debian.org/debian testing main non-free contrib' >> /etc/apt/sources.list @@ -20,7 +20,6 @@ COPY . $REPO # Install the vendored dependencies # docker caching prevents reinstall on code change! RUN make get_tools -RUN make get_vendor_deps # install ABCI CLI RUN make install_abci diff --git a/tools/build/Makefile b/tools/build/Makefile index f9384ac64..8c33ffd5d 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile @@ -64,7 +64,7 @@ build-tendermint: git-branch gopath-setup @echo "*** Building tendermint" go get -d -u github.com/tendermint/tendermint/cmd/tendermint cd $(GOPATH)/src/github.com/tendermint/tendermint && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/tendermint get_tools get_vendor_deps build + export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/tendermint get_tools build cp $(GOPATH)/src/github.com/tendermint/tendermint/build/tendermint $(GOPATH)/bin @echo "*** Built tendermint" @@ -72,7 +72,7 @@ build-ethermint: git-branch gopath-setup @echo "*** Building ethermint" go get -d -u github.com/tendermint/ethermint/cmd/ethermint cd $(GOPATH)/src/github.com/tendermint/ethermint && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/ethermint get_vendor_deps build + export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/tendermint/ethermint build cp $(GOPATH)/src/github.com/tendermint/ethermint/build/ethermint $(GOPATH)/bin @echo "*** Built ethermint" @@ -80,14 +80,14 @@ build-gaia: git-branch gopath-setup @echo "*** Building gaia" go get -d -u go github.com/cosmos/gaia || echo "Workaround for go downloads." cd $(GOPATH)/src/github.com/cosmos/gaia && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/gaia get_vendor_deps install + export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/gaia install @echo "*** Built gaia" build-basecoind: git-branch gopath-setup @echo "*** Building basecoind from cosmos-sdk" go get -d -u github.com/cosmos/cosmos-sdk/examples/basecoin/cmd/basecoind cd $(GOPATH)/src/github.com/cosmos/cosmos-sdk && git checkout "$(GIT_BRANCH)" && git pull - export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/cosmos-sdk get_tools get_vendor_deps build + export PATH=$(GOPATH)/bin:$(PATH) && $(MAKE) -C $(GOPATH)/src/github.com/cosmos/cosmos-sdk get_tools build cp $(GOPATH)/src/github.com/cosmos/cosmos-sdk/build/basecoind $(GOPATH)/bin/basecoind @echo "*** Built basecoind from cosmos-sdk" diff --git a/tools/tm-bench/Dockerfile.dev b/tools/tm-bench/Dockerfile.dev index 469bb8150..1151965a2 100644 --- a/tools/tm-bench/Dockerfile.dev +++ b/tools/tm-bench/Dockerfile.dev @@ -9,4 +9,3 @@ RUN make get_tools COPY . /go/src/github.com/tendermint/tendermint/tools/tm-bench -RUN make get_vendor_deps diff --git a/tools/tm-bench/README.md b/tools/tm-bench/README.md index b4e8cec5a..d5ed1231f 100644 --- a/tools/tm-bench/README.md +++ b/tools/tm-bench/README.md @@ -100,6 +100,5 @@ Each of the connections is handled via two separate goroutines. ## Development ``` -make get_vendor_deps make test ``` diff --git a/tools/tm-monitor/Dockerfile.dev b/tools/tm-monitor/Dockerfile.dev index 5bfbbfd5a..e593bf89c 100644 --- a/tools/tm-monitor/Dockerfile.dev +++ b/tools/tm-monitor/Dockerfile.dev @@ -9,4 +9,3 @@ RUN make get_tools COPY . /go/src/github.com/tendermint/tools/tm-monitor -RUN make get_vendor_deps diff --git a/tools/tm-monitor/README.md b/tools/tm-monitor/README.md index 374a56b0a..2bd367b99 100644 --- a/tools/tm-monitor/README.md +++ b/tools/tm-monitor/README.md @@ -87,6 +87,5 @@ websocket. ``` make get_tools -make get_vendor_deps make test ``` From ab0835463f1f89dcadf83f9492e98d85583b0e71 Mon Sep 17 00:00:00 2001 From: Alexander Bezobchuk Date: Wed, 12 Jun 2019 14:03:45 +0200 Subject: [PATCH 229/259] abci: Refactor tagging events using list of lists (#3643) ## PR This PR introduces a fundamental breaking change to the structure of ABCI response and tx tags and the way they're processed. Namely, the SDK can support more complex and aggregated events for distribution and slashing. In addition, block responses can include duplicate keys in events. Implement new Event type. An event has a type and a list of KV pairs (ie. list-of-lists). Typical events may look like: "rewards": [{"amount": "5000uatom", "validator": "...", "recipient": "..."}] "sender": [{"address": "...", "balance": "100uatom"}] The events are indexed by {even.type}.{even.attribute[i].key}/.... In this case a client would subscribe or query for rewards.recipient='...' ABCI response types and related types now include Events []Event instead of Tags []cmn.KVPair. PubSub logic now publishes/matches against map[string][]string instead of map[string]string to support duplicate keys in response events (from #1385). A match is successful if the value is found in the slice of strings. closes: #1859 closes: #2905 ## Commits: * Implement Event ABCI type and updates responses to use events * Update messages_test.go * Update kvstore.go * Update event_bus.go * Update subscription.go * Update pubsub.go * Update kvstore.go * Update query logic to handle slice of strings in events * Update Empty#Matches and unit tests * Update pubsub logic * Update EventBus#Publish * Update kv tx indexer * Update godocs * Update ResultEvent to use slice of strings; update RPC * Update more tests * Update abci.md * Check for key in validateAndStringifyEvents * Fix KV indexer to skip empty keys * Fix linting errors * Update CHANGELOG_PENDING.md * Update docs/spec/abci/abci.md Co-Authored-By: Federico Kunze <31522760+fedekunze@users.noreply.github.com> * Update abci/types/types.proto Co-Authored-By: Ethan Buchman * Update docs/spec/abci/abci.md Co-Authored-By: Ethan Buchman * Update libs/pubsub/query/query.go Co-Authored-By: Ethan Buchman * Update match function to match if ANY value matches * Implement TestSubscribeDuplicateKeys * Update TestMatches to include multi-key test cases * Update events.go * Update Query interface godoc * Update match godoc * Add godoc for matchValue * DRY-up tx indexing * Return error from PublishWithEvents in EventBus#Publish * Update PublishEventNewBlockHeader to return an error * Fix build * Update events doc in ABCI * Update ABCI events godoc * Implement TestEventBusPublishEventTxDuplicateKeys * Update TestSubscribeDuplicateKeys to be table-driven * Remove mod file * Remove markdown from events godoc * Implement TestTxSearchDeprecatedIndexing test --- CHANGELOG_PENDING.md | 10 +- abci/example/kvstore/kvstore.go | 15 +- abci/types/messages_test.go | 19 +- abci/types/types.pb.go | 938 +++++++++++++++++++++----------- abci/types/types.proto | 13 +- abci/types/typespb_test.go | 124 +++++ blockchain/reactor_test.go | 2 +- docs/spec/abci/abci.md | 54 +- libs/pubsub/example_test.go | 2 +- libs/pubsub/pubsub.go | 37 +- libs/pubsub/pubsub_test.go | 65 ++- libs/pubsub/query/empty.go | 2 +- libs/pubsub/query/empty_test.go | 8 +- libs/pubsub/query/query.go | 72 ++- libs/pubsub/query/query_test.go | 50 +- libs/pubsub/subscription.go | 14 +- rpc/client/localclient.go | 2 +- rpc/core/events.go | 89 ++- rpc/core/types/responses.go | 6 +- state/execution_test.go | 3 +- state/state_test.go | 16 +- state/txindex/kv/kv.go | 59 +- state/txindex/kv/kv_test.go | 144 ++++- types/event_bus.go | 75 ++- types/event_bus_test.go | 137 ++++- 25 files changed, 1405 insertions(+), 551 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 45ed56919..c23dd76b9 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -10,14 +10,18 @@ - read more on Modules here: https://github.com/golang/go/wiki/Modules * CLI/RPC/Config -- [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` -> + * [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` -> `results.deliver_tx`). See docs for details. * Apps + * [abci] \#1859 `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`, + and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event` + contains a `type` and a list of `attributes` (list of key-value pairs) allowing + for inclusion of multiple distinct events in each response. * Go API -- [libs/db] Removed deprecated `LevelDBBackend` const - * If you have `db_backend` set to `leveldb` in your config file, please + * [libs/db] Removed deprecated `LevelDBBackend` const + * If you have `db_backend` set to `leveldb` in your config file, please change it to `goleveldb` or `cleveldb`. - [p2p] \#3521 Remove NewNetAddressStringWithOptionalID diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 955baefb4..0c28813f2 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -84,14 +84,21 @@ func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { } else { key, value = tx, tx } + app.state.db.Set(prefixKey(key), value) app.state.Size += 1 - tags := []cmn.KVPair{ - {Key: []byte("app.creator"), Value: []byte("Cosmoshi Netowoko")}, - {Key: []byte("app.key"), Value: key}, + events := []types.Event{ + { + Type: "app", + Attributes: []cmn.KVPair{ + {Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")}, + {Key: []byte("key"), Value: key}, + }, + }, } - return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags} + + return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events} } func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx { diff --git a/abci/types/messages_test.go b/abci/types/messages_test.go index 762111b61..904b16410 100644 --- a/abci/types/messages_test.go +++ b/abci/types/messages_test.go @@ -8,6 +8,7 @@ import ( "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" + cmn "github.com/tendermint/tendermint/libs/common" ) @@ -21,8 +22,13 @@ func TestMarshalJSON(t *testing.T) { Code: 1, Data: []byte("hello"), GasWanted: 43, - Tags: []cmn.KVPair{ - {Key: []byte("pho"), Value: []byte("bo")}, + Events: []Event{ + { + Type: "testEvent", + Attributes: []cmn.KVPair{ + {Key: []byte("pho"), Value: []byte("bo")}, + }, + }, }, } b, err = json.Marshal(&r1) @@ -82,8 +88,13 @@ func TestWriteReadMessage2(t *testing.T) { Data: []byte(phrase), Log: phrase, GasWanted: 10, - Tags: []cmn.KVPair{ - {Key: []byte("abc"), Value: []byte("def")}, + Events: []Event{ + { + Type: "testEvent", + Attributes: []cmn.KVPair{ + {Key: []byte("abc"), Value: []byte("def")}, + }, + }, }, }, // TODO: add the rest diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 7bf50f330..a7455b523 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -61,7 +61,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{0} + return fileDescriptor_types_62f0c59aeb977f78, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -483,7 +483,7 @@ func (m *RequestEcho) Reset() { *m = RequestEcho{} } func (m *RequestEcho) String() string { return proto.CompactTextString(m) } func (*RequestEcho) ProtoMessage() {} func (*RequestEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{1} + return fileDescriptor_types_62f0c59aeb977f78, []int{1} } func (m *RequestEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -529,7 +529,7 @@ func (m *RequestFlush) Reset() { *m = RequestFlush{} } func (m *RequestFlush) String() string { return proto.CompactTextString(m) } func (*RequestFlush) ProtoMessage() {} func (*RequestFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{2} + return fileDescriptor_types_62f0c59aeb977f78, []int{2} } func (m *RequestFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -571,7 +571,7 @@ func (m *RequestInfo) Reset() { *m = RequestInfo{} } func (m *RequestInfo) String() string { return proto.CompactTextString(m) } func (*RequestInfo) ProtoMessage() {} func (*RequestInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{3} + return fileDescriptor_types_62f0c59aeb977f78, []int{3} } func (m *RequestInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,7 +634,7 @@ func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } func (*RequestSetOption) ProtoMessage() {} func (*RequestSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{4} + return fileDescriptor_types_62f0c59aeb977f78, []int{4} } func (m *RequestSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -692,7 +692,7 @@ func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } func (*RequestInitChain) ProtoMessage() {} func (*RequestInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{5} + return fileDescriptor_types_62f0c59aeb977f78, []int{5} } func (m *RequestInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -770,7 +770,7 @@ func (m *RequestQuery) Reset() { *m = RequestQuery{} } func (m *RequestQuery) String() string { return proto.CompactTextString(m) } func (*RequestQuery) ProtoMessage() {} func (*RequestQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{6} + return fileDescriptor_types_62f0c59aeb977f78, []int{6} } func (m *RequestQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -841,7 +841,7 @@ func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } func (*RequestBeginBlock) ProtoMessage() {} func (*RequestBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{7} + return fileDescriptor_types_62f0c59aeb977f78, []int{7} } func (m *RequestBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -909,7 +909,7 @@ func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } func (*RequestCheckTx) ProtoMessage() {} func (*RequestCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{8} + return fileDescriptor_types_62f0c59aeb977f78, []int{8} } func (m *RequestCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -956,7 +956,7 @@ func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } func (*RequestDeliverTx) ProtoMessage() {} func (*RequestDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{9} + return fileDescriptor_types_62f0c59aeb977f78, []int{9} } func (m *RequestDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1003,7 @@ func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } func (*RequestEndBlock) ProtoMessage() {} func (*RequestEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{10} + return fileDescriptor_types_62f0c59aeb977f78, []int{10} } func (m *RequestEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1049,7 @@ func (m *RequestCommit) Reset() { *m = RequestCommit{} } func (m *RequestCommit) String() string { return proto.CompactTextString(m) } func (*RequestCommit) ProtoMessage() {} func (*RequestCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{11} + return fileDescriptor_types_62f0c59aeb977f78, []int{11} } func (m *RequestCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1102,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{12} + return fileDescriptor_types_62f0c59aeb977f78, []int{12} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1555,7 +1555,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{13} + return fileDescriptor_types_62f0c59aeb977f78, []int{13} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1602,7 +1602,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{14} + return fileDescriptor_types_62f0c59aeb977f78, []int{14} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1648,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{15} + return fileDescriptor_types_62f0c59aeb977f78, []int{15} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1692,7 +1692,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{16} + return fileDescriptor_types_62f0c59aeb977f78, []int{16} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1771,7 +1771,7 @@ func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } func (*ResponseSetOption) ProtoMessage() {} func (*ResponseSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{17} + return fileDescriptor_types_62f0c59aeb977f78, []int{17} } func (m *ResponseSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1833,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{18} + return fileDescriptor_types_62f0c59aeb977f78, []int{18} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1896,7 +1896,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{19} + return fileDescriptor_types_62f0c59aeb977f78, []int{19} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1989,17 +1989,17 @@ func (m *ResponseQuery) GetCodespace() string { } type ResponseBeginBlock struct { - Tags []common.KVPair `protobuf:"bytes,1,rep,name=tags" json:"tags,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Events []Event `protobuf:"bytes,1,rep,name=events" json:"events,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } func (*ResponseBeginBlock) ProtoMessage() {} func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{20} + return fileDescriptor_types_62f0c59aeb977f78, []int{20} } func (m *ResponseBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2028,32 +2028,32 @@ func (m *ResponseBeginBlock) XXX_DiscardUnknown() { var xxx_messageInfo_ResponseBeginBlock proto.InternalMessageInfo -func (m *ResponseBeginBlock) GetTags() []common.KVPair { +func (m *ResponseBeginBlock) GetEvents() []Event { if m != nil { - return m.Tags + return m.Events } return nil } type ResponseCheckTx struct { - Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` - Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` - GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` - GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` - Tags []common.KVPair `protobuf:"bytes,7,rep,name=tags" json:"tags,omitempty"` - Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` + GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` + GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + Events []Event `protobuf:"bytes,7,rep,name=events" json:"events,omitempty"` + Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{21} + return fileDescriptor_types_62f0c59aeb977f78, []int{21} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2124,9 +2124,9 @@ func (m *ResponseCheckTx) GetGasUsed() int64 { return 0 } -func (m *ResponseCheckTx) GetTags() []common.KVPair { +func (m *ResponseCheckTx) GetEvents() []Event { if m != nil { - return m.Tags + return m.Events } return nil } @@ -2139,24 +2139,24 @@ func (m *ResponseCheckTx) GetCodespace() string { } type ResponseDeliverTx struct { - Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` - Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` - GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` - GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` - Tags []common.KVPair `protobuf:"bytes,7,rep,name=tags" json:"tags,omitempty"` - Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Code uint32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Log string `protobuf:"bytes,3,opt,name=log,proto3" json:"log,omitempty"` + Info string `protobuf:"bytes,4,opt,name=info,proto3" json:"info,omitempty"` + GasWanted int64 `protobuf:"varint,5,opt,name=gas_wanted,json=gasWanted,proto3" json:"gas_wanted,omitempty"` + GasUsed int64 `protobuf:"varint,6,opt,name=gas_used,json=gasUsed,proto3" json:"gas_used,omitempty"` + Events []Event `protobuf:"bytes,7,rep,name=events" json:"events,omitempty"` + Codespace string `protobuf:"bytes,8,opt,name=codespace,proto3" json:"codespace,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } func (*ResponseDeliverTx) ProtoMessage() {} func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{22} + return fileDescriptor_types_62f0c59aeb977f78, []int{22} } func (m *ResponseDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2227,9 +2227,9 @@ func (m *ResponseDeliverTx) GetGasUsed() int64 { return 0 } -func (m *ResponseDeliverTx) GetTags() []common.KVPair { +func (m *ResponseDeliverTx) GetEvents() []Event { if m != nil { - return m.Tags + return m.Events } return nil } @@ -2244,7 +2244,7 @@ func (m *ResponseDeliverTx) GetCodespace() string { type ResponseEndBlock struct { ValidatorUpdates []ValidatorUpdate `protobuf:"bytes,1,rep,name=validator_updates,json=validatorUpdates" json:"validator_updates"` ConsensusParamUpdates *ConsensusParams `protobuf:"bytes,2,opt,name=consensus_param_updates,json=consensusParamUpdates" json:"consensus_param_updates,omitempty"` - Tags []common.KVPair `protobuf:"bytes,3,rep,name=tags" json:"tags,omitempty"` + Events []Event `protobuf:"bytes,3,rep,name=events" json:"events,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -2254,7 +2254,7 @@ func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } func (*ResponseEndBlock) ProtoMessage() {} func (*ResponseEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{23} + return fileDescriptor_types_62f0c59aeb977f78, []int{23} } func (m *ResponseEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2297,9 +2297,9 @@ func (m *ResponseEndBlock) GetConsensusParamUpdates() *ConsensusParams { return nil } -func (m *ResponseEndBlock) GetTags() []common.KVPair { +func (m *ResponseEndBlock) GetEvents() []Event { if m != nil { - return m.Tags + return m.Events } return nil } @@ -2316,7 +2316,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{24} + return fileDescriptor_types_62f0c59aeb977f78, []int{24} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2367,7 +2367,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } func (*ConsensusParams) ProtoMessage() {} func (*ConsensusParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{25} + return fileDescriptor_types_62f0c59aeb977f78, []int{25} } func (m *ConsensusParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2417,7 +2417,7 @@ func (m *ConsensusParams) GetValidator() *ValidatorParams { return nil } -// BlockParams contains limits on the block size. +// BlockParams contains limits on the block size and timestamp. type BlockParams struct { // Note: must be greater than 0 MaxBytes int64 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` @@ -2432,7 +2432,7 @@ func (m *BlockParams) Reset() { *m = BlockParams{} } func (m *BlockParams) String() string { return proto.CompactTextString(m) } func (*BlockParams) ProtoMessage() {} func (*BlockParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{26} + return fileDescriptor_types_62f0c59aeb977f78, []int{26} } func (m *BlockParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2488,7 +2488,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } func (m *EvidenceParams) String() string { return proto.CompactTextString(m) } func (*EvidenceParams) ProtoMessage() {} func (*EvidenceParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{27} + return fileDescriptor_types_62f0c59aeb977f78, []int{27} } func (m *EvidenceParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2536,7 +2536,7 @@ func (m *ValidatorParams) Reset() { *m = ValidatorParams{} } func (m *ValidatorParams) String() string { return proto.CompactTextString(m) } func (*ValidatorParams) ProtoMessage() {} func (*ValidatorParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{28} + return fileDescriptor_types_62f0c59aeb977f78, []int{28} } func (m *ValidatorParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2584,7 +2584,7 @@ func (m *LastCommitInfo) Reset() { *m = LastCommitInfo{} } func (m *LastCommitInfo) String() string { return proto.CompactTextString(m) } func (*LastCommitInfo) ProtoMessage() {} func (*LastCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{29} + return fileDescriptor_types_62f0c59aeb977f78, []int{29} } func (m *LastCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2627,6 +2627,61 @@ func (m *LastCommitInfo) GetVotes() []VoteInfo { return nil } +type Event struct { + Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"` + Attributes []common.KVPair `protobuf:"bytes,2,rep,name=attributes" json:"attributes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Event) Reset() { *m = Event{} } +func (m *Event) String() string { return proto.CompactTextString(m) } +func (*Event) ProtoMessage() {} +func (*Event) Descriptor() ([]byte, []int) { + return fileDescriptor_types_62f0c59aeb977f78, []int{30} +} +func (m *Event) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Event) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Event.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalTo(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (dst *Event) XXX_Merge(src proto.Message) { + xxx_messageInfo_Event.Merge(dst, src) +} +func (m *Event) XXX_Size() int { + return m.Size() +} +func (m *Event) XXX_DiscardUnknown() { + xxx_messageInfo_Event.DiscardUnknown(m) +} + +var xxx_messageInfo_Event proto.InternalMessageInfo + +func (m *Event) GetType() string { + if m != nil { + return m.Type + } + return "" +} + +func (m *Event) GetAttributes() []common.KVPair { + if m != nil { + return m.Attributes + } + return nil +} + type Header struct { // basic block info Version Version `protobuf:"bytes,1,opt,name=version" json:"version"` @@ -2658,7 +2713,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{30} + return fileDescriptor_types_62f0c59aeb977f78, []int{31} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2811,7 +2866,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{31} + return fileDescriptor_types_62f0c59aeb977f78, []int{32} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2866,7 +2921,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{32} + return fileDescriptor_types_62f0c59aeb977f78, []int{33} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2921,7 +2976,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{33} + return fileDescriptor_types_62f0c59aeb977f78, []int{34} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2978,7 +3033,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{34} + return fileDescriptor_types_62f0c59aeb977f78, []int{35} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3034,7 +3089,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{35} + return fileDescriptor_types_62f0c59aeb977f78, []int{36} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3090,7 +3145,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{36} + return fileDescriptor_types_62f0c59aeb977f78, []int{37} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3145,7 +3200,7 @@ func (m *PubKey) Reset() { *m = PubKey{} } func (m *PubKey) String() string { return proto.CompactTextString(m) } func (*PubKey) ProtoMessage() {} func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{37} + return fileDescriptor_types_62f0c59aeb977f78, []int{38} } func (m *PubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3203,7 +3258,7 @@ func (m *Evidence) Reset() { *m = Evidence{} } func (m *Evidence) String() string { return proto.CompactTextString(m) } func (*Evidence) ProtoMessage() {} func (*Evidence) Descriptor() ([]byte, []int) { - return fileDescriptor_types_7e896a7c04915591, []int{38} + return fileDescriptor_types_62f0c59aeb977f78, []int{39} } func (m *Evidence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3328,6 +3383,8 @@ func init() { golang_proto.RegisterType((*ValidatorParams)(nil), "types.ValidatorParams") proto.RegisterType((*LastCommitInfo)(nil), "types.LastCommitInfo") golang_proto.RegisterType((*LastCommitInfo)(nil), "types.LastCommitInfo") + proto.RegisterType((*Event)(nil), "types.Event") + golang_proto.RegisterType((*Event)(nil), "types.Event") proto.RegisterType((*Header)(nil), "types.Header") golang_proto.RegisterType((*Header)(nil), "types.Header") proto.RegisterType((*Version)(nil), "types.Version") @@ -4560,11 +4617,11 @@ func (this *ResponseBeginBlock) Equal(that interface{}) bool { } else if this == nil { return false } - if len(this.Tags) != len(that1.Tags) { + if len(this.Events) != len(that1.Events) { return false } - for i := range this.Tags { - if !this.Tags[i].Equal(&that1.Tags[i]) { + for i := range this.Events { + if !this.Events[i].Equal(&that1.Events[i]) { return false } } @@ -4610,11 +4667,11 @@ func (this *ResponseCheckTx) Equal(that interface{}) bool { if this.GasUsed != that1.GasUsed { return false } - if len(this.Tags) != len(that1.Tags) { + if len(this.Events) != len(that1.Events) { return false } - for i := range this.Tags { - if !this.Tags[i].Equal(&that1.Tags[i]) { + for i := range this.Events { + if !this.Events[i].Equal(&that1.Events[i]) { return false } } @@ -4663,11 +4720,11 @@ func (this *ResponseDeliverTx) Equal(that interface{}) bool { if this.GasUsed != that1.GasUsed { return false } - if len(this.Tags) != len(that1.Tags) { + if len(this.Events) != len(that1.Events) { return false } - for i := range this.Tags { - if !this.Tags[i].Equal(&that1.Tags[i]) { + for i := range this.Events { + if !this.Events[i].Equal(&that1.Events[i]) { return false } } @@ -4709,11 +4766,11 @@ func (this *ResponseEndBlock) Equal(that interface{}) bool { if !this.ConsensusParamUpdates.Equal(that1.ConsensusParamUpdates) { return false } - if len(this.Tags) != len(that1.Tags) { + if len(this.Events) != len(that1.Events) { return false } - for i := range this.Tags { - if !this.Tags[i].Equal(&that1.Tags[i]) { + for i := range this.Events { + if !this.Events[i].Equal(&that1.Events[i]) { return false } } @@ -4906,6 +4963,41 @@ func (this *LastCommitInfo) Equal(that interface{}) bool { } return true } +func (this *Event) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*Event) + if !ok { + that2, ok := that.(Event) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if this.Type != that1.Type { + return false + } + if len(this.Attributes) != len(that1.Attributes) { + return false + } + for i := range this.Attributes { + if !this.Attributes[i].Equal(&that1.Attributes[i]) { + return false + } + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} func (this *Header) Equal(that interface{}) bool { if that == nil { return this == nil @@ -6691,8 +6783,8 @@ func (m *ResponseBeginBlock) MarshalTo(dAtA []byte) (int, error) { _ = i var l int _ = l - if len(m.Tags) > 0 { - for _, msg := range m.Tags { + if len(m.Events) > 0 { + for _, msg := range m.Events { dAtA[i] = 0xa i++ i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) @@ -6757,8 +6849,8 @@ func (m *ResponseCheckTx) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintTypes(dAtA, i, uint64(m.GasUsed)) } - if len(m.Tags) > 0 { - for _, msg := range m.Tags { + if len(m.Events) > 0 { + for _, msg := range m.Events { dAtA[i] = 0x3a i++ i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) @@ -6829,8 +6921,8 @@ func (m *ResponseDeliverTx) MarshalTo(dAtA []byte) (int, error) { i++ i = encodeVarintTypes(dAtA, i, uint64(m.GasUsed)) } - if len(m.Tags) > 0 { - for _, msg := range m.Tags { + if len(m.Events) > 0 { + for _, msg := range m.Events { dAtA[i] = 0x3a i++ i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) @@ -6890,8 +6982,8 @@ func (m *ResponseEndBlock) MarshalTo(dAtA []byte) (int, error) { } i += n32 } - if len(m.Tags) > 0 { - for _, msg := range m.Tags { + if len(m.Events) > 0 { + for _, msg := range m.Events { dAtA[i] = 0x1a i++ i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) @@ -7117,6 +7209,45 @@ func (m *LastCommitInfo) MarshalTo(dAtA []byte) (int, error) { return i, nil } +func (m *Event) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalTo(dAtA) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Event) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if len(m.Type) > 0 { + dAtA[i] = 0xa + i++ + i = encodeVarintTypes(dAtA, i, uint64(len(m.Type))) + i += copy(dAtA[i:], m.Type) + } + if len(m.Attributes) > 0 { + for _, msg := range m.Attributes { + dAtA[i] = 0x12 + i++ + i = encodeVarintTypes(dAtA, i, uint64(msg.Size())) + n, err := msg.MarshalTo(dAtA[i:]) + if err != nil { + return 0, err + } + i += n + } + } + if m.XXX_unrecognized != nil { + i += copy(dAtA[i:], m.XXX_unrecognized) + } + return i, nil +} + func (m *Header) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -7988,10 +8119,10 @@ func NewPopulatedResponseBeginBlock(r randyTypes, easy bool) *ResponseBeginBlock this := &ResponseBeginBlock{} if r.Intn(10) != 0 { v18 := r.Intn(5) - this.Tags = make([]common.KVPair, v18) + this.Events = make([]Event, v18) for i := 0; i < v18; i++ { - v19 := common.NewPopulatedKVPair(r, easy) - this.Tags[i] = *v19 + v19 := NewPopulatedEvent(r, easy) + this.Events[i] = *v19 } } if !easy && r.Intn(10) != 0 { @@ -8020,10 +8151,10 @@ func NewPopulatedResponseCheckTx(r randyTypes, easy bool) *ResponseCheckTx { } if r.Intn(10) != 0 { v21 := r.Intn(5) - this.Tags = make([]common.KVPair, v21) + this.Events = make([]Event, v21) for i := 0; i < v21; i++ { - v22 := common.NewPopulatedKVPair(r, easy) - this.Tags[i] = *v22 + v22 := NewPopulatedEvent(r, easy) + this.Events[i] = *v22 } } this.Codespace = string(randStringTypes(r)) @@ -8053,10 +8184,10 @@ func NewPopulatedResponseDeliverTx(r randyTypes, easy bool) *ResponseDeliverTx { } if r.Intn(10) != 0 { v24 := r.Intn(5) - this.Tags = make([]common.KVPair, v24) + this.Events = make([]Event, v24) for i := 0; i < v24; i++ { - v25 := common.NewPopulatedKVPair(r, easy) - this.Tags[i] = *v25 + v25 := NewPopulatedEvent(r, easy) + this.Events[i] = *v25 } } this.Codespace = string(randStringTypes(r)) @@ -8081,10 +8212,10 @@ func NewPopulatedResponseEndBlock(r randyTypes, easy bool) *ResponseEndBlock { } if r.Intn(10) != 0 { v28 := r.Intn(5) - this.Tags = make([]common.KVPair, v28) + this.Events = make([]Event, v28) for i := 0; i < v28; i++ { - v29 := common.NewPopulatedKVPair(r, easy) - this.Tags[i] = *v29 + v29 := NewPopulatedEvent(r, easy) + this.Events[i] = *v29 } } if !easy && r.Intn(10) != 0 { @@ -8184,17 +8315,34 @@ func NewPopulatedLastCommitInfo(r randyTypes, easy bool) *LastCommitInfo { return this } +func NewPopulatedEvent(r randyTypes, easy bool) *Event { + this := &Event{} + this.Type = string(randStringTypes(r)) + if r.Intn(10) != 0 { + v34 := r.Intn(5) + this.Attributes = make([]common.KVPair, v34) + for i := 0; i < v34; i++ { + v35 := common.NewPopulatedKVPair(r, easy) + this.Attributes[i] = *v35 + } + } + if !easy && r.Intn(10) != 0 { + this.XXX_unrecognized = randUnrecognizedTypes(r, 3) + } + return this +} + func NewPopulatedHeader(r randyTypes, easy bool) *Header { this := &Header{} - v34 := NewPopulatedVersion(r, easy) - this.Version = *v34 + v36 := NewPopulatedVersion(r, easy) + this.Version = *v36 this.ChainID = string(randStringTypes(r)) this.Height = int64(r.Int63()) if r.Intn(2) == 0 { this.Height *= -1 } - v35 := github_com_gogo_protobuf_types.NewPopulatedStdTime(r, easy) - this.Time = *v35 + v37 := github_com_gogo_protobuf_types.NewPopulatedStdTime(r, easy) + this.Time = *v37 this.NumTxs = int64(r.Int63()) if r.Intn(2) == 0 { this.NumTxs *= -1 @@ -8203,51 +8351,51 @@ func NewPopulatedHeader(r randyTypes, easy bool) *Header { if r.Intn(2) == 0 { this.TotalTxs *= -1 } - v36 := NewPopulatedBlockID(r, easy) - this.LastBlockId = *v36 - v37 := r.Intn(100) - this.LastCommitHash = make([]byte, v37) - for i := 0; i < v37; i++ { + v38 := NewPopulatedBlockID(r, easy) + this.LastBlockId = *v38 + v39 := r.Intn(100) + this.LastCommitHash = make([]byte, v39) + for i := 0; i < v39; i++ { this.LastCommitHash[i] = byte(r.Intn(256)) } - v38 := r.Intn(100) - this.DataHash = make([]byte, v38) - for i := 0; i < v38; i++ { + v40 := r.Intn(100) + this.DataHash = make([]byte, v40) + for i := 0; i < v40; i++ { this.DataHash[i] = byte(r.Intn(256)) } - v39 := r.Intn(100) - this.ValidatorsHash = make([]byte, v39) - for i := 0; i < v39; i++ { + v41 := r.Intn(100) + this.ValidatorsHash = make([]byte, v41) + for i := 0; i < v41; i++ { this.ValidatorsHash[i] = byte(r.Intn(256)) } - v40 := r.Intn(100) - this.NextValidatorsHash = make([]byte, v40) - for i := 0; i < v40; i++ { + v42 := r.Intn(100) + this.NextValidatorsHash = make([]byte, v42) + for i := 0; i < v42; i++ { this.NextValidatorsHash[i] = byte(r.Intn(256)) } - v41 := r.Intn(100) - this.ConsensusHash = make([]byte, v41) - for i := 0; i < v41; i++ { + v43 := r.Intn(100) + this.ConsensusHash = make([]byte, v43) + for i := 0; i < v43; i++ { this.ConsensusHash[i] = byte(r.Intn(256)) } - v42 := r.Intn(100) - this.AppHash = make([]byte, v42) - for i := 0; i < v42; i++ { + v44 := r.Intn(100) + this.AppHash = make([]byte, v44) + for i := 0; i < v44; i++ { this.AppHash[i] = byte(r.Intn(256)) } - v43 := r.Intn(100) - this.LastResultsHash = make([]byte, v43) - for i := 0; i < v43; i++ { + v45 := r.Intn(100) + this.LastResultsHash = make([]byte, v45) + for i := 0; i < v45; i++ { this.LastResultsHash[i] = byte(r.Intn(256)) } - v44 := r.Intn(100) - this.EvidenceHash = make([]byte, v44) - for i := 0; i < v44; i++ { + v46 := r.Intn(100) + this.EvidenceHash = make([]byte, v46) + for i := 0; i < v46; i++ { this.EvidenceHash[i] = byte(r.Intn(256)) } - v45 := r.Intn(100) - this.ProposerAddress = make([]byte, v45) - for i := 0; i < v45; i++ { + v47 := r.Intn(100) + this.ProposerAddress = make([]byte, v47) + for i := 0; i < v47; i++ { this.ProposerAddress[i] = byte(r.Intn(256)) } if !easy && r.Intn(10) != 0 { @@ -8268,13 +8416,13 @@ func NewPopulatedVersion(r randyTypes, easy bool) *Version { func NewPopulatedBlockID(r randyTypes, easy bool) *BlockID { this := &BlockID{} - v46 := r.Intn(100) - this.Hash = make([]byte, v46) - for i := 0; i < v46; i++ { + v48 := r.Intn(100) + this.Hash = make([]byte, v48) + for i := 0; i < v48; i++ { this.Hash[i] = byte(r.Intn(256)) } - v47 := NewPopulatedPartSetHeader(r, easy) - this.PartsHeader = *v47 + v49 := NewPopulatedPartSetHeader(r, easy) + this.PartsHeader = *v49 if !easy && r.Intn(10) != 0 { this.XXX_unrecognized = randUnrecognizedTypes(r, 3) } @@ -8287,9 +8435,9 @@ func NewPopulatedPartSetHeader(r randyTypes, easy bool) *PartSetHeader { if r.Intn(2) == 0 { this.Total *= -1 } - v48 := r.Intn(100) - this.Hash = make([]byte, v48) - for i := 0; i < v48; i++ { + v50 := r.Intn(100) + this.Hash = make([]byte, v50) + for i := 0; i < v50; i++ { this.Hash[i] = byte(r.Intn(256)) } if !easy && r.Intn(10) != 0 { @@ -8300,9 +8448,9 @@ func NewPopulatedPartSetHeader(r randyTypes, easy bool) *PartSetHeader { func NewPopulatedValidator(r randyTypes, easy bool) *Validator { this := &Validator{} - v49 := r.Intn(100) - this.Address = make([]byte, v49) - for i := 0; i < v49; i++ { + v51 := r.Intn(100) + this.Address = make([]byte, v51) + for i := 0; i < v51; i++ { this.Address[i] = byte(r.Intn(256)) } this.Power = int64(r.Int63()) @@ -8317,8 +8465,8 @@ func NewPopulatedValidator(r randyTypes, easy bool) *Validator { func NewPopulatedValidatorUpdate(r randyTypes, easy bool) *ValidatorUpdate { this := &ValidatorUpdate{} - v50 := NewPopulatedPubKey(r, easy) - this.PubKey = *v50 + v52 := NewPopulatedPubKey(r, easy) + this.PubKey = *v52 this.Power = int64(r.Int63()) if r.Intn(2) == 0 { this.Power *= -1 @@ -8331,8 +8479,8 @@ func NewPopulatedValidatorUpdate(r randyTypes, easy bool) *ValidatorUpdate { func NewPopulatedVoteInfo(r randyTypes, easy bool) *VoteInfo { this := &VoteInfo{} - v51 := NewPopulatedValidator(r, easy) - this.Validator = *v51 + v53 := NewPopulatedValidator(r, easy) + this.Validator = *v53 this.SignedLastBlock = bool(bool(r.Intn(2) == 0)) if !easy && r.Intn(10) != 0 { this.XXX_unrecognized = randUnrecognizedTypes(r, 3) @@ -8343,9 +8491,9 @@ func NewPopulatedVoteInfo(r randyTypes, easy bool) *VoteInfo { func NewPopulatedPubKey(r randyTypes, easy bool) *PubKey { this := &PubKey{} this.Type = string(randStringTypes(r)) - v52 := r.Intn(100) - this.Data = make([]byte, v52) - for i := 0; i < v52; i++ { + v54 := r.Intn(100) + this.Data = make([]byte, v54) + for i := 0; i < v54; i++ { this.Data[i] = byte(r.Intn(256)) } if !easy && r.Intn(10) != 0 { @@ -8357,14 +8505,14 @@ func NewPopulatedPubKey(r randyTypes, easy bool) *PubKey { func NewPopulatedEvidence(r randyTypes, easy bool) *Evidence { this := &Evidence{} this.Type = string(randStringTypes(r)) - v53 := NewPopulatedValidator(r, easy) - this.Validator = *v53 + v55 := NewPopulatedValidator(r, easy) + this.Validator = *v55 this.Height = int64(r.Int63()) if r.Intn(2) == 0 { this.Height *= -1 } - v54 := github_com_gogo_protobuf_types.NewPopulatedStdTime(r, easy) - this.Time = *v54 + v56 := github_com_gogo_protobuf_types.NewPopulatedStdTime(r, easy) + this.Time = *v56 this.TotalVotingPower = int64(r.Int63()) if r.Intn(2) == 0 { this.TotalVotingPower *= -1 @@ -8394,9 +8542,9 @@ func randUTF8RuneTypes(r randyTypes) rune { return rune(ru + 61) } func randStringTypes(r randyTypes) string { - v55 := r.Intn(100) - tmps := make([]rune, v55) - for i := 0; i < v55; i++ { + v57 := r.Intn(100) + tmps := make([]rune, v57) + for i := 0; i < v57; i++ { tmps[i] = randUTF8RuneTypes(r) } return string(tmps) @@ -8418,11 +8566,11 @@ func randFieldTypes(dAtA []byte, r randyTypes, fieldNumber int, wire int) []byte switch wire { case 0: dAtA = encodeVarintPopulateTypes(dAtA, uint64(key)) - v56 := r.Int63() + v58 := r.Int63() if r.Intn(2) == 0 { - v56 *= -1 + v58 *= -1 } - dAtA = encodeVarintPopulateTypes(dAtA, uint64(v56)) + dAtA = encodeVarintPopulateTypes(dAtA, uint64(v58)) case 1: dAtA = encodeVarintPopulateTypes(dAtA, uint64(key)) dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256))) @@ -9136,8 +9284,8 @@ func (m *ResponseBeginBlock) Size() (n int) { } var l int _ = l - if len(m.Tags) > 0 { - for _, e := range m.Tags { + if len(m.Events) > 0 { + for _, e := range m.Events { l = e.Size() n += 1 + l + sovTypes(uint64(l)) } @@ -9175,8 +9323,8 @@ func (m *ResponseCheckTx) Size() (n int) { if m.GasUsed != 0 { n += 1 + sovTypes(uint64(m.GasUsed)) } - if len(m.Tags) > 0 { - for _, e := range m.Tags { + if len(m.Events) > 0 { + for _, e := range m.Events { l = e.Size() n += 1 + l + sovTypes(uint64(l)) } @@ -9218,8 +9366,8 @@ func (m *ResponseDeliverTx) Size() (n int) { if m.GasUsed != 0 { n += 1 + sovTypes(uint64(m.GasUsed)) } - if len(m.Tags) > 0 { - for _, e := range m.Tags { + if len(m.Events) > 0 { + for _, e := range m.Events { l = e.Size() n += 1 + l + sovTypes(uint64(l)) } @@ -9250,8 +9398,8 @@ func (m *ResponseEndBlock) Size() (n int) { l = m.ConsensusParamUpdates.Size() n += 1 + l + sovTypes(uint64(l)) } - if len(m.Tags) > 0 { - for _, e := range m.Tags { + if len(m.Events) > 0 { + for _, e := range m.Events { l = e.Size() n += 1 + l + sovTypes(uint64(l)) } @@ -9374,6 +9522,28 @@ func (m *LastCommitInfo) Size() (n int) { return n } +func (m *Event) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Type) + if l > 0 { + n += 1 + l + sovTypes(uint64(l)) + } + if len(m.Attributes) > 0 { + for _, e := range m.Attributes { + l = e.Size() + n += 1 + l + sovTypes(uint64(l)) + } + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + func (m *Header) Size() (n int) { if m == nil { return 0 @@ -12570,7 +12740,7 @@ func (m *ResponseBeginBlock) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -12594,8 +12764,8 @@ func (m *ResponseBeginBlock) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Tags = append(m.Tags, common.KVPair{}) - if err := m.Tags[len(m.Tags)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Events = append(m.Events, Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -12798,7 +12968,7 @@ func (m *ResponseCheckTx) Unmarshal(dAtA []byte) error { } case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -12822,8 +12992,8 @@ func (m *ResponseCheckTx) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Tags = append(m.Tags, common.KVPair{}) - if err := m.Tags[len(m.Tags)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Events = append(m.Events, Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13055,7 +13225,7 @@ func (m *ResponseDeliverTx) Unmarshal(dAtA []byte) error { } case 7: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13079,8 +13249,8 @@ func (m *ResponseDeliverTx) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Tags = append(m.Tags, common.KVPair{}) - if err := m.Tags[len(m.Tags)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Events = append(m.Events, Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13230,7 +13400,7 @@ func (m *ResponseEndBlock) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Tags", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Events", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -13254,8 +13424,8 @@ func (m *ResponseEndBlock) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Tags = append(m.Tags, common.KVPair{}) - if err := m.Tags[len(m.Tags)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + m.Events = append(m.Events, Event{}) + if err := m.Events[len(m.Events)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -13853,6 +14023,117 @@ func (m *LastCommitInfo) Unmarshal(dAtA []byte) error { } return nil } +func (m *Event) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Event: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Event: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Type = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Attributes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTypes + } + postIndex := iNdEx + msglen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Attributes = append(m.Attributes, common.KVPair{}) + if err := m.Attributes[len(m.Attributes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTypes(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthTypes + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *Header) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -15357,149 +15638,152 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_7e896a7c04915591) } +func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_62f0c59aeb977f78) } func init() { - golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_7e896a7c04915591) + golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_62f0c59aeb977f78) } -var fileDescriptor_types_7e896a7c04915591 = []byte{ - // 2203 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcf, 0x73, 0x1c, 0x47, - 0xf5, 0xd7, 0xec, 0xef, 0x79, 0xab, 0xfd, 0xe1, 0xb6, 0x6c, 0xaf, 0xf7, 0x9b, 0xaf, 0xe4, 0x1a, - 0x43, 0x22, 0x11, 0x67, 0x95, 0x28, 0x98, 0x92, 0xe3, 0x40, 0x95, 0x56, 0x36, 0x48, 0x95, 0x00, - 0x62, 0x6c, 0x8b, 0x0b, 0x55, 0x53, 0xbd, 0x3b, 0xad, 0xdd, 0x29, 0xed, 0xce, 0x4c, 0x66, 0x7a, - 0x95, 0x15, 0x47, 0xce, 0x39, 0xe4, 0xc0, 0x9f, 0xc0, 0x81, 0x3f, 0x21, 0x47, 0x4e, 0x54, 0x8e, - 0x1c, 0x38, 0x1b, 0x10, 0xc5, 0x01, 0xae, 0x14, 0x55, 0x1c, 0xa9, 0x7e, 0xdd, 0xf3, 0x53, 0xb3, - 0x26, 0x0e, 0x9c, 0xb8, 0x48, 0xd3, 0xfd, 0x3e, 0xaf, 0x7f, 0xbc, 0x7d, 0xef, 0x7d, 0xde, 0x6b, - 0xb8, 0x4d, 0x47, 0x63, 0x67, 0x97, 0x5f, 0xfa, 0x2c, 0x94, 0x7f, 0x07, 0x7e, 0xe0, 0x71, 0x8f, - 0x54, 0x71, 0xd0, 0x7f, 0x67, 0xe2, 0xf0, 0xe9, 0x62, 0x34, 0x18, 0x7b, 0xf3, 0xdd, 0x89, 0x37, - 0xf1, 0x76, 0x51, 0x3a, 0x5a, 0x9c, 0xe1, 0x08, 0x07, 0xf8, 0x25, 0xb5, 0xfa, 0x8f, 0x53, 0x70, - 0xce, 0x5c, 0x9b, 0x05, 0x73, 0xc7, 0xe5, 0xe9, 0xcf, 0x71, 0x70, 0xe9, 0x73, 0x6f, 0x77, 0xce, - 0x82, 0xf3, 0x19, 0x53, 0xff, 0x94, 0xf2, 0xfe, 0xbf, 0x55, 0x9e, 0x39, 0xa3, 0x70, 0x77, 0xec, - 0xcd, 0xe7, 0x9e, 0x9b, 0x3e, 0x6c, 0x7f, 0x6b, 0xe2, 0x79, 0x93, 0x19, 0x4b, 0x0e, 0xc7, 0x9d, - 0x39, 0x0b, 0x39, 0x9d, 0xfb, 0x12, 0x60, 0xfc, 0xb6, 0x02, 0x75, 0x93, 0x7d, 0xb2, 0x60, 0x21, - 0x27, 0xdb, 0x50, 0x61, 0xe3, 0xa9, 0xd7, 0x2b, 0xdd, 0xd3, 0xb6, 0x9b, 0x7b, 0x64, 0x20, 0x17, - 0x52, 0xd2, 0xa7, 0xe3, 0xa9, 0x77, 0xb4, 0x66, 0x22, 0x82, 0xbc, 0x0d, 0xd5, 0xb3, 0xd9, 0x22, - 0x9c, 0xf6, 0xca, 0x08, 0xbd, 0x99, 0x85, 0x7e, 0x5f, 0x88, 0x8e, 0xd6, 0x4c, 0x89, 0x11, 0xcb, - 0x3a, 0xee, 0x99, 0xd7, 0xab, 0x14, 0x2d, 0x7b, 0xec, 0x9e, 0xe1, 0xb2, 0x02, 0x41, 0xf6, 0x01, - 0x42, 0xc6, 0x2d, 0xcf, 0xe7, 0x8e, 0xe7, 0xf6, 0xaa, 0x88, 0xbf, 0x93, 0xc5, 0x3f, 0x63, 0xfc, - 0xc7, 0x28, 0x3e, 0x5a, 0x33, 0xf5, 0x30, 0x1a, 0x08, 0x4d, 0xc7, 0x75, 0xb8, 0x35, 0x9e, 0x52, - 0xc7, 0xed, 0xd5, 0x8a, 0x34, 0x8f, 0x5d, 0x87, 0x1f, 0x0a, 0xb1, 0xd0, 0x74, 0xa2, 0x81, 0xb8, - 0xca, 0x27, 0x0b, 0x16, 0x5c, 0xf6, 0xea, 0x45, 0x57, 0xf9, 0x89, 0x10, 0x89, 0xab, 0x20, 0x86, - 0x3c, 0x86, 0xe6, 0x88, 0x4d, 0x1c, 0xd7, 0x1a, 0xcd, 0xbc, 0xf1, 0x79, 0xaf, 0x81, 0x2a, 0xbd, - 0xac, 0xca, 0x50, 0x00, 0x86, 0x42, 0x7e, 0xb4, 0x66, 0xc2, 0x28, 0x1e, 0x91, 0x3d, 0x68, 0x8c, - 0xa7, 0x6c, 0x7c, 0x6e, 0xf1, 0x65, 0x4f, 0x47, 0xcd, 0x5b, 0x59, 0xcd, 0x43, 0x21, 0x7d, 0xbe, - 0x3c, 0x5a, 0x33, 0xeb, 0x63, 0xf9, 0x49, 0x1e, 0x82, 0xce, 0x5c, 0x5b, 0x6d, 0xd7, 0x44, 0xa5, - 0xdb, 0xb9, 0xdf, 0xc5, 0xb5, 0xa3, 0xcd, 0x1a, 0x4c, 0x7d, 0x93, 0x01, 0xd4, 0x84, 0x33, 0x38, - 0xbc, 0xb7, 0x8e, 0x3a, 0x1b, 0xb9, 0x8d, 0x50, 0x76, 0xb4, 0x66, 0x2a, 0x94, 0x30, 0x9f, 0xcd, - 0x66, 0xce, 0x05, 0x0b, 0xc4, 0xe1, 0x6e, 0x16, 0x99, 0xef, 0x89, 0x94, 0xe3, 0xf1, 0x74, 0x3b, - 0x1a, 0x0c, 0xeb, 0x50, 0xbd, 0xa0, 0xb3, 0x05, 0x33, 0xde, 0x82, 0x66, 0xca, 0x53, 0x48, 0x0f, - 0xea, 0x73, 0x16, 0x86, 0x74, 0xc2, 0x7a, 0xda, 0x3d, 0x6d, 0x5b, 0x37, 0xa3, 0xa1, 0xd1, 0x86, - 0xf5, 0xb4, 0x9f, 0x18, 0xf3, 0x58, 0x51, 0xf8, 0x82, 0x50, 0xbc, 0x60, 0x41, 0x28, 0x1c, 0x40, - 0x29, 0xaa, 0x21, 0xb9, 0x0f, 0x2d, 0xb4, 0x83, 0x15, 0xc9, 0x85, 0x9f, 0x56, 0xcc, 0x75, 0x9c, - 0x3c, 0x55, 0xa0, 0x2d, 0x68, 0xfa, 0x7b, 0x7e, 0x0c, 0x29, 0x23, 0x04, 0xfc, 0x3d, 0x5f, 0x01, - 0x8c, 0x0f, 0xa0, 0x9b, 0x77, 0x25, 0xd2, 0x85, 0xf2, 0x39, 0xbb, 0x54, 0xfb, 0x89, 0x4f, 0xb2, - 0xa1, 0xae, 0x85, 0x7b, 0xe8, 0xa6, 0xba, 0xe3, 0xe7, 0xa5, 0x58, 0x39, 0xf6, 0x26, 0xb2, 0x0f, - 0x15, 0x11, 0x54, 0xa8, 0xdd, 0xdc, 0xeb, 0x0f, 0x64, 0xc4, 0x0d, 0xa2, 0x88, 0x1b, 0x3c, 0x8f, - 0x22, 0x6e, 0xd8, 0xf8, 0xf2, 0xe5, 0xd6, 0xda, 0xe7, 0x7f, 0xd8, 0xd2, 0x4c, 0xd4, 0x20, 0x77, - 0x85, 0x43, 0x50, 0xc7, 0xb5, 0x1c, 0x5b, 0xed, 0x53, 0xc7, 0xf1, 0xb1, 0x4d, 0x0e, 0xa0, 0x3b, - 0xf6, 0xdc, 0x90, 0xb9, 0xe1, 0x22, 0xb4, 0x7c, 0x1a, 0xd0, 0x79, 0xa8, 0x62, 0x2d, 0xfa, 0xf9, - 0x0f, 0x23, 0xf1, 0x09, 0x4a, 0xcd, 0xce, 0x38, 0x3b, 0x41, 0x3e, 0x04, 0xb8, 0xa0, 0x33, 0xc7, - 0xa6, 0xdc, 0x0b, 0xc2, 0x5e, 0xe5, 0x5e, 0x39, 0xa5, 0x7c, 0x1a, 0x09, 0x5e, 0xf8, 0x36, 0xe5, - 0x6c, 0x58, 0x11, 0x27, 0x33, 0x53, 0x78, 0xf2, 0x26, 0x74, 0xa8, 0xef, 0x5b, 0x21, 0xa7, 0x9c, - 0x59, 0xa3, 0x4b, 0xce, 0x42, 0x8c, 0xc7, 0x75, 0xb3, 0x45, 0x7d, 0xff, 0x99, 0x98, 0x1d, 0x8a, - 0x49, 0xc3, 0x8e, 0x7f, 0x4d, 0x0c, 0x15, 0x42, 0xa0, 0x62, 0x53, 0x4e, 0xd1, 0x1a, 0xeb, 0x26, - 0x7e, 0x8b, 0x39, 0x9f, 0xf2, 0xa9, 0xba, 0x23, 0x7e, 0x93, 0xdb, 0x50, 0x9b, 0x32, 0x67, 0x32, - 0xe5, 0x78, 0xad, 0xb2, 0xa9, 0x46, 0xc2, 0xf0, 0x7e, 0xe0, 0x5d, 0x30, 0xcc, 0x16, 0x0d, 0x53, - 0x0e, 0x8c, 0xbf, 0x68, 0x70, 0xe3, 0x5a, 0x78, 0x89, 0x75, 0xa7, 0x34, 0x9c, 0x46, 0x7b, 0x89, - 0x6f, 0xf2, 0xb6, 0x58, 0x97, 0xda, 0x2c, 0x50, 0x59, 0xac, 0xa5, 0x6e, 0x7c, 0x84, 0x93, 0xea, - 0xa2, 0x0a, 0x42, 0x9e, 0x42, 0x77, 0x46, 0x43, 0x6e, 0xc9, 0x28, 0xb0, 0x30, 0x4b, 0x95, 0x33, - 0x91, 0xf9, 0x31, 0x8d, 0xa2, 0x45, 0x38, 0xa7, 0x52, 0x6f, 0xcf, 0x32, 0xb3, 0xe4, 0x08, 0x36, - 0x46, 0x97, 0x3f, 0xa7, 0x2e, 0x77, 0x5c, 0x66, 0x5d, 0xb3, 0x79, 0x47, 0x2d, 0xf5, 0xf4, 0xc2, - 0xb1, 0x99, 0x3b, 0x8e, 0x8c, 0x7d, 0x33, 0x56, 0x89, 0x7f, 0x8c, 0xd0, 0xb8, 0x07, 0xed, 0x6c, - 0x2e, 0x20, 0x6d, 0x28, 0xf1, 0xa5, 0xba, 0x61, 0x89, 0x2f, 0x0d, 0x23, 0xf6, 0xc0, 0x38, 0x20, - 0xaf, 0x61, 0x76, 0xa0, 0x93, 0x4b, 0x0e, 0x29, 0x73, 0x6b, 0x69, 0x73, 0x1b, 0x1d, 0x68, 0x65, - 0x72, 0x82, 0xf1, 0x59, 0x15, 0x1a, 0x26, 0x0b, 0x7d, 0xe1, 0x4c, 0x64, 0x1f, 0x74, 0xb6, 0x1c, - 0x33, 0x99, 0x8e, 0xb5, 0x5c, 0xb2, 0x93, 0x98, 0xa7, 0x91, 0x5c, 0xa4, 0x85, 0x18, 0x4c, 0x76, - 0x32, 0x54, 0x72, 0x33, 0xaf, 0x94, 0xe6, 0x92, 0x07, 0x59, 0x2e, 0xd9, 0xc8, 0x61, 0x73, 0x64, - 0xb2, 0x93, 0x21, 0x93, 0xfc, 0xc2, 0x19, 0x36, 0x79, 0x54, 0xc0, 0x26, 0xf9, 0xe3, 0xaf, 0xa0, - 0x93, 0x47, 0x05, 0x74, 0xd2, 0xbb, 0xb6, 0x57, 0x21, 0x9f, 0x3c, 0xc8, 0xf2, 0x49, 0xfe, 0x3a, - 0x39, 0x42, 0xf9, 0xb0, 0x88, 0x50, 0xee, 0xe6, 0x74, 0x56, 0x32, 0xca, 0xfb, 0xd7, 0x18, 0xe5, - 0x76, 0x4e, 0xb5, 0x80, 0x52, 0x1e, 0x65, 0x72, 0x3d, 0x14, 0xde, 0xad, 0x38, 0xd9, 0x93, 0xef, - 0x5c, 0x67, 0xa3, 0x3b, 0xf9, 0x9f, 0xb6, 0x88, 0x8e, 0x76, 0x73, 0x74, 0x74, 0x2b, 0x7f, 0xca, - 0x1c, 0x1f, 0x25, 0xac, 0xb2, 0x23, 0xe2, 0x3e, 0xe7, 0x69, 0x22, 0x47, 0xb0, 0x20, 0xf0, 0x02, - 0x95, 0xb0, 0xe5, 0xc0, 0xd8, 0x16, 0x99, 0x28, 0xf1, 0xaf, 0x57, 0x30, 0x10, 0x3a, 0x7d, 0xca, +var fileDescriptor_types_62f0c59aeb977f78 = []byte{ + // 2241 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0x4b, 0x73, 0x1c, 0x49, + 0xf1, 0x57, 0xcf, 0xbb, 0x73, 0x34, 0x0f, 0x97, 0x65, 0x7b, 0x3c, 0x7f, 0xff, 0x25, 0x47, 0x1b, + 0x76, 0x25, 0xd6, 0x3b, 0xda, 0xd5, 0x62, 0x42, 0xc6, 0xcb, 0x46, 0x68, 0x6c, 0x83, 0x14, 0x6b, + 0x40, 0xb4, 0x6d, 0x71, 0x21, 0xa2, 0xa3, 0x66, 0xba, 0x3c, 0xd3, 0xe1, 0x99, 0xee, 0xde, 0xee, + 0x9a, 0xd9, 0x11, 0x47, 0xce, 0x7b, 0xd8, 0x03, 0x1f, 0x81, 0x03, 0x1f, 0x61, 0x8f, 0x9c, 0x88, + 0x3d, 0x72, 0xe0, 0x6c, 0x40, 0x04, 0x17, 0x22, 0x38, 0x03, 0x37, 0xa2, 0xb2, 0xaa, 0x9f, 0xea, + 0x31, 0xbb, 0x86, 0x1b, 0x17, 0xa9, 0xab, 0xf2, 0x97, 0xf5, 0xc8, 0xc9, 0xcc, 0x5f, 0x66, 0xc1, + 0x75, 0x3a, 0x1a, 0x3b, 0xfb, 0xfc, 0xdc, 0x67, 0xa1, 0xfc, 0x3b, 0xf0, 0x03, 0x8f, 0x7b, 0xa4, + 0x8a, 0x83, 0xfe, 0xbb, 0x13, 0x87, 0x4f, 0x17, 0xa3, 0xc1, 0xd8, 0x9b, 0xef, 0x4f, 0xbc, 0x89, + 0xb7, 0x8f, 0xd2, 0xd1, 0xe2, 0x05, 0x8e, 0x70, 0x80, 0x5f, 0x52, 0xab, 0xff, 0x20, 0x05, 0xe7, + 0xcc, 0xb5, 0x59, 0x30, 0x77, 0x5c, 0x9e, 0xfe, 0x1c, 0x07, 0xe7, 0x3e, 0xf7, 0xf6, 0xe7, 0x2c, + 0x78, 0x39, 0x63, 0xea, 0x9f, 0x52, 0x3e, 0xfc, 0xb7, 0xca, 0x33, 0x67, 0x14, 0xee, 0x8f, 0xbd, + 0xf9, 0xdc, 0x73, 0xd3, 0x87, 0xed, 0xef, 0x4c, 0x3c, 0x6f, 0x32, 0x63, 0xc9, 0xe1, 0xb8, 0x33, + 0x67, 0x21, 0xa7, 0x73, 0x5f, 0x02, 0x8c, 0xdf, 0x56, 0xa0, 0x6e, 0xb2, 0x4f, 0x16, 0x2c, 0xe4, + 0x64, 0x17, 0x2a, 0x6c, 0x3c, 0xf5, 0x7a, 0xa5, 0xdb, 0xda, 0x6e, 0xf3, 0x80, 0x0c, 0xe4, 0x42, + 0x4a, 0xfa, 0x78, 0x3c, 0xf5, 0x8e, 0x37, 0x4c, 0x44, 0x90, 0x77, 0xa0, 0xfa, 0x62, 0xb6, 0x08, + 0xa7, 0xbd, 0x32, 0x42, 0xaf, 0x66, 0xa1, 0xdf, 0x17, 0xa2, 0xe3, 0x0d, 0x53, 0x62, 0xc4, 0xb2, + 0x8e, 0xfb, 0xc2, 0xeb, 0x55, 0x8a, 0x96, 0x3d, 0x71, 0x5f, 0xe0, 0xb2, 0x02, 0x41, 0x0e, 0x01, + 0x42, 0xc6, 0x2d, 0xcf, 0xe7, 0x8e, 0xe7, 0xf6, 0xaa, 0x88, 0xbf, 0x91, 0xc5, 0x3f, 0x65, 0xfc, + 0xc7, 0x28, 0x3e, 0xde, 0x30, 0xf5, 0x30, 0x1a, 0x08, 0x4d, 0xc7, 0x75, 0xb8, 0x35, 0x9e, 0x52, + 0xc7, 0xed, 0xd5, 0x8a, 0x34, 0x4f, 0x5c, 0x87, 0x3f, 0x14, 0x62, 0xa1, 0xe9, 0x44, 0x03, 0x71, + 0x95, 0x4f, 0x16, 0x2c, 0x38, 0xef, 0xd5, 0x8b, 0xae, 0xf2, 0x13, 0x21, 0x12, 0x57, 0x41, 0x0c, + 0x79, 0x00, 0xcd, 0x11, 0x9b, 0x38, 0xae, 0x35, 0x9a, 0x79, 0xe3, 0x97, 0xbd, 0x06, 0xaa, 0xf4, + 0xb2, 0x2a, 0x43, 0x01, 0x18, 0x0a, 0xf9, 0xf1, 0x86, 0x09, 0xa3, 0x78, 0x44, 0x0e, 0xa0, 0x31, + 0x9e, 0xb2, 0xf1, 0x4b, 0x8b, 0xaf, 0x7a, 0x3a, 0x6a, 0x5e, 0xcb, 0x6a, 0x3e, 0x14, 0xd2, 0x67, + 0xab, 0xe3, 0x0d, 0xb3, 0x3e, 0x96, 0x9f, 0xe4, 0x1e, 0xe8, 0xcc, 0xb5, 0xd5, 0x76, 0x4d, 0x54, + 0xba, 0x9e, 0xfb, 0x5d, 0x5c, 0x3b, 0xda, 0xac, 0xc1, 0xd4, 0x37, 0x19, 0x40, 0x4d, 0x38, 0x83, + 0xc3, 0x7b, 0x9b, 0xa8, 0xb3, 0x95, 0xdb, 0x08, 0x65, 0xc7, 0x1b, 0xa6, 0x42, 0x09, 0xf3, 0xd9, + 0x6c, 0xe6, 0x2c, 0x59, 0x20, 0x0e, 0x77, 0xb5, 0xc8, 0x7c, 0x8f, 0xa4, 0x1c, 0x8f, 0xa7, 0xdb, + 0xd1, 0x60, 0x58, 0x87, 0xea, 0x92, 0xce, 0x16, 0xcc, 0x78, 0x1b, 0x9a, 0x29, 0x4f, 0x21, 0x3d, + 0xa8, 0xcf, 0x59, 0x18, 0xd2, 0x09, 0xeb, 0x69, 0xb7, 0xb5, 0x5d, 0xdd, 0x8c, 0x86, 0x46, 0x1b, + 0x36, 0xd3, 0x7e, 0x62, 0xcc, 0x63, 0x45, 0xe1, 0x0b, 0x42, 0x71, 0xc9, 0x82, 0x50, 0x38, 0x80, + 0x52, 0x54, 0x43, 0x72, 0x07, 0x5a, 0x68, 0x07, 0x2b, 0x92, 0x0b, 0x3f, 0xad, 0x98, 0x9b, 0x38, + 0x79, 0xa6, 0x40, 0x3b, 0xd0, 0xf4, 0x0f, 0xfc, 0x18, 0x52, 0x46, 0x08, 0xf8, 0x07, 0xbe, 0x02, + 0x18, 0xdf, 0x85, 0x6e, 0xde, 0x95, 0x48, 0x17, 0xca, 0x2f, 0xd9, 0xb9, 0xda, 0x4f, 0x7c, 0x92, + 0x2d, 0x75, 0x2d, 0xdc, 0x43, 0x37, 0xd5, 0x1d, 0x3f, 0x2f, 0xc5, 0xca, 0xb1, 0x37, 0x91, 0x43, + 0xa8, 0x88, 0xa0, 0x42, 0xed, 0xe6, 0x41, 0x7f, 0x20, 0x23, 0x6e, 0x10, 0x45, 0xdc, 0xe0, 0x59, + 0x14, 0x71, 0xc3, 0xc6, 0x97, 0xaf, 0x76, 0x36, 0x3e, 0xff, 0xc3, 0x8e, 0x66, 0xa2, 0x06, 0xb9, + 0x29, 0x1c, 0x82, 0x3a, 0xae, 0xe5, 0xd8, 0x6a, 0x9f, 0x3a, 0x8e, 0x4f, 0x6c, 0x72, 0x04, 0xdd, + 0xb1, 0xe7, 0x86, 0xcc, 0x0d, 0x17, 0xa1, 0xe5, 0xd3, 0x80, 0xce, 0x43, 0x15, 0x6b, 0xd1, 0xcf, + 0xff, 0x30, 0x12, 0x9f, 0xa2, 0xd4, 0xec, 0x8c, 0xb3, 0x13, 0xe4, 0x43, 0x80, 0x25, 0x9d, 0x39, + 0x36, 0xe5, 0x5e, 0x10, 0xf6, 0x2a, 0xb7, 0xcb, 0x29, 0xe5, 0xb3, 0x48, 0xf0, 0xdc, 0xb7, 0x29, + 0x67, 0xc3, 0x8a, 0x38, 0x99, 0x99, 0xc2, 0x93, 0xb7, 0xa0, 0x43, 0x7d, 0xdf, 0x0a, 0x39, 0xe5, + 0xcc, 0x1a, 0x9d, 0x73, 0x16, 0x62, 0x3c, 0x6e, 0x9a, 0x2d, 0xea, 0xfb, 0x4f, 0xc5, 0xec, 0x50, + 0x4c, 0x1a, 0x76, 0xfc, 0x6b, 0x62, 0xa8, 0x10, 0x02, 0x15, 0x9b, 0x72, 0x8a, 0xd6, 0xd8, 0x34, + 0xf1, 0x5b, 0xcc, 0xf9, 0x94, 0x4f, 0xd5, 0x1d, 0xf1, 0x9b, 0x5c, 0x87, 0xda, 0x94, 0x39, 0x93, + 0x29, 0xc7, 0x6b, 0x95, 0x4d, 0x35, 0x12, 0x86, 0xf7, 0x03, 0x6f, 0xc9, 0x30, 0x5b, 0x34, 0x4c, + 0x39, 0x30, 0xfe, 0xa2, 0xc1, 0x95, 0x4b, 0xe1, 0x25, 0xd6, 0x9d, 0xd2, 0x70, 0x1a, 0xed, 0x25, + 0xbe, 0xc9, 0x3b, 0x62, 0x5d, 0x6a, 0xb3, 0x40, 0x65, 0xb1, 0x96, 0xba, 0xf1, 0x31, 0x4e, 0xaa, + 0x8b, 0x2a, 0x08, 0x79, 0x0c, 0xdd, 0x19, 0x0d, 0xb9, 0x25, 0xa3, 0xc0, 0xc2, 0x2c, 0x55, 0xce, + 0x44, 0xe6, 0x13, 0x1a, 0x45, 0x8b, 0x70, 0x4e, 0xa5, 0xde, 0x9e, 0x65, 0x66, 0xc9, 0x31, 0x6c, + 0x8d, 0xce, 0x7f, 0x4e, 0x5d, 0xee, 0xb8, 0xcc, 0xba, 0x64, 0xf3, 0x8e, 0x5a, 0xea, 0xf1, 0xd2, + 0xb1, 0x99, 0x3b, 0x8e, 0x8c, 0x7d, 0x35, 0x56, 0x89, 0x7f, 0x8c, 0xd0, 0xb8, 0x0d, 0xed, 0x6c, + 0x2e, 0x20, 0x6d, 0x28, 0xf1, 0x95, 0xba, 0x61, 0x89, 0xaf, 0x0c, 0x23, 0xf6, 0xc0, 0x38, 0x20, + 0x2f, 0x61, 0xf6, 0xa0, 0x93, 0x4b, 0x0e, 0x29, 0x73, 0x6b, 0x69, 0x73, 0x1b, 0x1d, 0x68, 0x65, + 0x72, 0x82, 0xf1, 0x59, 0x15, 0x1a, 0x26, 0x0b, 0x7d, 0xe1, 0x4c, 0xe4, 0x10, 0x74, 0xb6, 0x1a, + 0x33, 0x99, 0x8e, 0xb5, 0x5c, 0xb2, 0x93, 0x98, 0xc7, 0x91, 0x5c, 0xa4, 0x85, 0x18, 0x4c, 0xf6, + 0x32, 0x54, 0x72, 0x35, 0xaf, 0x94, 0xe6, 0x92, 0xbb, 0x59, 0x2e, 0xd9, 0xca, 0x61, 0x73, 0x64, + 0xb2, 0x97, 0x21, 0x93, 0xfc, 0xc2, 0x19, 0x36, 0xb9, 0x5f, 0xc0, 0x26, 0xf9, 0xe3, 0xaf, 0xa1, + 0x93, 0xfb, 0x05, 0x74, 0xd2, 0xbb, 0xb4, 0x57, 0x21, 0x9f, 0xdc, 0xcd, 0xf2, 0x49, 0xfe, 0x3a, + 0x39, 0x42, 0xf9, 0xb0, 0x88, 0x50, 0x6e, 0xe6, 0x74, 0xd6, 0x32, 0xca, 0x07, 0x97, 0x18, 0xe5, + 0x7a, 0x4e, 0xb5, 0x80, 0x52, 0xee, 0x67, 0x72, 0x3d, 0x14, 0xde, 0xad, 0x38, 0xd9, 0x93, 0xef, + 0x5c, 0x66, 0xa3, 0x1b, 0xf9, 0x9f, 0xb6, 0x88, 0x8e, 0xf6, 0x73, 0x74, 0x74, 0x2d, 0x7f, 0xca, + 0x1c, 0x1f, 0x25, 0xac, 0xb2, 0x27, 0xe2, 0x3e, 0xe7, 0x69, 0x22, 0x47, 0xb0, 0x20, 0xf0, 0x02, + 0x95, 0xb0, 0xe5, 0xc0, 0xd8, 0x15, 0x99, 0x28, 0xf1, 0xaf, 0xd7, 0x30, 0x10, 0x3a, 0x7d, 0xca, 0xbb, 0x8c, 0x2f, 0xb4, 0x44, 0x17, 0x23, 0x3a, 0x9d, 0xc5, 0x74, 0x95, 0xc5, 0x52, 0xc4, 0x54, - 0xca, 0x12, 0xd3, 0x16, 0x34, 0x45, 0xae, 0xcc, 0x71, 0x0e, 0xf5, 0x23, 0xce, 0x21, 0xdf, 0x82, - 0x1b, 0x98, 0x67, 0x24, 0x7d, 0xa9, 0x40, 0xac, 0x60, 0x20, 0x76, 0x84, 0x40, 0x5a, 0x4c, 0x26, - 0xc0, 0x77, 0xe0, 0x66, 0x0a, 0x2b, 0xd6, 0xc5, 0x1c, 0x27, 0x93, 0x6f, 0x37, 0x46, 0x1f, 0xf8, - 0xfe, 0x11, 0x0d, 0xa7, 0xc6, 0x0f, 0x13, 0x03, 0x25, 0x7c, 0x46, 0xa0, 0x32, 0xf6, 0x6c, 0x79, + 0xca, 0x12, 0xd3, 0x0e, 0x34, 0x45, 0xae, 0xcc, 0x71, 0x0e, 0xf5, 0x23, 0xce, 0x21, 0xdf, 0x82, + 0x2b, 0x98, 0x67, 0x24, 0x7d, 0xa9, 0x40, 0xac, 0x60, 0x20, 0x76, 0x84, 0x40, 0x5a, 0x4c, 0x26, + 0xc0, 0x77, 0xe1, 0x6a, 0x0a, 0x2b, 0xd6, 0xc5, 0x1c, 0x27, 0x93, 0x6f, 0x37, 0x46, 0x1f, 0xf9, + 0xfe, 0x31, 0x0d, 0xa7, 0xc6, 0x0f, 0x13, 0x03, 0x25, 0x7c, 0x46, 0xa0, 0x32, 0xf6, 0x6c, 0x79, 0xef, 0x96, 0x89, 0xdf, 0x82, 0xe3, 0x66, 0xde, 0x04, 0x0f, 0xa7, 0x9b, 0xe2, 0x53, 0xa0, 0xe2, 0x50, 0xd2, 0x65, 0xcc, 0x18, 0xbf, 0xd4, 0x92, 0xf5, 0x12, 0x8a, 0x2b, 0x62, 0x23, 0xed, 0x3f, - 0x61, 0xa3, 0xd2, 0xeb, 0xb1, 0x91, 0x71, 0xa5, 0x25, 0x3f, 0x59, 0xcc, 0x33, 0x5f, 0xef, 0x8a, - 0xc2, 0x7b, 0x1c, 0xd7, 0x66, 0x4b, 0x34, 0x69, 0xd9, 0x94, 0x83, 0xa8, 0x04, 0xa8, 0xa1, 0x99, - 0xb3, 0x25, 0x40, 0x1d, 0xe7, 0xe4, 0x80, 0xdc, 0x47, 0x7e, 0xf2, 0xce, 0x54, 0xa8, 0xb6, 0x06, - 0xaa, 0x50, 0x3f, 0x11, 0x93, 0xa6, 0x94, 0xa5, 0xb2, 0xad, 0x9e, 0x21, 0xb7, 0x37, 0x40, 0x17, - 0x07, 0x0d, 0x7d, 0x3a, 0x66, 0x18, 0x79, 0xba, 0x99, 0x4c, 0x18, 0x27, 0x40, 0xae, 0x47, 0x3c, - 0xf9, 0x00, 0x2a, 0x9c, 0x4e, 0x84, 0xbd, 0x85, 0xc9, 0xda, 0x03, 0x59, 0xe4, 0x0f, 0x3e, 0x3a, - 0x3d, 0xa1, 0x4e, 0x30, 0xbc, 0x2d, 0x4c, 0xf5, 0xb7, 0x97, 0x5b, 0x6d, 0x81, 0x79, 0xe0, 0xcd, - 0x1d, 0xce, 0xe6, 0x3e, 0xbf, 0x34, 0x51, 0xc7, 0xf8, 0xbb, 0x26, 0x98, 0x20, 0x93, 0x09, 0x0a, - 0x0d, 0x17, 0xb9, 0x7b, 0x29, 0x45, 0xda, 0x5f, 0xcd, 0x98, 0xff, 0x0f, 0x30, 0xa1, 0xa1, 0xf5, - 0x29, 0x75, 0x39, 0xb3, 0x95, 0x45, 0xf5, 0x09, 0x0d, 0x7f, 0x8a, 0x13, 0xa2, 0xc2, 0x11, 0xe2, - 0x45, 0xc8, 0x6c, 0x34, 0x6d, 0xd9, 0xac, 0x4f, 0x68, 0xf8, 0x22, 0x64, 0x76, 0x7c, 0xaf, 0xfa, - 0xeb, 0xdf, 0x2b, 0x6b, 0xc7, 0x46, 0xde, 0x8e, 0xff, 0x48, 0xf9, 0x70, 0x42, 0x92, 0xff, 0xfb, - 0xf7, 0xfe, 0xab, 0x26, 0x6a, 0x83, 0x6c, 0x1a, 0x26, 0xc7, 0x70, 0x23, 0x8e, 0x23, 0x6b, 0x81, - 0xf1, 0x15, 0xf9, 0xd2, 0xab, 0xc3, 0xaf, 0x7b, 0x91, 0x9d, 0x0e, 0xc9, 0x8f, 0xe0, 0x4e, 0x2e, - 0x0b, 0xc4, 0x0b, 0x96, 0x5e, 0x99, 0x0c, 0x6e, 0x65, 0x93, 0x41, 0xb4, 0x5e, 0x64, 0x89, 0xf2, - 0xd7, 0xf0, 0xec, 0x6f, 0x88, 0x42, 0x29, 0x4d, 0x1e, 0x45, 0xbf, 0xa5, 0xf1, 0x2b, 0x0d, 0x3a, - 0xb9, 0xc3, 0x90, 0x6d, 0xa8, 0x4a, 0xfe, 0xd2, 0x32, 0xed, 0x28, 0x5a, 0x4b, 0x9d, 0x57, 0x02, - 0xc8, 0x7b, 0xd0, 0x60, 0xaa, 0x66, 0x53, 0x17, 0xbc, 0x95, 0x2b, 0xe5, 0x14, 0x3e, 0x86, 0x91, - 0x6f, 0x83, 0x1e, 0x9b, 0x2d, 0x57, 0xaf, 0xc7, 0x56, 0x56, 0x4a, 0x09, 0xd0, 0x38, 0x84, 0x66, - 0x6a, 0x7b, 0xf2, 0x7f, 0xa0, 0xcf, 0xe9, 0x52, 0x15, 0xdd, 0xb2, 0x5c, 0x6b, 0xcc, 0xe9, 0x12, - 0xeb, 0x6d, 0x72, 0x07, 0xea, 0x42, 0x38, 0xa1, 0xd2, 0xe8, 0x65, 0xb3, 0x36, 0xa7, 0xcb, 0x1f, - 0xd0, 0xd0, 0xd8, 0x81, 0x76, 0xf6, 0x58, 0x11, 0x34, 0x22, 0x40, 0x09, 0x3d, 0x98, 0x30, 0xe3, - 0x21, 0x74, 0x72, 0xa7, 0x21, 0x06, 0xb4, 0xfc, 0xc5, 0xc8, 0x3a, 0x67, 0x97, 0x16, 0x1e, 0x17, - 0x5d, 0x44, 0x37, 0x9b, 0xfe, 0x62, 0xf4, 0x11, 0xbb, 0x7c, 0x2e, 0xa6, 0x8c, 0x67, 0xd0, 0xce, - 0x96, 0xc3, 0x22, 0x45, 0x06, 0xde, 0xc2, 0xb5, 0x71, 0xfd, 0xaa, 0x29, 0x07, 0xa2, 0xa3, 0xbe, - 0xf0, 0xa4, 0x57, 0xa4, 0xeb, 0xdf, 0x53, 0x8f, 0xb3, 0x54, 0x11, 0x2d, 0x31, 0xc6, 0x2f, 0xaa, - 0x50, 0x93, 0xb5, 0x39, 0x19, 0x64, 0x3b, 0x3f, 0xe1, 0x12, 0x4a, 0x53, 0xce, 0x2a, 0xc5, 0x98, - 0x76, 0xdf, 0xcc, 0xb7, 0x4f, 0xc3, 0xe6, 0xd5, 0xcb, 0xad, 0x3a, 0x52, 0xd6, 0xf1, 0x93, 0xa4, - 0x97, 0x5a, 0xd5, 0x6a, 0x44, 0x8d, 0x5b, 0xe5, 0xb5, 0x1b, 0xb7, 0x3b, 0x50, 0x77, 0x17, 0x73, - 0x8b, 0x2f, 0x43, 0x15, 0xfa, 0x35, 0x77, 0x31, 0x7f, 0xbe, 0xc4, 0x9f, 0x8e, 0x7b, 0x9c, 0xce, - 0x50, 0x24, 0x03, 0xbf, 0x81, 0x13, 0x42, 0xb8, 0x0f, 0xad, 0x14, 0xb3, 0x3b, 0xb6, 0xaa, 0x10, - 0xdb, 0x69, 0x0f, 0x3c, 0x7e, 0xa2, 0x6e, 0xd9, 0x8c, 0x99, 0xfe, 0xd8, 0x26, 0xdb, 0xd9, 0x3e, - 0x05, 0x0b, 0x82, 0x06, 0xfa, 0x79, 0xaa, 0x15, 0x11, 0xe5, 0x80, 0x38, 0x80, 0xf0, 0x7c, 0x09, - 0xd1, 0x11, 0xd2, 0x10, 0x13, 0x28, 0x7c, 0x0b, 0x3a, 0x09, 0xa7, 0x4a, 0x08, 0xc8, 0x55, 0x92, - 0x69, 0x04, 0xbe, 0x0b, 0x1b, 0x2e, 0x5b, 0x72, 0x2b, 0x8f, 0x6e, 0x22, 0x9a, 0x08, 0xd9, 0x69, - 0x56, 0xe3, 0x9b, 0xd0, 0x4e, 0x72, 0x03, 0x62, 0xd7, 0x65, 0xb7, 0x18, 0xcf, 0x22, 0xec, 0x2e, - 0x34, 0xe2, 0x8a, 0xa6, 0x85, 0x80, 0x3a, 0x95, 0x85, 0x4c, 0x5c, 0x23, 0x05, 0x2c, 0x5c, 0xcc, - 0xb8, 0x5a, 0xa4, 0x8d, 0x18, 0xac, 0x91, 0x4c, 0x39, 0x8f, 0xd8, 0xfb, 0xd0, 0x8a, 0x42, 0x4e, - 0xe2, 0x3a, 0x88, 0x5b, 0x8f, 0x26, 0x11, 0xb4, 0x03, 0x5d, 0x3f, 0xf0, 0x7c, 0x2f, 0x64, 0x81, - 0x45, 0x6d, 0x3b, 0x60, 0x61, 0xd8, 0xeb, 0xca, 0xf5, 0xa2, 0xf9, 0x03, 0x39, 0x6d, 0xbc, 0x07, - 0xf5, 0xa8, 0x54, 0xdb, 0x80, 0xea, 0x30, 0x4e, 0x0f, 0x15, 0x53, 0x0e, 0x04, 0x29, 0x1c, 0xf8, - 0xbe, 0x7a, 0x70, 0x10, 0x9f, 0xc6, 0xcf, 0xa0, 0xae, 0x7e, 0xb0, 0xc2, 0x36, 0xf4, 0xbb, 0xb0, - 0xee, 0xd3, 0x40, 0x5c, 0x23, 0xdd, 0x8c, 0x46, 0xcd, 0xc0, 0x09, 0x0d, 0xf8, 0x33, 0xc6, 0x33, - 0x3d, 0x69, 0x13, 0xf1, 0x72, 0xca, 0x78, 0x04, 0xad, 0x0c, 0x46, 0x1c, 0x0b, 0xfd, 0x28, 0x8a, - 0x34, 0x1c, 0xc4, 0x3b, 0x97, 0x92, 0x9d, 0x8d, 0xc7, 0xa0, 0xc7, 0xbf, 0x8d, 0xa8, 0x59, 0xa3, - 0xab, 0x6b, 0xca, 0xdc, 0x72, 0x88, 0x7d, 0xb6, 0xf7, 0x29, 0x0b, 0x54, 0x4c, 0xc8, 0x81, 0xf1, - 0x22, 0x95, 0x19, 0x64, 0x9a, 0x26, 0x0f, 0xa0, 0xae, 0x32, 0x83, 0x8a, 0xca, 0xa8, 0xa3, 0x3e, - 0xc1, 0xd4, 0x10, 0x75, 0xd4, 0x32, 0x51, 0x24, 0xcb, 0x96, 0xd2, 0xcb, 0xce, 0xa0, 0x11, 0x45, - 0x7f, 0x36, 0x45, 0xca, 0x15, 0xbb, 0xf9, 0x14, 0xa9, 0x16, 0x4d, 0x80, 0xc2, 0x3b, 0x42, 0x67, - 0xe2, 0x32, 0xdb, 0x4a, 0x42, 0x08, 0xf7, 0x68, 0x98, 0x1d, 0x29, 0xf8, 0x38, 0x8a, 0x17, 0xe3, - 0x5d, 0xa8, 0xc9, 0xb3, 0x09, 0xfb, 0x88, 0x95, 0xa3, 0x32, 0x5e, 0x7c, 0x17, 0xf2, 0xc4, 0xef, - 0x35, 0x68, 0x44, 0xc9, 0xb3, 0x50, 0x29, 0x73, 0xe8, 0xd2, 0x57, 0x3d, 0xf4, 0x7f, 0x3f, 0xf1, - 0x3c, 0x00, 0x22, 0xf3, 0xcb, 0x85, 0xc7, 0x1d, 0x77, 0x62, 0x49, 0x5b, 0xcb, 0x1c, 0xd4, 0x45, - 0xc9, 0x29, 0x0a, 0x4e, 0xc4, 0xfc, 0xde, 0x67, 0x55, 0xe8, 0x1c, 0x0c, 0x0f, 0x8f, 0x0f, 0x7c, - 0x7f, 0xe6, 0x8c, 0x29, 0xb6, 0x06, 0xbb, 0x50, 0xc1, 0xee, 0xa8, 0xe0, 0x75, 0xb7, 0x5f, 0xd4, - 0xa6, 0x93, 0x3d, 0xa8, 0x62, 0x93, 0x44, 0x8a, 0x1e, 0x79, 0xfb, 0x85, 0xdd, 0xba, 0xd8, 0x44, - 0xb6, 0x51, 0xd7, 0xdf, 0x7a, 0xfb, 0x45, 0x2d, 0x3b, 0xf9, 0x1e, 0xe8, 0x49, 0xf7, 0xb2, 0xea, - 0xc5, 0xb7, 0xbf, 0xb2, 0x79, 0x17, 0xfa, 0x49, 0xa5, 0xb7, 0xea, 0xe1, 0xb2, 0xbf, 0xb2, 0xcb, - 0x25, 0xfb, 0x50, 0x8f, 0xea, 0xe3, 0xe2, 0x37, 0xd9, 0xfe, 0x8a, 0xc6, 0x5a, 0x98, 0x47, 0x36, - 0x24, 0x45, 0x0f, 0xc7, 0xfd, 0xc2, 0xee, 0x9f, 0x3c, 0x84, 0x9a, 0x2a, 0x5a, 0x0a, 0xdf, 0x65, - 0xfb, 0xc5, 0xed, 0xb1, 0xb8, 0x64, 0xd2, 0x92, 0xad, 0x7a, 0xdc, 0xee, 0xaf, 0x7c, 0xa6, 0x20, - 0x07, 0x00, 0xa9, 0xbe, 0x62, 0xe5, 0xab, 0x75, 0x7f, 0xf5, 0xf3, 0x03, 0x79, 0x0c, 0x8d, 0xe4, - 0x49, 0xa9, 0xf8, 0x1d, 0xba, 0xbf, 0xea, 0x45, 0x60, 0xf8, 0xc6, 0x3f, 0xff, 0xb4, 0xa9, 0xfd, - 0xfa, 0x6a, 0x53, 0xfb, 0xe2, 0x6a, 0x53, 0xfb, 0xf2, 0x6a, 0x53, 0xfb, 0xdd, 0xd5, 0xa6, 0xf6, - 0xc7, 0xab, 0x4d, 0xed, 0x37, 0x7f, 0xde, 0xd4, 0x46, 0x35, 0x74, 0xff, 0xf7, 0xff, 0x15, 0x00, - 0x00, 0xff, 0xff, 0x38, 0x2d, 0x52, 0x86, 0x77, 0x19, 0x00, 0x00, + 0x61, 0xa3, 0xd2, 0xd7, 0x63, 0x23, 0xe3, 0x42, 0x4b, 0x7e, 0xb2, 0x98, 0x67, 0xde, 0xec, 0x8a, + 0xc2, 0x7b, 0x1c, 0xd7, 0x66, 0x2b, 0x34, 0x69, 0xd9, 0x94, 0x83, 0xa8, 0x04, 0xa8, 0xa1, 0x99, + 0xb3, 0x25, 0x40, 0x1d, 0xe7, 0xe4, 0x80, 0xdc, 0x41, 0x7e, 0xf2, 0x5e, 0xa8, 0x50, 0x6d, 0x0d, + 0x54, 0xa1, 0x7e, 0x2a, 0x26, 0x4d, 0x29, 0x4b, 0x65, 0x5b, 0x3d, 0x43, 0x6e, 0xb7, 0x40, 0x17, + 0x07, 0x0d, 0x7d, 0x3a, 0x66, 0x18, 0x79, 0xba, 0x99, 0x4c, 0x18, 0xcf, 0x80, 0x5c, 0x8e, 0x78, + 0xf2, 0x11, 0xd4, 0xd8, 0x92, 0xb9, 0x5c, 0x58, 0x5c, 0x18, 0x6d, 0x33, 0xa6, 0x13, 0xe6, 0xf2, + 0x61, 0x4f, 0x98, 0xea, 0xaf, 0xaf, 0x76, 0xba, 0x12, 0x73, 0xd7, 0x9b, 0x3b, 0x9c, 0xcd, 0x7d, + 0x7e, 0x6e, 0x2a, 0x2d, 0xe3, 0xef, 0x9a, 0x60, 0x83, 0x4c, 0x36, 0x28, 0x34, 0x5e, 0xe4, 0xf2, + 0xa5, 0x14, 0x71, 0x7f, 0x35, 0x83, 0xfe, 0x3f, 0xc0, 0x84, 0x86, 0xd6, 0xa7, 0xd4, 0xe5, 0xcc, + 0x56, 0x56, 0xd5, 0x27, 0x34, 0xfc, 0x29, 0x4e, 0x88, 0x2a, 0x47, 0x88, 0x17, 0x21, 0xb3, 0xd1, + 0xbc, 0x65, 0xb3, 0x3e, 0xa1, 0xe1, 0xf3, 0x90, 0xd9, 0xa9, 0xbb, 0xd5, 0xdf, 0xe4, 0x6e, 0x59, + 0x7b, 0x36, 0xf2, 0xf6, 0xfc, 0x67, 0xca, 0x97, 0x13, 0xb2, 0xfc, 0xdf, 0xb8, 0xfb, 0xdf, 0x34, + 0x51, 0x27, 0x64, 0x53, 0x32, 0x39, 0x81, 0x2b, 0x71, 0x4c, 0x59, 0x0b, 0x8c, 0xb5, 0xc8, 0xab, + 0x5e, 0x1f, 0x8a, 0xdd, 0x65, 0x76, 0x3a, 0x24, 0x3f, 0x82, 0x1b, 0xb9, 0x8c, 0x10, 0x2f, 0x58, + 0x7a, 0x6d, 0x62, 0xb8, 0x96, 0x4d, 0x0c, 0xd1, 0x7a, 0x89, 0x35, 0xca, 0x6f, 0xe4, 0xe5, 0xdf, + 0x10, 0x85, 0x53, 0x9a, 0x4c, 0x8a, 0x7e, 0x53, 0xe3, 0x57, 0x1a, 0x74, 0x72, 0x07, 0x22, 0xbb, + 0x50, 0x95, 0x7c, 0xa6, 0x65, 0xda, 0x53, 0xb4, 0x98, 0x3a, 0xb3, 0x04, 0x90, 0xf7, 0xa1, 0xc1, + 0x54, 0x0d, 0xa7, 0x2e, 0x79, 0x2d, 0x57, 0xda, 0x29, 0x7c, 0x0c, 0x23, 0xdf, 0x06, 0x3d, 0x36, + 0x5d, 0xae, 0x7e, 0x8f, 0x2d, 0xad, 0x94, 0x12, 0xa0, 0xf1, 0x10, 0x9a, 0xa9, 0xed, 0xc9, 0xff, + 0x81, 0x3e, 0xa7, 0x2b, 0x55, 0x84, 0xcb, 0xf2, 0xad, 0x31, 0xa7, 0x2b, 0xac, 0xbf, 0xc9, 0x0d, + 0xa8, 0x0b, 0xe1, 0x84, 0x4a, 0xc3, 0x97, 0xcd, 0xda, 0x9c, 0xae, 0x7e, 0x40, 0x43, 0x63, 0x0f, + 0xda, 0xd9, 0x63, 0x45, 0xd0, 0x88, 0x10, 0x25, 0xf4, 0x68, 0xc2, 0x8c, 0x7b, 0xd0, 0xc9, 0x9d, + 0x86, 0x18, 0xd0, 0xf2, 0x17, 0x23, 0xeb, 0x25, 0x3b, 0xb7, 0xf0, 0xb8, 0xe8, 0x26, 0xba, 0xd9, + 0xf4, 0x17, 0xa3, 0x8f, 0xd9, 0xf9, 0x33, 0x31, 0x65, 0x3c, 0x85, 0x76, 0xb6, 0x3c, 0x16, 0x29, + 0x33, 0xf0, 0x16, 0xae, 0x8d, 0xeb, 0x57, 0x4d, 0x39, 0x10, 0x1d, 0xf6, 0xd2, 0x93, 0x9e, 0x91, + 0xae, 0x87, 0xcf, 0x3c, 0xce, 0x52, 0x45, 0xb5, 0xc4, 0x18, 0x0e, 0x54, 0xf1, 0x37, 0x17, 0xbf, + 0x9f, 0xc0, 0x45, 0x14, 0x2c, 0xbe, 0xc9, 0x13, 0x00, 0xca, 0x79, 0xe0, 0x8c, 0x16, 0xc9, 0x72, + 0xed, 0x81, 0x7c, 0xf6, 0x18, 0x7c, 0x7c, 0x76, 0x4a, 0x9d, 0x60, 0x78, 0x4b, 0xf9, 0xca, 0x56, + 0x82, 0x4c, 0xf9, 0x4b, 0x4a, 0xdf, 0xf8, 0x45, 0x15, 0x6a, 0xb2, 0x2d, 0x20, 0x83, 0x6c, 0xd3, + 0x29, 0x56, 0x55, 0x87, 0x94, 0xb3, 0xea, 0x8c, 0x31, 0xe3, 0xbf, 0x95, 0xef, 0xdc, 0x86, 0xcd, + 0x8b, 0x57, 0x3b, 0x75, 0x64, 0xcb, 0x93, 0x47, 0x49, 0x1b, 0xb7, 0xae, 0xcb, 0x89, 0x7a, 0xc6, + 0xca, 0xd7, 0xee, 0x19, 0x6f, 0x40, 0xdd, 0x5d, 0xcc, 0x2d, 0xbe, 0x0a, 0x55, 0xb6, 0xa9, 0xb9, + 0x8b, 0xf9, 0xb3, 0x15, 0x7a, 0x09, 0xf7, 0x38, 0x9d, 0xa1, 0x48, 0xe6, 0x9a, 0x06, 0x4e, 0x08, + 0xe1, 0x21, 0xb4, 0x52, 0x45, 0x85, 0x63, 0xab, 0xe2, 0xb4, 0x9d, 0x76, 0xf6, 0x93, 0x47, 0xea, + 0x96, 0xcd, 0xb8, 0xc8, 0x38, 0xb1, 0xc9, 0x6e, 0xb6, 0x45, 0xc2, 0x5a, 0xa4, 0x81, 0x21, 0x95, + 0xea, 0x82, 0x44, 0x25, 0x22, 0x0e, 0x20, 0x82, 0x4c, 0x42, 0x74, 0x84, 0x34, 0xc4, 0x04, 0x0a, + 0xdf, 0x86, 0x4e, 0x42, 0xe7, 0x12, 0x02, 0x72, 0x95, 0x64, 0x1a, 0x81, 0xef, 0xc1, 0x96, 0xcb, + 0x56, 0xdc, 0xca, 0xa3, 0x9b, 0x88, 0x26, 0x42, 0x76, 0x96, 0xd5, 0xf8, 0x26, 0xb4, 0x93, 0x54, + 0x84, 0xd8, 0x4d, 0xd9, 0xa8, 0xc6, 0xb3, 0x08, 0xbb, 0x09, 0x8d, 0xb8, 0x98, 0x6a, 0x21, 0xa0, + 0x4e, 0x65, 0x0d, 0x15, 0x97, 0x67, 0x01, 0x0b, 0x17, 0x33, 0xae, 0x16, 0x69, 0x23, 0x06, 0xcb, + 0x33, 0x53, 0xce, 0x23, 0xf6, 0x0e, 0xb4, 0xa2, 0xe8, 0x96, 0xb8, 0x0e, 0xe2, 0x36, 0xa3, 0x49, + 0x04, 0xed, 0x41, 0xd7, 0x0f, 0x3c, 0xdf, 0x0b, 0x59, 0x60, 0x51, 0xdb, 0x0e, 0x58, 0x18, 0xf6, + 0xba, 0x72, 0xbd, 0x68, 0xfe, 0x48, 0x4e, 0x1b, 0xef, 0x43, 0x3d, 0xaa, 0x12, 0xb7, 0xa0, 0x3a, + 0x8c, 0x33, 0x51, 0xc5, 0x94, 0x03, 0xc1, 0x43, 0x47, 0xbe, 0xaf, 0xde, 0x3a, 0xc4, 0xa7, 0xf1, + 0x33, 0xa8, 0xab, 0x1f, 0xac, 0xb0, 0x03, 0xfe, 0x1e, 0x6c, 0xfa, 0x34, 0x10, 0xd7, 0x48, 0xf7, + 0xc1, 0x51, 0x1f, 0x72, 0x4a, 0x03, 0xfe, 0x94, 0xf1, 0x4c, 0x3b, 0xdc, 0x44, 0xbc, 0x9c, 0x32, + 0xee, 0x43, 0x2b, 0x83, 0x11, 0xc7, 0x42, 0x3f, 0x8a, 0x82, 0x1a, 0x07, 0xf1, 0xce, 0xa5, 0x64, + 0x67, 0xe3, 0x01, 0xe8, 0xf1, 0x6f, 0x23, 0xca, 0xe5, 0xe8, 0xea, 0x9a, 0x32, 0xb7, 0x1c, 0x62, + 0x8b, 0xef, 0x7d, 0xca, 0x02, 0x15, 0x13, 0x72, 0x60, 0x3c, 0x4f, 0x25, 0x21, 0xc9, 0x0a, 0xe4, + 0x2e, 0xd4, 0x55, 0x12, 0x52, 0x51, 0x19, 0x35, 0xf3, 0xa7, 0x98, 0x85, 0xa2, 0x66, 0x5e, 0xe6, + 0xa4, 0x64, 0xd9, 0x52, 0x7a, 0xd9, 0x19, 0x34, 0xa2, 0x44, 0x93, 0xcd, 0xc6, 0x72, 0xc5, 0x6e, + 0x3e, 0x1b, 0xab, 0x45, 0x13, 0xa0, 0xf0, 0x8e, 0xd0, 0x99, 0xb8, 0xcc, 0xb6, 0x92, 0x10, 0xc2, + 0x3d, 0x1a, 0x66, 0x47, 0x0a, 0x9e, 0x44, 0xf1, 0x62, 0xbc, 0x07, 0x35, 0x79, 0xb6, 0xc2, 0xf4, + 0x55, 0x44, 0x49, 0xbf, 0xd7, 0xa0, 0x11, 0xe5, 0xe9, 0x42, 0xa5, 0xcc, 0xa1, 0x4b, 0x5f, 0xf5, + 0xd0, 0xff, 0xfd, 0xc4, 0x73, 0x17, 0x88, 0xcc, 0x2f, 0x4b, 0x8f, 0x3b, 0xee, 0xc4, 0x92, 0xb6, + 0x96, 0x39, 0xa8, 0x8b, 0x92, 0x33, 0x14, 0x9c, 0x8a, 0xf9, 0x83, 0xcf, 0xaa, 0xd0, 0x39, 0x1a, + 0x3e, 0x3c, 0x39, 0xf2, 0xfd, 0x99, 0x33, 0xa6, 0xd8, 0x95, 0xec, 0x43, 0x05, 0x1b, 0xb3, 0x82, + 0x87, 0xe5, 0x7e, 0xd1, 0x0b, 0x01, 0x39, 0x80, 0x2a, 0xf6, 0x67, 0xa4, 0xe8, 0x7d, 0xb9, 0x5f, + 0xf8, 0x50, 0x20, 0x36, 0x91, 0x1d, 0xdc, 0xe5, 0x67, 0xe6, 0x7e, 0xd1, 0x6b, 0x01, 0xf9, 0x08, + 0xf4, 0xa4, 0x71, 0x5a, 0xf7, 0xd8, 0xdc, 0x5f, 0xfb, 0x6e, 0x20, 0xf4, 0x93, 0xe2, 0x72, 0xdd, + 0x9b, 0x69, 0x7f, 0x6d, 0x83, 0x4d, 0x0e, 0xa1, 0x1e, 0x95, 0xe5, 0xc5, 0xcf, 0xc1, 0xfd, 0x35, + 0x3d, 0xbd, 0x30, 0x8f, 0xec, 0x85, 0x8a, 0xde, 0xac, 0xfb, 0x85, 0x0f, 0x0f, 0xe4, 0x1e, 0xd4, + 0x54, 0x7d, 0x54, 0xf8, 0x24, 0xdc, 0x2f, 0xee, 0xcc, 0xc5, 0x25, 0x93, 0x6e, 0x70, 0xdd, 0xbb, + 0x7a, 0x7f, 0xed, 0x0b, 0x09, 0x39, 0x02, 0x48, 0xb5, 0x34, 0x6b, 0x1f, 0xcc, 0xfb, 0xeb, 0x5f, + 0x3e, 0xc8, 0x03, 0x68, 0x24, 0xaf, 0x59, 0xc5, 0x4f, 0xe0, 0xfd, 0x75, 0x8f, 0x11, 0xc3, 0x5b, + 0xff, 0xf8, 0xd3, 0xb6, 0xf6, 0xeb, 0x8b, 0x6d, 0xed, 0x8b, 0x8b, 0x6d, 0xed, 0xcb, 0x8b, 0x6d, + 0xed, 0x77, 0x17, 0xdb, 0xda, 0x1f, 0x2f, 0xb6, 0xb5, 0xdf, 0xfc, 0x79, 0x5b, 0x1b, 0xd5, 0xd0, + 0xfd, 0x3f, 0xf8, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x33, 0xc0, 0x3b, 0xf2, 0x19, 0x00, + 0x00, } diff --git a/abci/types/types.proto b/abci/types/types.proto index 4fdf575c6..8a2da5b47 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -165,7 +165,7 @@ message ResponseQuery { } message ResponseBeginBlock { - repeated common.KVPair tags = 1 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; + repeated Event events = 1 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; } message ResponseCheckTx { @@ -175,7 +175,7 @@ message ResponseCheckTx { string info = 4; // nondeterministic int64 gas_wanted = 5; int64 gas_used = 6; - repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; + repeated Event events = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; string codespace = 8; } @@ -186,14 +186,14 @@ message ResponseDeliverTx { string info = 4; // nondeterministic int64 gas_wanted = 5; int64 gas_used = 6; - repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; + repeated Event events = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; string codespace = 8; } message ResponseEndBlock { repeated ValidatorUpdate validator_updates = 1 [(gogoproto.nullable)=false]; ConsensusParams consensus_param_updates = 2; - repeated common.KVPair tags = 3 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"]; + repeated Event events = 3 [(gogoproto.nullable)=false, (gogoproto.jsontag)="events,omitempty"]; } message ResponseCommit { @@ -236,6 +236,11 @@ message LastCommitInfo { repeated VoteInfo votes = 2 [(gogoproto.nullable)=false]; } +message Event { + string type = 1; + repeated common.KVPair attributes = 2 [(gogoproto.nullable)=false, (gogoproto.jsontag)="attributes,omitempty"]; +} + //---------------------------------------- // Blockchain Types diff --git a/abci/types/typespb_test.go b/abci/types/typespb_test.go index a4c0a3f82..523561532 100644 --- a/abci/types/typespb_test.go +++ b/abci/types/typespb_test.go @@ -1703,6 +1703,62 @@ func TestLastCommitInfoMarshalTo(t *testing.T) { } } +func TestEventProto(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedEvent(popr, false) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + msg := &Event{} + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + littlefuzz := make([]byte, len(dAtA)) + copy(littlefuzz, dAtA) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } + if len(littlefuzz) > 0 { + fuzzamount := 100 + for i := 0; i < fuzzamount; i++ { + littlefuzz[popr.Intn(len(littlefuzz))] = byte(popr.Intn(256)) + littlefuzz = append(littlefuzz, byte(popr.Intn(256))) + } + // shouldn't panic + _ = github_com_gogo_protobuf_proto.Unmarshal(littlefuzz, msg) + } +} + +func TestEventMarshalTo(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedEvent(popr, false) + size := p.Size() + dAtA := make([]byte, size) + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) + } + _, err := p.MarshalTo(dAtA) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + msg := &Event{} + if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + for i := range dAtA { + dAtA[i] = byte(popr.Intn(256)) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } +} + func TestHeaderProto(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -2747,6 +2803,24 @@ func TestLastCommitInfoJSON(t *testing.T) { t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) } } +func TestEventJSON(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedEvent(popr, true) + marshaler := github_com_gogo_protobuf_jsonpb.Marshaler{} + jsondata, err := marshaler.MarshalToString(p) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + msg := &Event{} + err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p) + } +} func TestHeaderJSON(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -3749,6 +3823,34 @@ func TestLastCommitInfoProtoCompactText(t *testing.T) { } } +func TestEventProtoText(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedEvent(popr, true) + dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p) + msg := &Event{} + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } +} + +func TestEventProtoCompactText(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedEvent(popr, true) + dAtA := github_com_gogo_protobuf_proto.CompactTextString(p) + msg := &Event{} + if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + if !p.Equal(msg) { + t.Fatalf("seed = %d, %#v !Proto %#v", seed, msg, p) + } +} + func TestHeaderProtoText(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) @@ -4661,6 +4763,28 @@ func TestLastCommitInfoSize(t *testing.T) { } } +func TestEventSize(t *testing.T) { + seed := time.Now().UnixNano() + popr := math_rand.New(math_rand.NewSource(seed)) + p := NewPopulatedEvent(popr, true) + size2 := github_com_gogo_protobuf_proto.Size(p) + dAtA, err := github_com_gogo_protobuf_proto.Marshal(p) + if err != nil { + t.Fatalf("seed = %d, err = %v", seed, err) + } + size := p.Size() + if len(dAtA) != size { + t.Errorf("seed = %d, size %v != marshalled size %v", seed, size, len(dAtA)) + } + if size2 != size { + t.Errorf("seed = %d, size %v != before marshal proto.Size %v", seed, size, size2) + } + size3 := github_com_gogo_protobuf_proto.Size(p) + if size3 != size { + t.Errorf("seed = %d, size %v != after marshal proto.Size %v", seed, size, size3) + } +} + func TestHeaderSize(t *testing.T) { seed := time.Now().UnixNano() popr := math_rand.New(math_rand.NewSource(seed)) diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 52ef9ba64..1a4a87d08 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -292,7 +292,7 @@ func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { } func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { - return abci.ResponseDeliverTx{Tags: []cmn.KVPair{}} + return abci.ResponseDeliverTx{Events: []abci.Event{}} } func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx { diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index c65d96ec1..6dc00b5b9 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -38,20 +38,58 @@ Finally, `Query`, `CheckTx`, and `DeliverTx` include a `Codespace string`, whose intended use is to disambiguate `Code` values returned by different domains of the application. The `Codespace` is a namespace for the `Code`. -## Tags +## Events Some methods (`CheckTx, BeginBlock, DeliverTx, EndBlock`) -include a `Tags` field in their `Response*`. Each tag is key-value pair denoting -something about what happened during the methods execution. +include an `Events` field in their `Response*`. Each event contains a type and a +list of attributes, which are key-value pairs denoting something about what happened +during the method's execution. -Tags can be used to index transactions and blocks according to what happened -during their execution. Note that the set of tags returned for a block from +Events can be used to index transactions and blocks according to what happened +during their execution. Note that the set of events returned for a block from `BeginBlock` and `EndBlock` are merged. In case both methods return the same tag, only the value defined in `EndBlock` is used. -Keys and values in tags must be UTF-8 encoded strings (e.g. -"account.owner": "Bob", "balance": "100.0", -"time": "2018-01-02T12:30:00Z") +Each event has a `type` which is meant to categorize the event for a particular +`Response*` or tx. A `Response*` or tx may contain multiple events with duplicate +`type` values, where each distinct entry is meant to categorize attributes for a +particular event. Every key and value in an event's attributes must be UTF-8 +encoded strings along with the even type itself. + +Example: + +```go + abci.ResponseDeliverTx{ + // ... + Events: []abci.Event{ + { + Type: "validator.provisions", + Attributes: cmn.KVPairs{ + cmn.KVPair{Key: []byte("address"), Value: []byte("...")}, + cmn.KVPair{Key: []byte("amount"), Value: []byte("...")}, + cmn.KVPair{Key: []byte("balance"), Value: []byte("...")}, + }, + }, + { + Type: "validator.provisions", + Attributes: cmn.KVPairs{ + cmn.KVPair{Key: []byte("address"), Value: []byte("...")}, + cmn.KVPair{Key: []byte("amount"), Value: []byte("...")}, + cmn.KVPair{Key: []byte("balance"), Value: []byte("...")}, + }, + }, + { + Type: "validator.slashed", + Attributes: cmn.KVPairs{ + cmn.KVPair{Key: []byte("address"), Value: []byte("...")}, + cmn.KVPair{Key: []byte("amount"), Value: []byte("...")}, + cmn.KVPair{Key: []byte("reason"), Value: []byte("...")}, + }, + }, + // ... + }, +} +``` ## Determinism diff --git a/libs/pubsub/example_test.go b/libs/pubsub/example_test.go index a43696266..34bb2a88f 100644 --- a/libs/pubsub/example_test.go +++ b/libs/pubsub/example_test.go @@ -21,7 +21,7 @@ func TestExample(t *testing.T) { ctx := context.Background() subscription, err := s.Subscribe(ctx, "example-client", query.MustParse("abci.account.name='John'")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Tombstone", map[string]string{"abci.account.name": "John"}) + err = s.PublishWithEvents(ctx, "Tombstone", map[string][]string{"abci.account.name": {"John"}}) require.NoError(t, err) assertReceive(t, "Tombstone", subscription.Out()) } diff --git a/libs/pubsub/pubsub.go b/libs/pubsub/pubsub.go index f78dac1ba..cb7b8d5bb 100644 --- a/libs/pubsub/pubsub.go +++ b/libs/pubsub/pubsub.go @@ -26,7 +26,7 @@ // for { // select { // case msg <- subscription.Out(): -// // handle msg.Data() and msg.Tags() +// // handle msg.Data() and msg.Events() // case <-subscription.Cancelled(): // return subscription.Err() // } @@ -61,9 +61,14 @@ var ( ErrAlreadySubscribed = errors.New("already subscribed") ) -// Query defines an interface for a query to be used for subscribing. +// Query defines an interface for a query to be used for subscribing. A query +// matches against a map of events. Each key in this map is a composite of the +// even type and an attribute key (e.g. "{eventType}.{eventAttrKey}") and the +// values are the event values that are contained under that relationship. This +// allows event types to repeat themselves with the same set of keys and +// different values. type Query interface { - Matches(tags map[string]string) bool + Matches(events map[string][]string) bool String() string } @@ -76,12 +81,12 @@ type cmd struct { clientID string // publish - msg interface{} - tags map[string]string + msg interface{} + events map[string][]string } // Server allows clients to subscribe/unsubscribe for messages, publishing -// messages with or without tags, and manages internal state. +// messages with or without events, and manages internal state. type Server struct { cmn.BaseService @@ -258,15 +263,15 @@ func (s *Server) NumClientSubscriptions(clientID string) int { // 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, make(map[string]string)) + return s.PublishWithEvents(ctx, msg, make(map[string][]string)) } -// PublishWithTags publishes the given message with the set of tags. The set is -// matched with clients queries. If there is a match, the message is sent to +// PublishWithEvents publishes the given message with the set of events. The set +// is matched with clients queries. If there is a match, the message is sent to // the client. -func (s *Server) PublishWithTags(ctx context.Context, msg interface{}, tags map[string]string) error { +func (s *Server) PublishWithEvents(ctx context.Context, msg interface{}, events map[string][]string) error { select { - case s.cmds <- cmd{op: pub, msg: msg, tags: tags}: + case s.cmds <- cmd{op: pub, msg: msg, events: events}: return nil case <-ctx.Done(): return ctx.Err() @@ -325,7 +330,7 @@ loop: case sub: state.add(cmd.clientID, cmd.query, cmd.subscription) case pub: - state.send(cmd.msg, cmd.tags) + state.send(cmd.msg, cmd.events) } } } @@ -392,18 +397,18 @@ func (state *state) removeAll(reason error) { } } -func (state *state) send(msg interface{}, tags map[string]string) { +func (state *state) send(msg interface{}, events map[string][]string) { for qStr, clientSubscriptions := range state.subscriptions { q := state.queries[qStr].q - if q.Matches(tags) { + if q.Matches(events) { for clientID, subscription := range clientSubscriptions { if cap(subscription.out) == 0 { // block on unbuffered channel - subscription.out <- Message{msg, tags} + subscription.out <- NewMessage(msg, events) } else { // don't block on buffered channels select { - case subscription.out <- Message{msg, tags}: + case subscription.out <- NewMessage(msg, events): default: state.remove(clientID, qStr, ErrOutOfCapacity) } diff --git a/libs/pubsub/pubsub_test.go b/libs/pubsub/pubsub_test.go index 74af431f8..d5f61dc07 100644 --- a/libs/pubsub/pubsub_test.go +++ b/libs/pubsub/pubsub_test.go @@ -136,24 +136,75 @@ func TestDifferentClients(t *testing.T) { ctx := context.Background() subscription1, err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Iceman", map[string]string{"tm.events.type": "NewBlock"}) + err = s.PublishWithEvents(ctx, "Iceman", map[string][]string{"tm.events.type": {"NewBlock"}}) require.NoError(t, err) assertReceive(t, "Iceman", subscription1.Out()) subscription2, err := s.Subscribe(ctx, "client-2", query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Ultimo", map[string]string{"tm.events.type": "NewBlock", "abci.account.name": "Igor"}) + err = s.PublishWithEvents(ctx, "Ultimo", map[string][]string{"tm.events.type": {"NewBlock"}, "abci.account.name": {"Igor"}}) require.NoError(t, err) assertReceive(t, "Ultimo", subscription1.Out()) assertReceive(t, "Ultimo", subscription2.Out()) subscription3, err := s.Subscribe(ctx, "client-3", query.MustParse("tm.events.type='NewRoundStep' AND abci.account.name='Igor' AND abci.invoice.number = 10")) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Valeria Richards", map[string]string{"tm.events.type": "NewRoundStep"}) + err = s.PublishWithEvents(ctx, "Valeria Richards", map[string][]string{"tm.events.type": {"NewRoundStep"}}) require.NoError(t, err) assert.Zero(t, len(subscription3.Out())) } +func TestSubscribeDuplicateKeys(t *testing.T) { + ctx := context.Background() + s := pubsub.NewServer() + s.SetLogger(log.TestingLogger()) + require.NoError(t, s.Start()) + defer s.Stop() + + testCases := []struct { + query string + expected interface{} + }{ + { + "withdraw.rewards='17'", + "Iceman", + }, + { + "withdraw.rewards='22'", + "Iceman", + }, + { + "withdraw.rewards='1' AND withdraw.rewards='22'", + "Iceman", + }, + { + "withdraw.rewards='100'", + nil, + }, + } + + for i, tc := range testCases { + sub, err := s.Subscribe(ctx, fmt.Sprintf("client-%d", i), query.MustParse(tc.query)) + require.NoError(t, err) + + err = s.PublishWithEvents( + ctx, + "Iceman", + map[string][]string{ + "transfer.sender": {"foo", "bar", "baz"}, + "withdraw.rewards": {"1", "17", "22"}, + }, + ) + require.NoError(t, err) + + if tc.expected != nil { + assertReceive(t, tc.expected, sub.Out()) + } else { + require.Zero(t, len(sub.Out())) + } + } +} + func TestClientSubscribesTwice(t *testing.T) { s := pubsub.NewServer() s.SetLogger(log.TestingLogger()) @@ -165,7 +216,7 @@ func TestClientSubscribesTwice(t *testing.T) { subscription1, err := s.Subscribe(ctx, clientID, q) require.NoError(t, err) - err = s.PublishWithTags(ctx, "Goblin Queen", map[string]string{"tm.events.type": "NewBlock"}) + err = s.PublishWithEvents(ctx, "Goblin Queen", map[string][]string{"tm.events.type": {"NewBlock"}}) require.NoError(t, err) assertReceive(t, "Goblin Queen", subscription1.Out()) @@ -173,7 +224,7 @@ func TestClientSubscribesTwice(t *testing.T) { require.Error(t, err) require.Nil(t, subscription2) - err = s.PublishWithTags(ctx, "Spider-Man", map[string]string{"tm.events.type": "NewBlock"}) + err = s.PublishWithEvents(ctx, "Spider-Man", map[string][]string{"tm.events.type": {"NewBlock"}}) require.NoError(t, err) assertReceive(t, "Spider-Man", subscription1.Out()) } @@ -312,7 +363,7 @@ func benchmarkNClients(n int, b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - s.PublishWithTags(ctx, "Gamora", map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": string(i)}) + s.PublishWithEvents(ctx, "Gamora", map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {string(i)}}) } } @@ -343,7 +394,7 @@ func benchmarkNClientsOneQuery(n int, b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - s.PublishWithTags(ctx, "Gamora", map[string]string{"abci.Account.Owner": "Ivan", "abci.Invoices.Number": "1"}) + s.PublishWithEvents(ctx, "Gamora", map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {"1"}}) } } diff --git a/libs/pubsub/query/empty.go b/libs/pubsub/query/empty.go index 83271f047..2d7642adc 100644 --- a/libs/pubsub/query/empty.go +++ b/libs/pubsub/query/empty.go @@ -5,7 +5,7 @@ type Empty struct { } // Matches always returns true. -func (Empty) Matches(tags map[string]string) bool { +func (Empty) Matches(tags map[string][]string) bool { return true } diff --git a/libs/pubsub/query/empty_test.go b/libs/pubsub/query/empty_test.go index 141fb9515..3fcd2d728 100644 --- a/libs/pubsub/query/empty_test.go +++ b/libs/pubsub/query/empty_test.go @@ -10,8 +10,8 @@ import ( func TestEmptyQueryMatchesAnything(t *testing.T) { q := query.Empty{} - assert.True(t, q.Matches(map[string]string{})) - assert.True(t, q.Matches(map[string]string{"Asher": "Roth"})) - assert.True(t, q.Matches(map[string]string{"Route": "66"})) - assert.True(t, q.Matches(map[string]string{"Route": "66", "Billy": "Blue"})) + assert.True(t, q.Matches(map[string][]string{})) + assert.True(t, q.Matches(map[string][]string{"Asher": {"Roth"}})) + assert.True(t, q.Matches(map[string][]string{"Route": {"66"}})) + assert.True(t, q.Matches(map[string][]string{"Route": {"66"}, "Billy": {"Blue"}})) } diff --git a/libs/pubsub/query/query.go b/libs/pubsub/query/query.go index 189110a3e..80dbfc056 100644 --- a/libs/pubsub/query/query.go +++ b/libs/pubsub/query/query.go @@ -148,12 +148,14 @@ func (q *Query) Conditions() []Condition { return conditions } -// Matches returns true if the query matches the given set of tags, false otherwise. +// Matches returns true if the query matches against any event in the given set +// of events, false otherwise. For each event, a match exists if the query is +// matched against *any* value in a slice of values. // -// For example, query "name=John" matches tags = {"name": "John"}. More -// examples could be found in parser_test.go and query_test.go. -func (q *Query) Matches(tags map[string]string) bool { - if len(tags) == 0 { +// For example, query "name=John" matches events = {"name": ["John", "Eric"]}. +// More examples could be found in parser_test.go and query_test.go. +func (q *Query) Matches(events map[string][]string) bool { + if len(events) == 0 { return false } @@ -162,7 +164,8 @@ func (q *Query) Matches(tags map[string]string) bool { var tag string var op Operator - // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7") + // tokens must be in the following order: + // tag ("tx.gas") -> operator ("=") -> operand ("7") for _, token := range q.parser.Tokens() { switch token.pegRule { @@ -188,7 +191,7 @@ func (q *Query) Matches(tags map[string]string) bool { // see if the triplet (tag, operator, operand) matches any tag // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" } - if !match(tag, op, reflect.ValueOf(valueWithoutSingleQuotes), tags) { + if !match(tag, op, reflect.ValueOf(valueWithoutSingleQuotes), events) { return false } case rulenumber: @@ -198,7 +201,7 @@ func (q *Query) Matches(tags map[string]string) bool { 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)) } - if !match(tag, op, reflect.ValueOf(value), tags) { + if !match(tag, op, reflect.ValueOf(value), events) { return false } } else { @@ -206,7 +209,7 @@ func (q *Query) Matches(tags map[string]string) bool { if err != nil { panic(fmt.Sprintf("got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", err, number)) } - if !match(tag, op, reflect.ValueOf(value), tags) { + if !match(tag, op, reflect.ValueOf(value), events) { return false } } @@ -215,7 +218,7 @@ func (q *Query) Matches(tags map[string]string) bool { 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])) } - if !match(tag, op, reflect.ValueOf(value), tags) { + if !match(tag, op, reflect.ValueOf(value), events) { return false } case ruledate: @@ -223,7 +226,7 @@ func (q *Query) Matches(tags map[string]string) bool { if err != nil { panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", err, buffer[begin:end])) } - if !match(tag, op, reflect.ValueOf(value), tags) { + if !match(tag, op, reflect.ValueOf(value), events) { return false } } @@ -232,34 +235,53 @@ func (q *Query) Matches(tags map[string]string) bool { return true } -// match returns true if the given triplet (tag, operator, operand) matches any tag. +// match returns true if the given triplet (tag, operator, operand) matches any +// value in an event for that key. // -// First, it looks up the tag in tags and if it finds one, tries to compare the -// value from it to the operand using the operator. +// First, it looks up the key in the events and if it finds one, tries to compare +// all the values from it to the operand using the operator. // -// "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" } -func match(tag string, op Operator, operand reflect.Value, tags map[string]string) bool { +// "tx.gas", "=", "7", {"tx": [{"gas": 7, "ID": "4AE393495334"}]} +func match(tag string, op Operator, operand reflect.Value, events map[string][]string) bool { // look up the tag from the query in tags - value, ok := tags[tag] + values, ok := events[tag] if !ok { return false } + + for _, value := range values { + // return true if any value in the set of the event's values matches + if matchValue(value, op, operand) { + return true + } + } + + return false +} + +// matchValue will attempt to match a string value against an operation an +// operand. A boolean is returned representing the match result. It will panic +// if an error occurs or if the operand is invalid. +func matchValue(value string, op Operator, operand reflect.Value) bool { switch operand.Kind() { case reflect.Struct: // time operandAsTime := operand.Interface().(time.Time) + // 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)) + panic(fmt.Sprintf("failed to convert value %v from tag to time.Time: %v", value, err)) } + switch op { case OpLessEqual: return v.Before(operandAsTime) || v.Equal(operandAsTime) @@ -272,14 +294,17 @@ func match(tag string, op Operator, operand reflect.Value, tags map[string]strin case OpEqual: return v.Equal(operandAsTime) } + case reflect.Float64: operandFloat64 := operand.Interface().(float64) var v float64 + // try our best to convert value from tags to float64 v, err := strconv.ParseFloat(value, 64) if err != nil { - panic(fmt.Sprintf("Failed to convert value %v from tag to float64: %v", value, err)) + panic(fmt.Sprintf("failed to convert value %v from tag to float64: %v", value, err)) } + switch op { case OpLessEqual: return v <= operandFloat64 @@ -292,6 +317,7 @@ func match(tag string, op Operator, operand reflect.Value, tags map[string]strin case OpEqual: return v == operandFloat64 } + case reflect.Int64: operandInt := operand.Interface().(int64) var v int64 @@ -299,7 +325,7 @@ func match(tag string, op Operator, operand reflect.Value, tags map[string]strin 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)) + panic(fmt.Sprintf("failed to convert value %v from tag to float64: %v", value, err)) } v = int64(v1) } else { @@ -307,7 +333,7 @@ func match(tag string, op Operator, operand reflect.Value, tags map[string]strin // 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)) + panic(fmt.Sprintf("failed to convert value %v from tag to int64: %v", value, err)) } } switch op { @@ -322,6 +348,7 @@ func match(tag string, op Operator, operand reflect.Value, tags map[string]strin case OpEqual: return v == operandInt } + case reflect.String: switch op { case OpEqual: @@ -329,8 +356,9 @@ func match(tag string, op Operator, operand reflect.Value, tags map[string]strin case OpContains: return strings.Contains(value, operand.String()) } + default: - panic(fmt.Sprintf("Unknown kind of operand %v", operand.Kind())) + panic(fmt.Sprintf("unknown kind of operand %v", operand.Kind())) } return false diff --git a/libs/pubsub/query/query_test.go b/libs/pubsub/query/query_test.go index a3d83b259..10dc3d221 100644 --- a/libs/pubsub/query/query_test.go +++ b/libs/pubsub/query/query_test.go @@ -19,30 +19,40 @@ func TestMatches(t *testing.T) { testCases := []struct { s string - tags map[string]string + events map[string][]string err bool matches bool }{ - {"tm.events.type='NewBlock'", map[string]string{"tm.events.type": "NewBlock"}, false, true}, + {"tm.events.type='NewBlock'", map[string][]string{"tm.events.type": {"NewBlock"}}, false, true}, - {"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.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]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.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]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}, + {"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]string{"abci.owner.name": "Igor,Ivan"}, false, true}, - {"abci.owner.name CONTAINS 'Igor'", map[string]string{"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}, + + {"abci.owner.name = 'Igor'", map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, false, true}, + {"abci.owner.name = 'Ivan'", map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, false, true}, + {"abci.owner.name = 'Ivan' AND abci.owner.name = 'Igor'", map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, false, true}, + {"abci.owner.name = 'Ivan' AND abci.owner.name = 'John'", map[string][]string{"abci.owner.name": {"Igor", "Ivan"}}, false, false}, + + {"tm.events.type='NewBlock'", map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, false, true}, + {"app.name = 'fuzzed'", map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, false, true}, + {"tm.events.type='NewBlock' AND app.name = 'fuzzed'", map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, false, true}, + {"tm.events.type='NewHeader' AND app.name = 'fuzzed'", map[string][]string{"tm.events.type": {"NewBlock"}, "app.name": {"fuzzed"}}, false, false}, } for _, tc := range testCases { @@ -51,10 +61,12 @@ func TestMatches(t *testing.T) { require.Nil(t, err) } + require.NotNil(t, q, "Query '%s' should not be nil", tc.s) + if tc.matches { - assert.True(t, q.Matches(tc.tags), "Query '%s' should match %v", tc.s, tc.tags) + assert.True(t, q.Matches(tc.events), "Query '%s' should match %v", tc.s, tc.events) } else { - assert.False(t, q.Matches(tc.tags), "Query '%s' should not match %v", tc.s, tc.tags) + assert.False(t, q.Matches(tc.events), "Query '%s' should not match %v", tc.s, tc.events) } } } diff --git a/libs/pubsub/subscription.go b/libs/pubsub/subscription.go index 2660439f5..40c97c9ee 100644 --- a/libs/pubsub/subscription.go +++ b/libs/pubsub/subscription.go @@ -70,12 +70,12 @@ func (s *Subscription) cancel(err error) { // Message glues data and tags together. type Message struct { - data interface{} - tags map[string]string + data interface{} + events map[string][]string } -func NewMessage(data interface{}, tags map[string]string) Message { - return Message{data, tags} +func NewMessage(data interface{}, events map[string][]string) Message { + return Message{data, events} } // Data returns an original data published. @@ -83,7 +83,7 @@ func (msg Message) Data() interface{} { return msg.data } -// Tags returns tags, which matched the client's query. -func (msg Message) Tags() map[string]string { - return msg.tags +// Events returns events, which matched the client's query. +func (msg Message) Events() map[string][]string { + return msg.events } diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go index d57ced311..161f44fdf 100644 --- a/rpc/client/localclient.go +++ b/rpc/client/localclient.go @@ -182,7 +182,7 @@ func (c *Local) eventsRoutine(sub types.Subscription, subscriber string, q tmpub for { select { case msg := <-sub.Out(): - result := ctypes.ResultEvent{Query: q.String(), Data: msg.Data(), Tags: msg.Tags()} + result := ctypes.ResultEvent{Query: q.String(), Data: msg.Data(), Events: msg.Events()} if cap(outc) == 0 { outc <- result } else { diff --git a/rpc/core/events.go b/rpc/core/events.go index 4b05e5ce1..acb90b46f 100644 --- a/rpc/core/events.go +++ b/rpc/core/events.go @@ -22,26 +22,83 @@ import ( // string (escaped with single quotes), number, date or time. // // Examples: -// tm.event = 'NewBlock' # new blocks -// tm.event = 'CompleteProposal' # node got a complete proposal +// tm.event = 'NewBlock' # new blocks +// tm.event = 'CompleteProposal' # node got a complete proposal // tm.event = 'Tx' AND tx.hash = 'XYZ' # single transaction -// tm.event = 'Tx' AND tx.height = 5 # all txs of the fifth block -// tx.height = 5 # all txs of the fifth block +// tm.event = 'Tx' AND tx.height = 5 # all txs of the fifth block +// tx.height = 5 # all txs of the fifth block // // Tendermint provides a few predefined keys: tm.event, tx.hash and tx.height. -// Note for transactions, you can define additional keys by providing tags with +// Note for transactions, you can define additional keys by providing events with // DeliverTx response. // -// DeliverTx{ -// Tags: []*KVPair{ -// "agent.name": "K", -// } -// } +// import ( +// abci "github.com/tendermint/tendermint/abci/types" +// "github.com/tendermint/tendermint/libs/pubsub/query" +// ) // -// tm.event = 'Tx' AND agent.name = 'K' -// tm.event = 'Tx' AND account.created_at >= TIME 2013-05-03T14:45:00Z -// tm.event = 'Tx' AND contract.sign_date = DATE 2017-01-01 -// tm.event = 'Tx' AND account.owner CONTAINS 'Igor' +// abci.ResponseDeliverTx{ +// Events: []abci.Event{ +// { +// Type: "rewards.withdraw", +// Attributes: cmn.KVPairs{ +// cmn.KVPair{Key: []byte("address"), Value: []byte("AddrA")}, +// cmn.KVPair{Key: []byte("source"), Value: []byte("SrcX")}, +// cmn.KVPair{Key: []byte("amount"), Value: []byte("...")}, +// cmn.KVPair{Key: []byte("balance"), Value: []byte("...")}, +// }, +// }, +// { +// Type: "rewards.withdraw", +// Attributes: cmn.KVPairs{ +// cmn.KVPair{Key: []byte("address"), Value: []byte("AddrB")}, +// cmn.KVPair{Key: []byte("source"), Value: []byte("SrcY")}, +// cmn.KVPair{Key: []byte("amount"), Value: []byte("...")}, +// cmn.KVPair{Key: []byte("balance"), Value: []byte("...")}, +// }, +// }, +// { +// Type: "transfer", +// Attributes: cmn.KVPairs{ +// cmn.KVPair{Key: []byte("sender"), Value: []byte("AddrC")}, +// cmn.KVPair{Key: []byte("recipient"), Value: []byte("AddrD")}, +// cmn.KVPair{Key: []byte("amount"), Value: []byte("...")}, +// }, +// }, +// }, +// } +// +// All events are indexed by a composite key of the form {eventType}.{evenAttrKey}. +// In the above examples, the following keys would be indexed: +// - rewards.withdraw.address +// - rewards.withdraw.source +// - rewards.withdraw.amount +// - rewards.withdraw.balance +// - transfer.sender +// - transfer.recipient +// - transfer.amount +// +// Multiple event types with duplicate keys are allowed and are meant to +// categorize unique and distinct events. In the above example, all events +// indexed under the key `rewards.withdraw.address` will have the following +// values stored and queryable: +// +// - AddrA +// - AddrB +// +// To create a query for txs where address AddrA withdrew rewards: +// query.MustParse("tm.event = 'Tx' AND rewards.withdraw.address = 'AddrA'") +// +// To create a query for txs where address AddrA withdrew rewards from source Y: +// query.MustParse("tm.event = 'Tx' AND rewards.withdraw.address = 'AddrA' AND rewards.withdraw.source = 'Y'") +// +// To create a query for txs where AddrA transferred funds: +// query.MustParse("tm.event = 'Tx' AND transfer.sender = 'AddrA'") +// +// The following queries would return no results: +// query.MustParse("tm.event = 'Tx' AND transfer.sender = 'AddrZ'") +// query.MustParse("tm.event = 'Tx' AND rewards.withdraw.address = 'AddrZ'") +// query.MustParse("tm.event = 'Tx' AND rewards.withdraw.source = 'W'") // // See list of all possible events here // https://godoc.org/github.com/tendermint/tendermint/types#pkg-constants @@ -106,8 +163,10 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er if err != nil { return nil, errors.Wrap(err, "failed to parse query") } + subCtx, cancel := context.WithTimeout(ctx.Context(), SubscribeTimeout) defer cancel() + sub, err := eventBus.Subscribe(subCtx, addr, q) if err != nil { return nil, err @@ -117,7 +176,7 @@ func Subscribe(ctx *rpctypes.Context, query string) (*ctypes.ResultSubscribe, er for { select { case msg := <-sub.Out(): - resultEvent := &ctypes.ResultEvent{Query: query, Data: msg.Data(), Tags: msg.Tags()} + resultEvent := &ctypes.ResultEvent{Query: query, Data: msg.Data(), Events: msg.Events()} ctx.WSConn.TryWriteRPCResponse( rpctypes.NewRPCSuccessResponse( ctx.WSConn.Codec(), diff --git a/rpc/core/types/responses.go b/rpc/core/types/responses.go index 74457b38a..f1ae16a39 100644 --- a/rpc/core/types/responses.go +++ b/rpc/core/types/responses.go @@ -205,7 +205,7 @@ type ( // Event data from a subscription type ResultEvent struct { - Query string `json:"query"` - Data types.TMEventData `json:"data"` - Tags map[string]string `json:"tags"` + Query string `json:"query"` + Data types.TMEventData `json:"data"` + Events map[string][]string `json:"events"` } diff --git a/state/execution_test.go b/state/execution_test.go index 465b6b0be..80b442e3d 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -13,7 +13,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" - cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/mock" @@ -455,7 +454,7 @@ func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { } func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { - return abci.ResponseDeliverTx{Tags: []cmn.KVPair{}} + return abci.ResponseDeliverTx{Events: []abci.Event{}} } func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx { diff --git a/state/state_test.go b/state/state_test.go index eddbe255b..c7600cc38 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -93,8 +93,8 @@ func TestABCIResponsesSaveLoad1(t *testing.T) { // Build mock responses. block := makeBlock(state, 2) abciResponses := NewABCIResponses(block) - abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Tags: nil} - abciResponses.DeliverTx[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Tags: nil} + abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Events: nil} + abciResponses.DeliverTx[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Events: nil} abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{ types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10), }} @@ -134,11 +134,13 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { 2: { []*abci.ResponseDeliverTx{ {Code: 383}, - {Data: []byte("Gotcha!"), - Tags: []cmn.KVPair{ - {Key: []byte("a"), Value: []byte("1")}, - {Key: []byte("build"), Value: []byte("stuff")}, - }}, + { + Data: []byte("Gotcha!"), + Events: []abci.Event{ + {Type: "type1", Attributes: []cmn.KVPair{{Key: []byte("a"), Value: []byte("1")}}}, + {Type: "type2", Attributes: []cmn.KVPair{{Key: []byte("build"), Value: []byte("stuff")}}}, + }, + }, }, types.ABCIResults{ {383, nil}, diff --git a/state/txindex/kv/kv.go b/state/txindex/kv/kv.go index 84208b8c1..053d26a71 100644 --- a/state/txindex/kv/kv.go +++ b/state/txindex/kv/kv.go @@ -10,9 +10,9 @@ import ( "time" "github.com/pkg/errors" + cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" - "github.com/tendermint/tendermint/libs/pubsub/query" "github.com/tendermint/tendermint/state/txindex" "github.com/tendermint/tendermint/types" @@ -75,7 +75,10 @@ func (txi *TxIndex) Get(hash []byte) (*types.TxResult, error) { return txResult, nil } -// AddBatch indexes a batch of transactions using the given list of tags. +// AddBatch indexes a batch of transactions using the given list of events. Each +// key that indexed from the tx's events is a composite of the event type and +// the respective attribute's key delimited by a "." (eg. "account.number"). +// Any event with an empty type is not indexed. func (txi *TxIndex) AddBatch(b *txindex.Batch) error { storeBatch := txi.store.NewBatch() defer storeBatch.Close() @@ -83,12 +86,8 @@ func (txi *TxIndex) AddBatch(b *txindex.Batch) error { for _, result := range b.Ops { hash := result.Tx.Hash() - // index tx by tags - for _, tag := range result.Result.Tags { - if txi.indexAllTags || cmn.StringInSlice(string(tag.Key), txi.tagsToIndex) { - storeBatch.Set(keyForTag(tag, result), hash) - } - } + // index tx by events + txi.indexEvents(result, hash, storeBatch) // index tx by height if txi.indexAllTags || cmn.StringInSlice(types.TxHeightKey, txi.tagsToIndex) { @@ -107,19 +106,18 @@ func (txi *TxIndex) AddBatch(b *txindex.Batch) error { return nil } -// Index indexes a single transaction using the given list of tags. +// Index indexes a single transaction using the given list of events. Each key +// that indexed from the tx's events is a composite of the event type and the +// respective attribute's key delimited by a "." (eg. "account.number"). +// Any event with an empty type is not indexed. func (txi *TxIndex) Index(result *types.TxResult) error { b := txi.store.NewBatch() defer b.Close() hash := result.Tx.Hash() - // index tx by tags - for _, tag := range result.Result.Tags { - if txi.indexAllTags || cmn.StringInSlice(string(tag.Key), txi.tagsToIndex) { - b.Set(keyForTag(tag, result), hash) - } - } + // index tx by events + txi.indexEvents(result, hash, b) // index tx by height if txi.indexAllTags || cmn.StringInSlice(types.TxHeightKey, txi.tagsToIndex) { @@ -131,12 +129,33 @@ func (txi *TxIndex) Index(result *types.TxResult) error { if err != nil { return err } - b.Set(hash, rawBytes) + b.Set(hash, rawBytes) b.Write() + return nil } +func (txi *TxIndex) indexEvents(result *types.TxResult, hash []byte, store dbm.SetDeleter) { + for _, event := range result.Result.Events { + // only index events with a non-empty type + if len(event.Type) == 0 { + continue + } + + for _, attr := range event.Attributes { + if len(attr.Key) == 0 { + continue + } + + compositeTag := fmt.Sprintf("%s.%s", event.Type, string(attr.Key)) + if txi.indexAllTags || cmn.StringInSlice(compositeTag, txi.tagsToIndex) { + store.Set(keyForEvent(compositeTag, attr.Value, result), hash) + } + } + } +} + // Search performs a search using the given query. It breaks the query into // conditions (like "tx.height > 5"). For each condition, it queries the DB // index. One special use cases here: (1) if "tx.hash" is found, it returns tx @@ -343,7 +362,7 @@ func (txi *TxIndex) match(c query.Condition, startKeyBz []byte) (hashes [][]byte } } else if c.Op == query.OpContains { // XXX: startKey does not apply here. - // For example, if startKey = "account.owner/an/" and search query = "accoutn.owner CONTAINS an" + // For example, if startKey = "account.owner/an/" and search query = "account.owner CONTAINS an" // we can't iterate with prefix "account.owner/an/" because we might miss keys like "account.owner/Ulan/" it := dbm.IteratePrefix(txi.store, startKey(c.Tag)) defer it.Close() @@ -420,10 +439,10 @@ func extractValueFromKey(key []byte) string { return parts[1] } -func keyForTag(tag cmn.KVPair, result *types.TxResult) []byte { +func keyForEvent(key string, value []byte, result *types.TxResult) []byte { return []byte(fmt.Sprintf("%s/%s/%d/%d", - tag.Key, - tag.Value, + key, + value, result.Height, result.Index, )) diff --git a/state/txindex/kv/kv_test.go b/state/txindex/kv/kv_test.go index b726a423c..cacfaad01 100644 --- a/state/txindex/kv/kv_test.go +++ b/state/txindex/kv/kv_test.go @@ -21,7 +21,15 @@ func TestTxIndex(t *testing.T) { indexer := NewTxIndex(db.NewMemDB()) tx := types.Tx("HELLO WORLD") - txResult := &types.TxResult{1, 0, tx, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: nil}} + txResult := &types.TxResult{ + Height: 1, + Index: 0, + Tx: tx, + Result: abci.ResponseDeliverTx{ + Data: []byte{0}, + Code: abci.CodeTypeOK, Log: "", Events: nil, + }, + } hash := tx.Hash() batch := txindex.NewBatch(1) @@ -36,7 +44,15 @@ func TestTxIndex(t *testing.T) { assert.Equal(t, txResult, loadedTxResult) tx2 := types.Tx("BYE BYE WORLD") - txResult2 := &types.TxResult{1, 0, tx2, abci.ResponseDeliverTx{Data: []byte{0}, Code: abci.CodeTypeOK, Log: "", Tags: nil}} + txResult2 := &types.TxResult{ + Height: 1, + Index: 0, + Tx: tx2, + Result: abci.ResponseDeliverTx{ + Data: []byte{0}, + Code: abci.CodeTypeOK, Log: "", Events: nil, + }, + } hash2 := tx2.Hash() err = indexer.Index(txResult2) @@ -51,10 +67,10 @@ func TestTxSearch(t *testing.T) { allowedTags := []string{"account.number", "account.owner", "account.date"} indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags)) - txResult := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.number"), Value: []byte("1")}, - {Key: []byte("account.owner"), Value: []byte("Ivan")}, - {Key: []byte("not_allowed"), Value: []byte("Vlad")}, + txResult := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("1")}}}, + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("owner"), Value: []byte("Ivan")}}}, + {Type: "", Attributes: []cmn.KVPair{{Key: []byte("not_allowed"), Value: []byte("Vlad")}}}, }) hash := txResult.Tx.Hash() @@ -108,13 +124,82 @@ func TestTxSearch(t *testing.T) { } } +func TestTxSearchDeprecatedIndexing(t *testing.T) { + allowedTags := []string{"account.number", "sender"} + indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags)) + + // index tx using events indexing (composite key) + txResult1 := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("1")}}}, + }) + hash1 := txResult1.Tx.Hash() + + err := indexer.Index(txResult1) + require.NoError(t, err) + + // index tx also using deprecated indexing (tag as key) + txResult2 := txResultWithEvents(nil) + txResult2.Tx = types.Tx("HELLO WORLD 2") + + hash2 := txResult2.Tx.Hash() + b := indexer.store.NewBatch() + + rawBytes, err := cdc.MarshalBinaryBare(txResult2) + require.NoError(t, err) + + depKey := []byte(fmt.Sprintf("%s/%s/%d/%d", + "sender", + "addr1", + txResult2.Height, + txResult2.Index, + )) + + b.Set(depKey, hash2) + b.Set(keyForHeight(txResult2), hash2) + b.Set(hash2, rawBytes) + b.Write() + + testCases := []struct { + q string + results []*types.TxResult + }{ + // search by hash + {fmt.Sprintf("tx.hash = '%X'", hash1), []*types.TxResult{txResult1}}, + // search by hash + {fmt.Sprintf("tx.hash = '%X'", hash2), []*types.TxResult{txResult2}}, + // search by exact match (one tag) + {"account.number = 1", []*types.TxResult{txResult1}}, + {"account.number >= 1 AND account.number <= 5", []*types.TxResult{txResult1}}, + // search by range (lower bound) + {"account.number >= 1", []*types.TxResult{txResult1}}, + // search by range (upper bound) + {"account.number <= 5", []*types.TxResult{txResult1}}, + // search using not allowed tag + {"not_allowed = 'boom'", []*types.TxResult{}}, + // search for not existing tx result + {"account.number >= 2 AND account.number <= 5", []*types.TxResult{}}, + // search using not existing tag + {"account.date >= TIME 2013-05-03T14:45:00Z", []*types.TxResult{}}, + // search by deprecated tag + {"sender = 'addr1'", []*types.TxResult{txResult2}}, + } + + for _, tc := range testCases { + t.Run(tc.q, func(t *testing.T) { + results, err := indexer.Search(query.MustParse(tc.q)) + require.NoError(t, err) + require.Equal(t, results, tc.results) + }) + } +} + func TestTxSearchOneTxWithMultipleSameTagsButDifferentValues(t *testing.T) { allowedTags := []string{"account.number"} indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags)) - txResult := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.number"), Value: []byte("1")}, - {Key: []byte("account.number"), Value: []byte("2")}, + txResult := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("1")}}}, + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("2")}}}, }) err := indexer.Index(txResult) @@ -132,9 +217,10 @@ func TestTxSearchMultipleTxs(t *testing.T) { indexer := NewTxIndex(db.NewMemDB(), IndexTags(allowedTags)) // indexed first, but bigger height (to test the order of transactions) - txResult := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.number"), Value: []byte("1")}, + txResult := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("1")}}}, }) + txResult.Tx = types.Tx("Bob's account") txResult.Height = 2 txResult.Index = 1 @@ -142,8 +228,8 @@ func TestTxSearchMultipleTxs(t *testing.T) { require.NoError(t, err) // indexed second, but smaller height (to test the order of transactions) - txResult2 := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.number"), Value: []byte("2")}, + txResult2 := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("2")}}}, }) txResult2.Tx = types.Tx("Alice's account") txResult2.Height = 1 @@ -153,8 +239,8 @@ func TestTxSearchMultipleTxs(t *testing.T) { require.NoError(t, err) // indexed third (to test the order of transactions) - txResult3 := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.number"), Value: []byte("3")}, + txResult3 := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("3")}}}, }) txResult3.Tx = types.Tx("Jack's account") txResult3.Height = 1 @@ -164,8 +250,8 @@ func TestTxSearchMultipleTxs(t *testing.T) { // indexed fourth (to test we don't include txs with similar tags) // https://github.com/tendermint/tendermint/issues/2908 - txResult4 := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.number.id"), Value: []byte("1")}, + txResult4 := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number.id"), Value: []byte("1")}}}, }) txResult4.Tx = types.Tx("Mike's account") txResult4.Height = 2 @@ -183,9 +269,9 @@ func TestTxSearchMultipleTxs(t *testing.T) { func TestIndexAllTags(t *testing.T) { indexer := NewTxIndex(db.NewMemDB(), IndexAllTags()) - txResult := txResultWithTags([]cmn.KVPair{ - {Key: []byte("account.owner"), Value: []byte("Ivan")}, - {Key: []byte("account.number"), Value: []byte("1")}, + txResult := txResultWithEvents([]abci.Event{ + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("owner"), Value: []byte("Ivan")}}}, + {Type: "account", Attributes: []cmn.KVPair{{Key: []byte("number"), Value: []byte("1")}}}, }) err := indexer.Index(txResult) @@ -202,17 +288,17 @@ func TestIndexAllTags(t *testing.T) { assert.Equal(t, []*types.TxResult{txResult}, results) } -func txResultWithTags(tags []cmn.KVPair) *types.TxResult { +func txResultWithEvents(events []abci.Event) *types.TxResult { tx := types.Tx("HELLO WORLD") return &types.TxResult{ Height: 1, Index: 0, Tx: tx, Result: abci.ResponseDeliverTx{ - Data: []byte{0}, - Code: abci.CodeTypeOK, - Log: "", - Tags: tags, + Data: []byte{0}, + Code: abci.CodeTypeOK, + Log: "", + Events: events, }, } } @@ -236,10 +322,10 @@ func benchmarkTxIndex(txsCount int64, b *testing.B) { Index: txIndex, Tx: tx, Result: abci.ResponseDeliverTx{ - Data: []byte{0}, - Code: abci.CodeTypeOK, - Log: "", - Tags: []cmn.KVPair{}, + Data: []byte{0}, + Code: abci.CodeTypeOK, + Log: "", + Events: []abci.Event{}, }, } if err := batch.Add(txResult); err != nil { diff --git a/types/event_bus.go b/types/event_bus.go index da959090f..9c50d6430 100644 --- a/types/event_bus.go +++ b/types/event_bus.go @@ -4,6 +4,7 @@ import ( "context" "fmt" + "github.com/tendermint/tendermint/abci/types" cmn "github.com/tendermint/tendermint/libs/common" "github.com/tendermint/tendermint/libs/log" tmpubsub "github.com/tendermint/tendermint/libs/pubsub" @@ -90,20 +91,32 @@ 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, map[string]string{EventTypeKey: eventType}) - return nil + return b.pubsub.PublishWithEvents(ctx, eventData, map[string][]string{EventTypeKey: {eventType}}) } -func (b *EventBus) validateAndStringifyTags(tags []cmn.KVPair, logger log.Logger) map[string]string { - result := make(map[string]string) - for _, tag := range tags { - // basic validation - if len(tag.Key) == 0 { - logger.Debug("Got tag with an empty key (skipping)", "tag", tag) +// validateAndStringifyEvents takes a slice of event objects and creates a +// map of stringified events where each key is composed of the event +// type and each of the event's attributes keys in the form of +// "{event.Type}.{attribute.Key}" and the value is each attribute's value. +func (b *EventBus) validateAndStringifyEvents(events []types.Event, logger log.Logger) map[string][]string { + result := make(map[string][]string) + for _, event := range events { + if len(event.Type) == 0 { + logger.Debug("Got an event with an empty type (skipping)", "event", event) continue } - result[string(tag.Key)] = string(tag.Value) + + for _, attr := range event.Attributes { + if len(attr.Key) == 0 { + logger.Debug("Got an event attribute with an empty key(skipping)", "event", event) + continue + } + + compositeTag := fmt.Sprintf("%s.%s", event.Type, string(attr.Key)) + result[compositeTag] = append(result[compositeTag], string(attr.Value)) + } } + return result } @@ -111,14 +124,13 @@ func (b *EventBus) PublishEventNewBlock(data EventDataNewBlock) error { // no explicit deadline for publishing events ctx := context.Background() - resultTags := append(data.ResultBeginBlock.Tags, data.ResultEndBlock.Tags...) - tags := b.validateAndStringifyTags(resultTags, b.Logger.With("block", data.Block.StringShort())) + resultEvents := append(data.ResultBeginBlock.Events, data.ResultEndBlock.Events...) + events := b.validateAndStringifyEvents(resultEvents, b.Logger.With("block", data.Block.StringShort())) - // add predefined tags - logIfTagExists(EventTypeKey, tags, b.Logger) - tags[EventTypeKey] = EventNewBlock + // add predefined new block event + events[EventTypeKey] = append(events[EventTypeKey], EventNewBlock) - b.pubsub.PublishWithTags(ctx, data, tags) + _ = b.pubsub.PublishWithEvents(ctx, data, events) return nil } @@ -126,16 +138,14 @@ func (b *EventBus) PublishEventNewBlockHeader(data EventDataNewBlockHeader) erro // no explicit deadline for publishing events ctx := context.Background() - resultTags := append(data.ResultBeginBlock.Tags, data.ResultEndBlock.Tags...) + resultTags := append(data.ResultBeginBlock.Events, data.ResultEndBlock.Events...) // TODO: Create StringShort method for Header and use it in logger. - tags := b.validateAndStringifyTags(resultTags, b.Logger.With("header", data.Header)) + events := b.validateAndStringifyEvents(resultTags, b.Logger.With("header", data.Header)) - // add predefined tags - logIfTagExists(EventTypeKey, tags, b.Logger) - tags[EventTypeKey] = EventNewBlockHeader + // add predefined new block header event + events[EventTypeKey] = append(events[EventTypeKey], EventNewBlockHeader) - b.pubsub.PublishWithTags(ctx, data, tags) - return nil + return b.pubsub.PublishWithEvents(ctx, data, events) } func (b *EventBus) PublishEventVote(data EventDataVote) error { @@ -153,19 +163,14 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error { // no explicit deadline for publishing events ctx := context.Background() - tags := b.validateAndStringifyTags(data.Result.Tags, b.Logger.With("tx", data.Tx)) + events := b.validateAndStringifyEvents(data.Result.Events, b.Logger.With("tx", data.Tx)) // add predefined tags - logIfTagExists(EventTypeKey, tags, b.Logger) - tags[EventTypeKey] = EventTx + events[EventTypeKey] = append(events[EventTypeKey], EventTx) + events[TxHashKey] = append(events[TxHashKey], fmt.Sprintf("%X", data.Tx.Hash())) + events[TxHeightKey] = append(events[TxHeightKey], fmt.Sprintf("%d", data.Height)) - logIfTagExists(TxHashKey, tags, b.Logger) - tags[TxHashKey] = fmt.Sprintf("%X", data.Tx.Hash()) - - logIfTagExists(TxHeightKey, tags, b.Logger) - tags[TxHeightKey] = fmt.Sprintf("%d", data.Height) - - b.pubsub.PublishWithTags(ctx, data, tags) + _ = b.pubsub.PublishWithEvents(ctx, data, events) return nil } @@ -209,12 +214,6 @@ func (b *EventBus) PublishEventValidatorSetUpdates(data EventDataValidatorSetUpd return b.Publish(EventValidatorSetUpdates, data) } -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) - } -} - //----------------------------------------------------------------------------- type NopEventBus struct{} diff --git a/types/event_bus_test.go b/types/event_bus_test.go index 508b423a6..45590217f 100644 --- a/types/event_bus_test.go +++ b/types/event_bus_test.go @@ -22,10 +22,15 @@ func TestEventBusPublishEventTx(t *testing.T) { defer eventBus.Stop() tx := Tx("foo") - result := abci.ResponseDeliverTx{Data: []byte("bar"), Tags: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}} + result := abci.ResponseDeliverTx{ + Data: []byte("bar"), + Events: []abci.Event{ + {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}}, + }, + } // 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' AND baz=1", tx.Hash()) + query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X' AND testType.baz=1", tx.Hash()) txsSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) require.NoError(t, err) @@ -62,11 +67,19 @@ func TestEventBusPublishEventNewBlock(t *testing.T) { defer eventBus.Stop() block := MakeBlock(0, []Tx{}, nil, []Evidence{}) - resultBeginBlock := abci.ResponseBeginBlock{Tags: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}} - resultEndBlock := abci.ResponseEndBlock{Tags: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}} + resultBeginBlock := abci.ResponseBeginBlock{ + Events: []abci.Event{ + {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}}, + }, + } + resultEndBlock := abci.ResponseEndBlock{ + Events: []abci.Event{ + {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}}, + }, + } // PublishEventNewBlock adds the tm.event tag, so the query below should work - query := "tm.event='NewBlock' AND baz=1 AND foz=2" + query := "tm.event='NewBlock' AND testType.baz=1 AND testType.foz=2" blocksSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) require.NoError(t, err) @@ -94,6 +107,106 @@ func TestEventBusPublishEventNewBlock(t *testing.T) { } } +func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) { + eventBus := NewEventBus() + err := eventBus.Start() + require.NoError(t, err) + defer eventBus.Stop() + + tx := Tx("foo") + result := abci.ResponseDeliverTx{ + Data: []byte("bar"), + Events: []abci.Event{ + { + Type: "transfer", + Attributes: []cmn.KVPair{ + {Key: []byte("sender"), Value: []byte("foo")}, + {Key: []byte("recipient"), Value: []byte("bar")}, + {Key: []byte("amount"), Value: []byte("5")}, + }, + }, + { + Type: "transfer", + Attributes: []cmn.KVPair{ + {Key: []byte("sender"), Value: []byte("baz")}, + {Key: []byte("recipient"), Value: []byte("cat")}, + {Key: []byte("amount"), Value: []byte("13")}, + }, + }, + { + Type: "withdraw.rewards", + Attributes: []cmn.KVPair{ + {Key: []byte("address"), Value: []byte("bar")}, + {Key: []byte("source"), Value: []byte("iceman")}, + {Key: []byte("amount"), Value: []byte("33")}, + }, + }, + }, + } + + testCases := []struct { + query string + expectResults bool + }{ + { + "tm.event='Tx' AND tx.height=1 AND transfer.sender='DoesNotExist'", + false, + }, + { + "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo'", + true, + }, + { + "tm.event='Tx' AND tx.height=1 AND transfer.sender='baz'", + true, + }, + { + "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='baz'", + true, + }, + { + "tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='DoesNotExist'", + false, + }, + } + + for i, tc := range testCases { + sub, err := eventBus.Subscribe(context.Background(), fmt.Sprintf("client-%d", i), tmquery.MustParse(tc.query)) + require.NoError(t, err) + + done := make(chan struct{}) + + go func() { + msg := <-sub.Out() + data := msg.Data().(EventDataTx) + assert.Equal(t, int64(1), data.Height) + assert.Equal(t, uint32(0), data.Index) + assert.Equal(t, tx, data.Tx) + assert.Equal(t, result, data.Result) + close(done) + }() + + err = eventBus.PublishEventTx(EventDataTx{TxResult{ + Height: 1, + Index: 0, + Tx: tx, + Result: result, + }}) + assert.NoError(t, err) + + select { + case <-done: + if !tc.expectResults { + require.Fail(t, "unexpected transaction result(s) from subscription") + } + case <-time.After(1 * time.Second): + if tc.expectResults { + require.Fail(t, "failed to receive a transaction after 1 second") + } + } + } +} + func TestEventBusPublishEventNewBlockHeader(t *testing.T) { eventBus := NewEventBus() err := eventBus.Start() @@ -101,11 +214,19 @@ func TestEventBusPublishEventNewBlockHeader(t *testing.T) { defer eventBus.Stop() block := MakeBlock(0, []Tx{}, nil, []Evidence{}) - resultBeginBlock := abci.ResponseBeginBlock{Tags: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}} - resultEndBlock := abci.ResponseEndBlock{Tags: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}} + resultBeginBlock := abci.ResponseBeginBlock{ + Events: []abci.Event{ + {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}}, + }, + } + resultEndBlock := abci.ResponseEndBlock{ + Events: []abci.Event{ + {Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}}, + }, + } // PublishEventNewBlockHeader adds the tm.event tag, so the query below should work - query := "tm.event='NewBlockHeader' AND baz=1 AND foz=2" + query := "tm.event='NewBlockHeader' AND testType.baz=1 AND testType.foz=2" headersSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query)) require.NoError(t, err) From 9010ff5f964447e89edd921866bbec9efb3cf753 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 12 Jun 2019 15:25:47 +0200 Subject: [PATCH 230/259] types: do not ignore errors returned by PublishWithEvents (#3722) Follow up to #3643 * update changelog * do not ignore errors returned by PublishWithEvents --- CHANGELOG_PENDING.md | 36 +++++++++++++++++------------------- types/event_bus.go | 6 ++---- 2 files changed, 19 insertions(+), 23 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index c23dd76b9..b1b09e20c 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -4,26 +4,25 @@ ### BREAKING CHANGES: -- \#3613 Switch from golang/dep to Go 1.11 Modules to resolve dependencies: - - it is recommended to switch to Go Modules if your project has tendermint - as a dependency - - read more on Modules here: https://github.com/golang/go/wiki/Modules - * CLI/RPC/Config - * [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` -> - `results.deliver_tx`). See docs for details. + - [cli] \#3613 Switch from golang/dep to Go Modules to resolve dependencies: + It is recommended to switch to Go Modules if your project has tendermint as + a dependency. Read more on Modules here: + https://github.com/golang/go/wiki/Modules + - [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` + -> `results.deliver_tx`). See docs for details. * Apps - * [abci] \#1859 `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`, - and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event` - contains a `type` and a list of `attributes` (list of key-value pairs) allowing - for inclusion of multiple distinct events in each response. + - [abci] \#1859 `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`, + and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event` + contains a `type` and a list of `attributes` (list of key-value pairs) + allowing for inclusion of multiple distinct events in each response. * Go API - * [libs/db] Removed deprecated `LevelDBBackend` const - * If you have `db_backend` set to `leveldb` in your config file, please + - [libs/db] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed deprecated `LevelDBBackend` const + If you have `db_backend` set to `leveldb` in your config file, please change it to `goleveldb` or `cleveldb`. -- [p2p] \#3521 Remove NewNetAddressStringWithOptionalID + - [p2p] \#3521 Remove NewNetAddressStringWithOptionalID * Blockchain Protocol @@ -32,10 +31,9 @@ ### FEATURES: ### IMPROVEMENTS: -- [p2p] \#3666 Add per channel telemtry to improve reactor observability - -* [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. +- [p2p] \#3666 Add per channel telemetry to improve reactor observability +- [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) ### BUG FIXES: -- [libs/db] Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) -- [libs/db] Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) +- [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) +- [libs/db] \#3718 Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) diff --git a/types/event_bus.go b/types/event_bus.go index 9c50d6430..b91340c72 100644 --- a/types/event_bus.go +++ b/types/event_bus.go @@ -130,8 +130,7 @@ func (b *EventBus) PublishEventNewBlock(data EventDataNewBlock) error { // add predefined new block event events[EventTypeKey] = append(events[EventTypeKey], EventNewBlock) - _ = b.pubsub.PublishWithEvents(ctx, data, events) - return nil + return b.pubsub.PublishWithEvents(ctx, data, events) } func (b *EventBus) PublishEventNewBlockHeader(data EventDataNewBlockHeader) error { @@ -170,8 +169,7 @@ func (b *EventBus) PublishEventTx(data EventDataTx) error { events[TxHashKey] = append(events[TxHashKey], fmt.Sprintf("%X", data.Tx.Hash())) events[TxHeightKey] = append(events[TxHeightKey], fmt.Sprintf("%d", data.Height)) - _ = b.pubsub.PublishWithEvents(ctx, data, events) - return nil + return b.pubsub.PublishWithEvents(ctx, data, events) } func (b *EventBus) PublishEventNewRoundStep(data EventDataRoundState) error { From 0e1c492d3ed1429afe87f9ea651753878821b341 Mon Sep 17 00:00:00 2001 From: Andy Nogueira <45477427+andynog@users.noreply.github.com> Date: Mon, 17 Jun 2019 06:51:12 -0400 Subject: [PATCH 231/259] docs: missing 'b' in python command (#3728) In Python 3 the command outlined in this doc `import codecs; codecs.decode("YWJjZA==", 'base64').decode('ascii')` throws an error: TypeError: decoding with 'base64' codec failed (TypeError: expected bytes-like object, not str), needs to add 'b' before the encoded string `import codecs; codecs.decode(b"YWJjZA==", 'base64').decode('ascii')` to make it work --- docs/app-dev/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/app-dev/getting-started.md b/docs/app-dev/getting-started.md index 9110761e2..eff70db68 100644 --- a/docs/app-dev/getting-started.md +++ b/docs/app-dev/getting-started.md @@ -137,7 +137,7 @@ The result should look like: Note the `value` in the result (`YWJjZA==`); this is the base64-encoding of the ASCII of `abcd`. You can verify this in a python 2 shell by running `"YWJjZA==".decode('base64')` or in python 3 shell by running -`import codecs; codecs.decode("YWJjZA==", 'base64').decode('ascii')`. +`import codecs; codecs.decode(b"YWJjZA==", 'base64').decode('ascii')`. Stay tuned for a future release that [makes this output more human-readable](https://github.com/tendermint/tendermint/issues/1794). From ed18ffdca3876ecc962bbfefe6f50d89fc2f1f88 Mon Sep 17 00:00:00 2001 From: Runchao Han Date: Mon, 17 Jun 2019 21:30:12 +1000 Subject: [PATCH 232/259] p2p: refactor Switch#OnStop (#3729) --- node/node.go | 1 - p2p/switch.go | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/node/node.go b/node/node.go index 46185847e..6f2cad1e5 100644 --- a/node/node.go +++ b/node/node.go @@ -714,7 +714,6 @@ func (n *Node) OnStop() { n.indexerService.Stop() // now stop the reactors - // TODO: gracefully disconnect from peers. n.sw.Stop() // stop mempool WAL diff --git a/p2p/switch.go b/p2p/switch.go index 31e0aa6e1..7e681d67c 100644 --- a/p2p/switch.go +++ b/p2p/switch.go @@ -221,11 +221,7 @@ func (sw *Switch) OnStart() error { func (sw *Switch) OnStop() { // Stop peers for _, p := range sw.peers.List() { - sw.transport.Cleanup(p) - p.Stop() - if sw.peers.Remove(p) { - sw.metrics.Peers.Add(float64(-1)) - } + sw.stopAndRemovePeer(p, nil) } // Stop reactors From 60827f75623b92eff132dc0eff5b49d2025c591e Mon Sep 17 00:00:00 2001 From: Hans Schoenburg Date: Wed, 19 Jun 2019 20:35:53 +0200 Subject: [PATCH 233/259] docs: fix some language issues and deprecated link (#3733) --- docs/.vuepress/config.js | 2 +- docs/app-dev/app-development.md | 6 +++--- docs/spec/abci/abci.md | 2 +- docs/spec/blockchain/encoding.md | 2 +- docs/spec/blockchain/state.md | 2 +- docs/tendermint-core/using-tendermint.md | 4 +++- 6 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 0b54d2011..9adfc5953 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -44,7 +44,7 @@ module.exports = { "/app-dev/app-development", "/app-dev/subscribing-to-events-via-websocket", "/app-dev/indexing-transactions", - "/app-dev/abci-spec", + "/spec/abci/abci", "/app-dev/ecosystem" ] }, diff --git a/docs/app-dev/app-development.md b/docs/app-dev/app-development.md index d157ce378..1b4e26800 100644 --- a/docs/app-dev/app-development.md +++ b/docs/app-dev/app-development.md @@ -133,8 +133,8 @@ the mempool. If Tendermint is just started or the clients sent more than 100k transactions, old transactions may be sent to the application. So it is important CheckTx implements some logic to handle them. -There are cases where a transaction will (or may) become valid in some -future state, in which case you probably want to disable Tendermint's +If there are cases in your application where a transaction may become invalid in some +future state, you probably want to disable Tendermint's cache. You can do that by setting `[mempool] cache_size = 0` in the config. @@ -205,7 +205,7 @@ Once all processing of the block is complete, Tendermint sends the Commit request and blocks waiting for a response. While the mempool may run concurrently with block processing (the BeginBlock, DeliverTxs, and EndBlock), it is locked for the Commit request so that its state can be -safely reset during Commit. This means the app _MUST NOT_ do any +safely updated during Commit. This means the app _MUST NOT_ do any blocking communication with the mempool (ie. broadcast_tx) during Commit, or there will be deadlock. Note also that all remaining transactions in the mempool are replayed on the mempool connection diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index 6dc00b5b9..b7b2e09fe 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -141,7 +141,7 @@ on them. All other fields in the `Response*` must be strictly deterministic. ## Block Execution The first time a new blockchain is started, Tendermint calls -`InitChain`. From then on, the follow sequence of methods is executed for each +`InitChain`. From then on, the following sequence of methods is executed for each block: `BeginBlock, [DeliverTx], EndBlock, Commit` diff --git a/docs/spec/blockchain/encoding.md b/docs/spec/blockchain/encoding.md index bde580a14..14d0e786b 100644 --- a/docs/spec/blockchain/encoding.md +++ b/docs/spec/blockchain/encoding.md @@ -218,7 +218,7 @@ func MerkleRoot(items [][]byte) []byte{ case 0: return nil case 1: - return leafHash(leafs[0]) + return leafHash(items[0]) default: k := getSplitPoint(len(items)) left := MerkleRoot(items[:k]) diff --git a/docs/spec/blockchain/state.md b/docs/spec/blockchain/state.md index 3ab65e12b..15fc37761 100644 --- a/docs/spec/blockchain/state.md +++ b/docs/spec/blockchain/state.md @@ -59,7 +59,7 @@ type Validator struct { When hashing the Validator struct, the address is not included, because it is redundant with the pubkey. -The `state.Validators`, `state.LastValidators`, and `state.NextValidators`, must always by sorted by validator address, +The `state.Validators`, `state.LastValidators`, and `state.NextValidators`, must always be sorted by validator address, so that there is a canonical order for computing the MerkleRoot. We also define a `TotalVotingPower` function, to return the total voting power: diff --git a/docs/tendermint-core/using-tendermint.md b/docs/tendermint-core/using-tendermint.md index 2ca8c9e92..05d481b2c 100644 --- a/docs/tendermint-core/using-tendermint.md +++ b/docs/tendermint-core/using-tendermint.md @@ -202,8 +202,10 @@ Note that raw hex cannot be used in `POST` transactions. ## Reset -**WARNING: UNSAFE** Only do this in development and only if you can +::: warning +**UNSAFE** Only do this in development and only if you can afford to lose all blockchain data! +::: To reset a blockchain, stop the node and run: From 1b5110e91ffa81eb4387e88d57473ebaaed57ec0 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Fri, 21 Jun 2019 09:30:32 +0400 Subject: [PATCH 234/259] node: fix a bug where `nil` is recorded as node's address (#3740) * node: fix a bug where `nil` is recorded as node's address Solution AddOurAddress when we know it sw.NetAddress is nil in createAddrBookAndSetOnSwitch it's set by n.transport.Listen function, which is called during start Fixes #3716 * use addr instead of n.sw.NetAddress * add both ExternalAddress and ListenAddress as our addresses --- CHANGELOG_PENDING.md | 1 + node/node.go | 24 ++++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index b1b09e20c..63cd05ae2 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -37,3 +37,4 @@ ### BUG FIXES: - [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) - [libs/db] \#3718 Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) +- [node] \#3716 Fix a bug where `nil` is recorded as node's address diff --git a/node/node.go b/node/node.go index 6f2cad1e5..85fef5ee7 100644 --- a/node/node.go +++ b/node/node.go @@ -441,17 +441,30 @@ func createSwitch(config *cfg.Config, } func createAddrBookAndSetOnSwitch(config *cfg.Config, sw *p2p.Switch, - p2pLogger log.Logger) pex.AddrBook { + p2pLogger log.Logger, nodeKey *p2p.NodeKey) (pex.AddrBook, error) { addrBook := pex.NewAddrBook(config.P2P.AddrBookFile(), config.P2P.AddrBookStrict) addrBook.SetLogger(p2pLogger.With("book", config.P2P.AddrBookFile())) // Add ourselves to addrbook to prevent dialing ourselves - addrBook.AddOurAddress(sw.NetAddress()) + if config.P2P.ExternalAddress != "" { + addr, err := p2p.NewNetAddressString(p2p.IDAddressString(nodeKey.ID(), config.P2P.ExternalAddress)) + if err != nil { + return nil, errors.Wrap(err, "p2p.external_address is incorrect") + } + addrBook.AddOurAddress(addr) + } + if config.P2P.ListenAddress != "" { + addr, err := p2p.NewNetAddressString(p2p.IDAddressString(nodeKey.ID(), config.P2P.ListenAddress)) + if err != nil { + return nil, errors.Wrap(err, "p2p.laddr is incorrect") + } + addrBook.AddOurAddress(addr) + } sw.SetAddrBook(addrBook) - return addrBook + return addrBook, nil } func createPEXReactorAndAddToSwitch(addrBook pex.AddrBook, config *cfg.Config, @@ -594,7 +607,10 @@ func NewNode(config *cfg.Config, return nil, errors.Wrap(err, "could not add peers from persistent_peers field") } - addrBook := createAddrBookAndSetOnSwitch(config, sw, p2pLogger) + addrBook, err := createAddrBookAndSetOnSwitch(config, sw, p2pLogger, nodeKey) + if err != nil { + return nil, errors.Wrap(err, "could not create addrbook") + } // Optionally, start the pex reactor // From 9d5ba576eeb11a979bd7aab166edaa22e256fc62 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Fri, 21 Jun 2019 01:56:27 -0400 Subject: [PATCH 235/259] abci: Refactor ABCI CheckTx and DeliverTx signatures (#3735) * Refactor signature of Application.CheckTx * Refactor signature of Application.DeliverTx * Refactor example variable names for clarity and consistency * Rename method variables for consistency * Rename method variables for consistency * add a changelog entry * update docs --- CHANGELOG_PENDING.md | 1 + abci/client/local_client.go | 8 +- abci/example/counter/counter.go | 16 +-- abci/example/kvstore/kvstore.go | 8 +- abci/example/kvstore/kvstore_test.go | 7 +- abci/example/kvstore/persistent_kvstore.go | 12 +- abci/server/socket_server.go | 4 +- abci/types/application.go | 12 +- blockchain/reactor_test.go | 4 +- consensus/mempool_test.go | 10 +- consensus/replay.go | 2 +- docs/app-dev/app-development.md | 139 +++++++++++---------- docs/app-dev/indexing-transactions.md | 2 +- mempool/clist_mempool_test.go | 2 +- rpc/client/mock/abci.go | 12 +- state/execution_test.go | 4 +- 16 files changed, 127 insertions(+), 116 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 63cd05ae2..ed835aebc 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -23,6 +23,7 @@ If you have `db_backend` set to `leveldb` in your config file, please change it to `goleveldb` or `cleveldb`. - [p2p] \#3521 Remove NewNetAddressStringWithOptionalID + - [abci] \#3193 Use RequestDeliverTx and RequestCheckTx in the ABCI interface * Blockchain Protocol diff --git a/abci/client/local_client.go b/abci/client/local_client.go index d0e50c330..4a3e6a5ee 100644 --- a/abci/client/local_client.go +++ b/abci/client/local_client.go @@ -85,7 +85,7 @@ func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.DeliverTx(tx) + res := app.Application.DeliverTx(types.RequestDeliverTx{Tx: tx}) return app.callback( types.ToRequestDeliverTx(tx), types.ToResponseDeliverTx(res), @@ -96,7 +96,7 @@ func (app *localClient) CheckTxAsync(tx []byte) *ReqRes { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.CheckTx(tx) + res := app.Application.CheckTx(types.RequestCheckTx{Tx: tx}) return app.callback( types.ToRequestCheckTx(tx), types.ToResponseCheckTx(res), @@ -188,7 +188,7 @@ func (app *localClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, erro app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.DeliverTx(tx) + res := app.Application.DeliverTx(types.RequestDeliverTx{Tx: tx}) return &res, nil } @@ -196,7 +196,7 @@ func (app *localClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.CheckTx(tx) + res := app.Application.CheckTx(types.RequestCheckTx{Tx: tx}) return &res, nil } diff --git a/abci/example/counter/counter.go b/abci/example/counter/counter.go index a77e7821f..2cea1e558 100644 --- a/abci/example/counter/counter.go +++ b/abci/example/counter/counter.go @@ -42,15 +42,15 @@ func (app *CounterApplication) SetOption(req types.RequestSetOption) types.Respo return types.ResponseSetOption{} } -func (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { +func (app *CounterApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { if app.serial { - if len(tx) > 8 { + if len(req.Tx) > 8 { return types.ResponseDeliverTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))} + Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(req.Tx))} } tx8 := make([]byte, 8) - copy(tx8[len(tx8)-len(tx):], tx) + copy(tx8[len(tx8)-len(req.Tx):], req.Tx) txValue := binary.BigEndian.Uint64(tx8) if txValue != uint64(app.txCount) { return types.ResponseDeliverTx{ @@ -62,15 +62,15 @@ func (app *CounterApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { return types.ResponseDeliverTx{Code: code.CodeTypeOK} } -func (app *CounterApplication) CheckTx(tx []byte) types.ResponseCheckTx { +func (app *CounterApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { if app.serial { - if len(tx) > 8 { + if len(req.Tx) > 8 { return types.ResponseCheckTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(tx))} + Log: fmt.Sprintf("Max tx size is 8 bytes, got %d", len(req.Tx))} } tx8 := make([]byte, 8) - copy(tx8[len(tx8)-len(tx):], tx) + copy(tx8[len(tx8)-len(req.Tx):], req.Tx) txValue := binary.BigEndian.Uint64(tx8) if txValue < uint64(app.txCount) { return types.ResponseCheckTx{ diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 0c28813f2..82d404c76 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -76,13 +76,13 @@ func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.Respon } // tx is either "key=value" or just arbitrary bytes -func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { +func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { var key, value []byte - parts := bytes.Split(tx, []byte("=")) + parts := bytes.Split(req.Tx, []byte("=")) if len(parts) == 2 { key, value = parts[0], parts[1] } else { - key, value = tx, tx + key, value = req.Tx, req.Tx } app.state.db.Set(prefixKey(key), value) @@ -101,7 +101,7 @@ func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events} } -func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx { +func (app *KVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} } diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go index a18fb8d3c..074baa49f 100644 --- a/abci/example/kvstore/kvstore_test.go +++ b/abci/example/kvstore/kvstore_test.go @@ -19,10 +19,11 @@ import ( ) func testKVStore(t *testing.T, app types.Application, tx []byte, key, value string) { - ar := app.DeliverTx(tx) + req := types.RequestDeliverTx{Tx: tx} + ar := app.DeliverTx(req) require.False(t, ar.IsErr(), ar) // repeating tx doesn't raise error - ar = app.DeliverTx(tx) + ar = app.DeliverTx(req) require.False(t, ar.IsErr(), ar) // make sure query is fine @@ -179,7 +180,7 @@ func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header}) for _, tx := range txs { - if r := kvstore.DeliverTx(tx); r.IsErr() { + if r := kvstore.DeliverTx(types.RequestDeliverTx{Tx: tx}); r.IsErr() { t.Fatal(r) } } diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index f969eebfe..b7484a4aa 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -61,21 +61,21 @@ func (app *PersistentKVStoreApplication) SetOption(req types.RequestSetOption) t } // tx is either "val:pubkey/power" or "key=value" or just arbitrary bytes -func (app *PersistentKVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx { +func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { // if it starts with "val:", update the validator set // format is "val:pubkey/power" - if isValidatorTx(tx) { + if isValidatorTx(req.Tx) { // update validators in the merkle tree // and in app.ValUpdates - return app.execValidatorTx(tx) + return app.execValidatorTx(req.Tx) } // otherwise, update the key-value store - return app.app.DeliverTx(tx) + return app.app.DeliverTx(req) } -func (app *PersistentKVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx { - return app.app.CheckTx(tx) +func (app *PersistentKVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { + return app.app.CheckTx(req) } // Commit will panic if InitChain was not called diff --git a/abci/server/socket_server.go b/abci/server/socket_server.go index 4b92f04cf..96cb844b7 100644 --- a/abci/server/socket_server.go +++ b/abci/server/socket_server.go @@ -178,10 +178,10 @@ func (s *SocketServer) handleRequest(req *types.Request, responses chan<- *types res := s.app.SetOption(*r.SetOption) responses <- types.ToResponseSetOption(res) case *types.Request_DeliverTx: - res := s.app.DeliverTx(r.DeliverTx.Tx) + res := s.app.DeliverTx(*r.DeliverTx) responses <- types.ToResponseDeliverTx(res) case *types.Request_CheckTx: - res := s.app.CheckTx(r.CheckTx.Tx) + res := s.app.CheckTx(*r.CheckTx) responses <- types.ToResponseCheckTx(res) case *types.Request_Commit: res := s.app.Commit() diff --git a/abci/types/application.go b/abci/types/application.go index 88f8d001e..90518851d 100644 --- a/abci/types/application.go +++ b/abci/types/application.go @@ -15,12 +15,12 @@ type Application interface { Query(RequestQuery) ResponseQuery // Query for state // Mempool Connection - CheckTx(tx []byte) ResponseCheckTx // Validate a tx for the mempool + CheckTx(RequestCheckTx) ResponseCheckTx // Validate a tx for the mempool // Consensus Connection InitChain(RequestInitChain) ResponseInitChain // Initialize blockchain with validators and other info from TendermintCore BeginBlock(RequestBeginBlock) ResponseBeginBlock // Signals the beginning of a block - DeliverTx(tx []byte) ResponseDeliverTx // Deliver a tx for full processing + DeliverTx(RequestDeliverTx) ResponseDeliverTx // Deliver a tx for full processing EndBlock(RequestEndBlock) ResponseEndBlock // Signals the end of a block, returns changes to the validator set Commit() ResponseCommit // Commit the state and return the application Merkle root hash } @@ -45,11 +45,11 @@ func (BaseApplication) SetOption(req RequestSetOption) ResponseSetOption { return ResponseSetOption{} } -func (BaseApplication) DeliverTx(tx []byte) ResponseDeliverTx { +func (BaseApplication) DeliverTx(req RequestDeliverTx) ResponseDeliverTx { return ResponseDeliverTx{Code: CodeTypeOK} } -func (BaseApplication) CheckTx(tx []byte) ResponseCheckTx { +func (BaseApplication) CheckTx(req RequestCheckTx) ResponseCheckTx { return ResponseCheckTx{Code: CodeTypeOK} } @@ -103,12 +103,12 @@ func (app *GRPCApplication) SetOption(ctx context.Context, req *RequestSetOption } func (app *GRPCApplication) DeliverTx(ctx context.Context, req *RequestDeliverTx) (*ResponseDeliverTx, error) { - res := app.app.DeliverTx(req.Tx) + res := app.app.DeliverTx(*req) return &res, nil } func (app *GRPCApplication) CheckTx(ctx context.Context, req *RequestCheckTx) (*ResponseCheckTx, error) { - res := app.app.CheckTx(req.Tx) + res := app.app.CheckTx(*req) return &res, nil } diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index 1a4a87d08..cb3598668 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -291,11 +291,11 @@ func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { return abci.ResponseEndBlock{} } -func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { +func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { return abci.ResponseDeliverTx{Events: []abci.Event{}} } -func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx { +func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { return abci.ResponseCheckTx{} } diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index af15a1fef..d9feef9b4 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -141,7 +141,7 @@ func TestMempoolRmBadTx(t *testing.T) { txBytes := make([]byte, 8) binary.BigEndian.PutUint64(txBytes, uint64(0)) - resDeliver := app.DeliverTx(txBytes) + resDeliver := app.DeliverTx(abci.RequestDeliverTx{Tx: txBytes}) assert.False(t, resDeliver.IsErr(), fmt.Sprintf("expected no error. got %v", resDeliver)) resCommit := app.Commit() @@ -209,8 +209,8 @@ func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo { return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)} } -func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx { - txValue := txAsUint64(tx) +func (app *CounterApplication) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + txValue := txAsUint64(req.Tx) if txValue != uint64(app.txCount) { return abci.ResponseDeliverTx{ Code: code.CodeTypeBadNonce, @@ -220,8 +220,8 @@ func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx { return abci.ResponseDeliverTx{Code: code.CodeTypeOK} } -func (app *CounterApplication) CheckTx(tx []byte) abci.ResponseCheckTx { - txValue := txAsUint64(tx) +func (app *CounterApplication) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { + txValue := txAsUint64(req.Tx) if txValue != uint64(app.mempoolTxCount) { return abci.ResponseCheckTx{ Code: code.CodeTypeBadNonce, diff --git a/consensus/replay.go b/consensus/replay.go index 794f870d0..2c4377ffa 100644 --- a/consensus/replay.go +++ b/consensus/replay.go @@ -515,7 +515,7 @@ type mockProxyApp struct { abciResponses *sm.ABCIResponses } -func (mock *mockProxyApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { +func (mock *mockProxyApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { r := mock.abciResponses.DeliverTx[mock.txCount] mock.txCount++ if r == nil { //it could be nil because of amino unMarshall, it will cause an empty ResponseDeliverTx to become nil diff --git a/docs/app-dev/app-development.md b/docs/app-dev/app-development.md index 1b4e26800..c9983beaa 100644 --- a/docs/app-dev/app-development.md +++ b/docs/app-dev/app-development.md @@ -101,8 +101,8 @@ mempool state (this behaviour can be turned off with In go: ``` -func (app *KVStoreApplication) CheckTx(tx []byte) types.Result { - return types.OK +func (app *KVStoreApplication) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx { + return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1} } ``` @@ -168,14 +168,29 @@ In go: ``` // tx is either "key=value" or just arbitrary bytes -func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result { - parts := strings.Split(string(tx), "=") - if len(parts) == 2 { - app.state.Set([]byte(parts[0]), []byte(parts[1])) - } else { - app.state.Set(tx, tx) - } - return types.OK +func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { + var key, value []byte + parts := bytes.Split(req.Tx, []byte("=")) + if len(parts) == 2 { + key, value = parts[0], parts[1] + } else { + key, value = req.Tx, req.Tx + } + + app.state.db.Set(prefixKey(key), value) + app.state.Size += 1 + + events := []types.Event{ + { + Type: "app", + Attributes: []cmn.KVPair{ + {Key: []byte("creator"), Value: []byte("Cosmoshi Netowoko")}, + {Key: []byte("key"), Value: key}, + }, + }, + } + + return types.ResponseDeliverTx{Code: code.CodeTypeOK, Events: events} } ``` @@ -223,9 +238,14 @@ job of the [Handshake](#handshake). In go: ``` -func (app *KVStoreApplication) Commit() types.Result { - hash := app.state.Hash() - return types.NewResultOK(hash, "") +func (app *KVStoreApplication) Commit() types.ResponseCommit { + // Using a memdb - just return the big endian size of the db + appHash := make([]byte, 8) + binary.PutVarint(appHash, app.state.Size) + app.state.AppHash = appHash + app.state.Height += 1 + saveState(app.state) + return types.ResponseCommit{Data: appHash} } ``` @@ -256,12 +276,10 @@ In go: ``` // Track the block hash and header information -func (app *PersistentKVStoreApplication) BeginBlock(params types.RequestBeginBlock) { - // update latest block info - app.blockHeader = params.Header - - // reset valset changes - app.changes = make([]*types.Validator, 0) +func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock { + // reset valset changes + app.ValUpdates = make([]types.ValidatorUpdate, 0) + return types.ResponseBeginBlock{} } ``` @@ -303,7 +321,7 @@ In go: ``` // Update the validator set func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) types.ResponseEndBlock { - return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates} + return types.ResponseEndBlock{ValidatorUpdates: app.ValUpdates} } ``` @@ -347,43 +365,29 @@ Note: these query formats are subject to change! In go: ``` - func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { - if reqQuery.Prove { - value, proof, exists := app.state.GetWithProof(reqQuery.Data) - resQuery.Index = -1 // TODO make Proof return index - resQuery.Key = reqQuery.Data - resQuery.Value = value - resQuery.Proof = proof - if exists { - resQuery.Log = "exists" - } else { - resQuery.Log = "does not exist" - } - return - } else { - index, value, exists := app.state.Get(reqQuery.Data) - resQuery.Index = int64(index) - resQuery.Value = value - if exists { - resQuery.Log = "exists" - } else { - resQuery.Log = "does not exist" - } - return - } - } - return - } else { - index, value, exists := app.state.Get(reqQuery.Data) - resQuery.Index = int64(index) - resQuery.Value = value - if exists { - resQuery.Log = "exists" - } else { - resQuery.Log = "does not exist" - } - return - } +func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery types.ResponseQuery) { + if reqQuery.Prove { + value := app.state.db.Get(prefixKey(reqQuery.Data)) + resQuery.Index = -1 // TODO make Proof return index + resQuery.Key = reqQuery.Data + resQuery.Value = value + if value != nil { + resQuery.Log = "exists" + } else { + resQuery.Log = "does not exist" + } + return + } else { + resQuery.Key = reqQuery.Data + value := app.state.db.Get(prefixKey(reqQuery.Data)) + resQuery.Value = value + if value != nil { + resQuery.Log = "exists" + } else { + resQuery.Log = "does not exist" + } + return + } } ``` @@ -439,7 +443,11 @@ In go: ``` func (app *KVStoreApplication) Info(req types.RequestInfo) (resInfo types.ResponseInfo) { - return types.ResponseInfo{Data: fmt.Sprintf("{\"size\":%v}", app.state.Size())} + return types.ResponseInfo{ + Data: fmt.Sprintf("{\"size\":%v}", app.state.Size), + Version: version.ABCIVersion, + AppVersion: ProtocolVersion.Uint64(), + } } ``` @@ -463,13 +471,14 @@ In go: ``` // Save the validators in the merkle tree -func (app *PersistentKVStoreApplication) InitChain(params types.RequestInitChain) { - for _, v := range params.Validators { - r := app.updateValidator(v) - if r.IsErr() { - app.logger.Error("Error updating validators", "r", r) - } - } +func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) types.ResponseInitChain { + for _, v := range req.Validators { + r := app.updateValidator(v) + if r.IsErr() { + app.logger.Error("Error updating validators", "r", r) + } + } + return types.ResponseInitChain{} } ``` diff --git a/docs/app-dev/indexing-transactions.md b/docs/app-dev/indexing-transactions.md index de8336a43..ffe8b989a 100644 --- a/docs/app-dev/indexing-transactions.md +++ b/docs/app-dev/indexing-transactions.md @@ -47,7 +47,7 @@ pairs of UTF-8 encoded strings (e.g. "account.owner": "Bob", "balance": Example: ``` -func (app *KVStoreApplication) DeliverTx(tx []byte) types.Result { +func (app *KVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.Result { ... tags := []cmn.KVPair{ {[]byte("account.name"), []byte("igor")}, diff --git a/mempool/clist_mempool_test.go b/mempool/clist_mempool_test.go index bf2c61dd7..db6e800b8 100644 --- a/mempool/clist_mempool_test.go +++ b/mempool/clist_mempool_test.go @@ -99,7 +99,7 @@ func TestReapMaxBytesMaxGas(t *testing.T) { checkTxs(t, mempool, 1, UnknownPeerID) tx0 := mempool.TxsFront().Value.(*mempoolTx) // assert that kv store has gas wanted = 1. - require.Equal(t, app.CheckTx(tx0.tx).GasWanted, int64(1), "KVStore had a gas value neq to 1") + require.Equal(t, app.CheckTx(abci.RequestCheckTx{Tx: tx0.tx}).GasWanted, int64(1), "KVStore had a gas value neq to 1") require.Equal(t, tx0.gasWanted, int64(1), "transactions gas was set incorrectly") // ensure each tx is 20 bytes long require.Equal(t, len(tx0.tx), 20, "Tx is longer than 20 bytes") diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go index 2ab62a420..f40755fec 100644 --- a/rpc/client/mock/abci.go +++ b/rpc/client/mock/abci.go @@ -45,29 +45,29 @@ func (a ABCIApp) ABCIQueryWithOptions(path string, data cmn.HexBytes, opts clien // TODO: Make it wait for a commit and set res.Height appropriately. func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) { res := ctypes.ResultBroadcastTxCommit{} - res.CheckTx = a.App.CheckTx(tx) + res.CheckTx = a.App.CheckTx(abci.RequestCheckTx{Tx: tx}) if res.CheckTx.IsErr() { return &res, nil } - res.DeliverTx = a.App.DeliverTx(tx) + res.DeliverTx = a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) res.Height = -1 // TODO return &res, nil } func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - c := a.App.CheckTx(tx) + c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx}) // and this gets written in a background thread... if !c.IsErr() { - go func() { a.App.DeliverTx(tx) }() // nolint: errcheck + go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }() // nolint: errcheck } return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil } func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) { - c := a.App.CheckTx(tx) + c := a.App.CheckTx(abci.RequestCheckTx{Tx: tx}) // and this gets written in a background thread... if !c.IsErr() { - go func() { a.App.DeliverTx(tx) }() // nolint: errcheck + go func() { a.App.DeliverTx(abci.RequestDeliverTx{Tx: tx}) }() // nolint: errcheck } return &ctypes.ResultBroadcastTx{Code: c.Code, Data: c.Data, Log: c.Log, Hash: tx.Hash()}, nil } diff --git a/state/execution_test.go b/state/execution_test.go index 80b442e3d..269f489a9 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -453,11 +453,11 @@ func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { return abci.ResponseEndBlock{ValidatorUpdates: app.ValidatorUpdates} } -func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx { +func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { return abci.ResponseDeliverTx{Events: []abci.Event{}} } -func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx { +func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { return abci.ResponseCheckTx{} } From 866b343c0c864c6ae4369c88506d17572513ce24 Mon Sep 17 00:00:00 2001 From: Marko Date: Fri, 21 Jun 2019 07:58:32 +0200 Subject: [PATCH 236/259] Changes to files that had linting issue (#3731) - Govet issues fixed - 1 gosec issue solved using nolint Signed-off-by: Marko Baricevic --- .golangci.yml | 1 - benchmarks/codec_test.go | 2 +- blockchain/reactor_test.go | 2 +- consensus/byzantine_test.go | 4 ++-- consensus/common_test.go | 4 ++-- consensus/reactor.go | 4 ---- consensus/state_test.go | 4 ++-- consensus/types/height_vote_set_test.go | 2 +- evidence/pool_test.go | 2 +- go.sum | 1 + node/node_test.go | 8 +++---- p2p/upnp/upnp.go | 2 +- privval/file_test.go | 14 ++++++------ state/execution_test.go | 20 ++++++++--------- state/state_test.go | 30 ++++++++++++------------- state/tx_filter_test.go | 2 +- state/txindex/indexer_service_test.go | 4 ++-- types/block.go | 2 +- types/protobuf_test.go | 2 +- 19 files changed, 53 insertions(+), 57 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index a051e1a45..6adbbd9da 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -20,7 +20,6 @@ linters: - gochecknoinits - scopelint - stylecheck - # linters-settings: # govet: # check-shadowing: true diff --git a/benchmarks/codec_test.go b/benchmarks/codec_test.go index eff5c7349..64c0e72cf 100644 --- a/benchmarks/codec_test.go +++ b/benchmarks/codec_test.go @@ -14,7 +14,7 @@ import ( func testNodeInfo(id p2p.ID) p2p.DefaultNodeInfo { return p2p.DefaultNodeInfo{ - ProtocolVersion: p2p.ProtocolVersion{1, 2, 3}, + ProtocolVersion: p2p.ProtocolVersion{P2P: 1, Block: 2, App: 3}, ID_: id, Moniker: "SOMENAME", Network: "SOMENAME", diff --git a/blockchain/reactor_test.go b/blockchain/reactor_test.go index cb3598668..b5137bb2a 100644 --- a/blockchain/reactor_test.go +++ b/blockchain/reactor_test.go @@ -111,7 +111,7 @@ func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals thisBlock := makeBlock(blockHeight, state, lastCommit) thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes) - blockID := types.BlockID{thisBlock.Hash(), thisParts.Header()} + blockID := types.BlockID{Hash: thisBlock.Hash(), PartsHeader: thisParts.Header()} state, err = blockExec.ApplyBlock(state, blockID, thisBlock) if err != nil { diff --git a/consensus/byzantine_test.go b/consensus/byzantine_test.go index c2eb114dc..1c52e79ad 100644 --- a/consensus/byzantine_test.go +++ b/consensus/byzantine_test.go @@ -177,7 +177,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons // Create a new proposal block from state/txs from the mempool. block1, blockParts1 := cs.createProposalBlock() - polRound, propBlockID := cs.ValidRound, types.BlockID{block1.Hash(), blockParts1.Header()} + polRound, propBlockID := cs.ValidRound, types.BlockID{Hash: block1.Hash(), PartsHeader: blockParts1.Header()} proposal1 := types.NewProposal(height, round, polRound, propBlockID) if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal1); err != nil { t.Error(err) @@ -185,7 +185,7 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons // Create a new proposal block from state/txs from the mempool. block2, blockParts2 := cs.createProposalBlock() - polRound, propBlockID = cs.ValidRound, types.BlockID{block2.Hash(), blockParts2.Header()} + polRound, propBlockID = cs.ValidRound, types.BlockID{Hash: block2.Hash(), PartsHeader: blockParts2.Header()} proposal2 := types.NewProposal(height, round, polRound, propBlockID) if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal2); err != nil { t.Error(err) diff --git a/consensus/common_test.go b/consensus/common_test.go index a4ad79c94..29db524ec 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -86,7 +86,7 @@ func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, hea Round: vs.Round, Timestamp: tmtime.Now(), Type: voteType, - BlockID: types.BlockID{hash, header}, + BlockID: types.BlockID{Hash: hash, PartsHeader: header}, } err := vs.PrivValidator.SignVote(config.ChainID(), vote) return vote, err @@ -159,7 +159,7 @@ func decideProposal(cs1 *ConsensusState, vs *validatorStub, height int64, round } // Make proposal - polRound, propBlockID := validRound, types.BlockID{block.Hash(), blockParts.Header()} + polRound, propBlockID := validRound, types.BlockID{Hash: block.Hash(), PartsHeader: blockParts.Header()} proposal = types.NewProposal(height, round, polRound, propBlockID) if err := vs.SignProposal(chainID, proposal); err != nil { panic(err) diff --git a/consensus/reactor.go b/consensus/reactor.go index 36e948f6d..f690a407d 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -351,10 +351,6 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) default: conR.Logger.Error(fmt.Sprintf("Unknown chId %X", chID)) } - - if err != nil { - conR.Logger.Error("Error in Receive()", "err", err) - } } // SetEventBus sets event bus. diff --git a/consensus/state_test.go b/consensus/state_test.go index 87e351dc8..93ef0d4cb 100644 --- a/consensus/state_test.go +++ b/consensus/state_test.go @@ -192,7 +192,7 @@ func TestStateBadProposal(t *testing.T) { stateHash[0] = byte((stateHash[0] + 1) % 255) propBlock.AppHash = stateHash propBlockParts := propBlock.MakePartSet(partSize) - blockID := types.BlockID{propBlock.Hash(), propBlockParts.Header()} + blockID := types.BlockID{Hash: propBlock.Hash(), PartsHeader: propBlockParts.Header()} proposal := types.NewProposal(vs2.Height, round, -1, blockID) if err := vs2.SignProposal(config.ChainID(), proposal); err != nil { t.Fatal("failed to sign bad proposal", err) @@ -811,7 +811,7 @@ func TestStateLockPOLSafety2(t *testing.T) { _, propBlock0 := decideProposal(cs1, vss[0], height, round) propBlockHash0 := propBlock0.Hash() propBlockParts0 := propBlock0.MakePartSet(partSize) - propBlockID0 := types.BlockID{propBlockHash0, propBlockParts0.Header()} + propBlockID0 := types.BlockID{Hash: propBlockHash0, PartsHeader: propBlockParts0.Header()} // the others sign a polka but we don't see it prevotes := signVotes(types.PrevoteType, propBlockHash0, propBlockParts0.Header(), vs2, vs3, vs4) diff --git a/consensus/types/height_vote_set_test.go b/consensus/types/height_vote_set_test.go index 42b5333a1..f45492aa4 100644 --- a/consensus/types/height_vote_set_test.go +++ b/consensus/types/height_vote_set_test.go @@ -62,7 +62,7 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali Round: round, Timestamp: tmtime.Now(), Type: types.PrecommitType, - BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}}, + BlockID: types.BlockID{Hash: []byte("fakehash"), PartsHeader: types.PartSetHeader{}}, } chainID := config.ChainID() err := privVal.SignVote(chainID, vote) diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 30b20011e..13bc45563 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -60,7 +60,7 @@ func TestEvidencePool(t *testing.T) { pool := NewEvidencePool(stateDB, evidenceDB) goodEvidence := types.NewMockGoodEvidence(height, 0, valAddr) - badEvidence := types.MockBadEvidence{goodEvidence} + badEvidence := types.MockBadEvidence{MockGoodEvidence: goodEvidence} // bad evidence err := pool.AddEvidence(badEvidence) diff --git a/go.sum b/go.sum index 10e54c0fd..fee691de9 100644 --- a/go.sum +++ b/go.sum @@ -17,6 +17,7 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0= github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw= +github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q= github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= diff --git a/node/node_test.go b/node/node_test.go index 6971ddd34..ce4e82c2d 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -285,10 +285,10 @@ func state(nVals int, height int64) (sm.State, dbm.DB) { secret := []byte(fmt.Sprintf("test%d", i)) pk := ed25519.GenPrivKeyFromSecret(secret) vals[i] = types.GenesisValidator{ - pk.PubKey().Address(), - pk.PubKey(), - 1000, - fmt.Sprintf("test%d", i), + Address: pk.PubKey().Address(), + PubKey: pk.PubKey(), + Power: 1000, + Name: fmt.Sprintf("test%d", i), } } s, _ := sm.MakeGenesisState(&types.GenesisDoc{ diff --git a/p2p/upnp/upnp.go b/p2p/upnp/upnp.go index d53974fc4..89f35c5df 100644 --- a/p2p/upnp/upnp.go +++ b/p2p/upnp/upnp.go @@ -197,7 +197,7 @@ func localIPv4() (net.IP, error) { } func getServiceURL(rootURL string) (url, urnDomain string, err error) { - r, err := http.Get(rootURL) + r, err := http.Get(rootURL) // nolint: gosec if err != nil { return } diff --git a/privval/file_test.go b/privval/file_test.go index 06d75a809..98de69480 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -50,7 +50,7 @@ func TestResetValidator(t *testing.T) { // test vote height, round := int64(10), 1 voteType := byte(types.PrevoteType) - blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} + blockID := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{}} vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) err = privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error signing vote") @@ -162,8 +162,8 @@ func TestSignVote(t *testing.T) { privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) - block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} - block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{}} + block1 := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{}} + block2 := types.BlockID{Hash: []byte{3, 2, 1}, PartsHeader: types.PartSetHeader{}} height, round := int64(10), 1 voteType := byte(types.PrevoteType) @@ -207,8 +207,8 @@ func TestSignProposal(t *testing.T) { privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) - block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}} - block2 := types.BlockID{[]byte{3, 2, 1}, types.PartSetHeader{10, []byte{3, 2, 1}}} + block1 := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{Total: 5, Hash: []byte{1, 2, 3}}} + block2 := types.BlockID{Hash: []byte{3, 2, 1}, PartsHeader: types.PartSetHeader{Total: 10, Hash: []byte{3, 2, 1}}} height, round := int64(10), 1 // sign a proposal for first time @@ -249,7 +249,7 @@ func TestDifferByTimestamp(t *testing.T) { privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) - block1 := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{5, []byte{1, 2, 3}}} + block1 := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{Total: 5, Hash: []byte{1, 2, 3}}} height, round := int64(10), 1 chainID := "mychainid" @@ -277,7 +277,7 @@ func TestDifferByTimestamp(t *testing.T) { // test vote { voteType := byte(types.PrevoteType) - blockID := types.BlockID{[]byte{1, 2, 3}, types.PartSetHeader{}} + blockID := types.BlockID{Hash: []byte{1, 2, 3}, PartsHeader: types.PartSetHeader{}} vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) err := privVal.SignVote("mychainid", vote) assert.NoError(t, err, "expected no error signing vote") diff --git a/state/execution_test.go b/state/execution_test.go index 269f489a9..ac7b9cc38 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -40,7 +40,7 @@ func TestApplyBlock(t *testing.T) { mock.Mempool{}, MockEvidencePool{}) block := makeBlock(state, 1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} //nolint:ineffassign state, err = blockExec.ApplyBlock(state, blockID, block) @@ -62,7 +62,7 @@ func TestBeginBlockValidators(t *testing.T) { prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} - prevBlockID := types.BlockID{prevHash, prevParts} + prevBlockID := types.BlockID{Hash: prevHash, PartsHeader: prevParts} now := tmtime.Now() commitSig0 := (&types.Vote{ValidatorIndex: 0, Timestamp: now, Type: types.PrecommitType}).CommitSig() @@ -115,7 +115,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} - prevBlockID := types.BlockID{prevHash, prevParts} + prevBlockID := types.BlockID{Hash: prevHash, PartsHeader: prevParts} height1, idx1, val1 := int64(8), 0, state.Validators.Validators[0].Address height2, idx2, val2 := int64(3), 1, state.Validators.Validators[1].Address @@ -159,7 +159,7 @@ func TestValidateValidatorUpdates(t *testing.T) { secpKey := secp256k1.GenPrivKey().PubKey() - defaultValidatorParams := types.ValidatorParams{[]string{types.ABCIPubKeyTypeEd25519}} + defaultValidatorParams := types.ValidatorParams{PubKeyTypes: []string{types.ABCIPubKeyTypeEd25519}} testCases := []struct { name string @@ -321,7 +321,7 @@ func TestEndBlockValidatorUpdates(t *testing.T) { require.NoError(t, err) block := makeBlock(state, 1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} pubkey := ed25519.GenPrivKey().PubKey() app.ValidatorUpdates = []abci.ValidatorUpdate{ @@ -369,7 +369,7 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{}) block := makeBlock(state, 1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} // Remove the only validator app.ValidatorUpdates = []abci.ValidatorUpdate{ @@ -398,10 +398,10 @@ func state(nVals, height int) (State, dbm.DB) { secret := []byte(fmt.Sprintf("test%d", i)) pk := ed25519.GenPrivKeyFromSecret(secret) vals[i] = types.GenesisValidator{ - pk.PubKey().Address(), - pk.PubKey(), - 1000, - fmt.Sprintf("test%d", i), + Address: pk.PubKey().Address(), + PubKey: pk.PubKey(), + Power: 1000, + Name: fmt.Sprintf("test%d", i), } } s, _ := MakeGenesisState(&types.GenesisDoc{ diff --git a/state/state_test.go b/state/state_test.go index c7600cc38..1ff09ccdb 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -129,7 +129,7 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { {Code: 32, Data: []byte("Hello"), Log: "Huh?"}, }, types.ABCIResults{ - {32, []byte("Hello")}, + {Code: 32, Data: []byte("Hello")}, }}, 2: { []*abci.ResponseDeliverTx{ @@ -143,8 +143,8 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { }, }, types.ABCIResults{ - {383, nil}, - {0, []byte("Gotcha!")}, + {Code: 383, Data: nil}, + {Code: 0, Data: []byte("Gotcha!")}, }}, 3: { nil, @@ -404,7 +404,7 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { assert.EqualValues(t, 0, val1.ProposerPriority) block := makeBlock(state, state.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} abciResponses := &ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } @@ -514,7 +514,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { assert.Equal(t, val1PubKey.Address(), state.Validators.Proposer.Address) block := makeBlock(state, state.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} // no updates: abciResponses := &ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, @@ -667,7 +667,7 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) block := makeBlock(oldState, oldState.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) @@ -693,7 +693,7 @@ func TestLargeGenesisValidator(t *testing.T) { EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}}, } block := makeBlock(oldState, oldState.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) @@ -707,7 +707,7 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) block := makeBlock(lastState, lastState.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} updatedStateInner, err := updateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) @@ -738,7 +738,7 @@ func TestLargeGenesisValidator(t *testing.T) { EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}}, } block := makeBlock(oldState, oldState.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) } @@ -750,7 +750,7 @@ func TestLargeGenesisValidator(t *testing.T) { EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}}, } block = makeBlock(oldState, oldState.LastBlockHeight+1) - blockID = types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID = types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) updatedState, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) @@ -770,7 +770,7 @@ func TestLargeGenesisValidator(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) block = makeBlock(curState, curState.LastBlockHeight+1) - blockID = types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID = types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} curState, err = updateState(curState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) { @@ -794,7 +794,7 @@ func TestLargeGenesisValidator(t *testing.T) { require.NoError(t, err) block := makeBlock(updatedState, updatedState.LastBlockHeight+1) - blockID := types.BlockID{block.Hash(), block.MakePartSet(testPartSize).Header()} + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} updatedState, err = updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) @@ -1033,7 +1033,7 @@ func makeHeaderPartsResponsesValPubKeyChange(state State, height int64, } } - return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses + return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses } func makeHeaderPartsResponsesValPowerChange(state State, height int64, @@ -1054,7 +1054,7 @@ func makeHeaderPartsResponsesValPowerChange(state State, height int64, } } - return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses + return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses } func makeHeaderPartsResponsesParams(state State, height int64, @@ -1064,7 +1064,7 @@ func makeHeaderPartsResponsesParams(state State, height int64, abciResponses := &ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)}, } - return block.Header, types.BlockID{block.Hash(), types.PartSetHeader{}}, abciResponses + return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses } type paramsChangeTestCase struct { diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index ffb41c178..e48ad2c3e 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -51,7 +51,7 @@ func randomGenesisDoc() *types.GenesisDoc { return &types.GenesisDoc{ GenesisTime: tmtime.Now(), ChainID: "abc", - Validators: []types.GenesisValidator{{pubkey.Address(), pubkey, 10, "myval"}}, + Validators: []types.GenesisValidator{{Address: pubkey.Address(), PubKey: pubkey, Power: 10, Name: "myval"}}, ConsensusParams: types.DefaultConsensusParams(), } } diff --git a/state/txindex/indexer_service_test.go b/state/txindex/indexer_service_test.go index 982d7b8c4..079f9cec2 100644 --- a/state/txindex/indexer_service_test.go +++ b/state/txindex/indexer_service_test.go @@ -43,14 +43,14 @@ func TestIndexerServiceIndexesBlocks(t *testing.T) { Tx: types.Tx("foo"), Result: abci.ResponseDeliverTx{Code: 0}, } - eventBus.PublishEventTx(types.EventDataTx{*txResult1}) + eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult1}) txResult2 := &types.TxResult{ Height: 1, Index: uint32(1), Tx: types.Tx("bar"), Result: abci.ResponseDeliverTx{Code: 0}, } - eventBus.PublishEventTx(types.EventDataTx{*txResult2}) + eventBus.PublishEventTx(types.EventDataTx{TxResult: *txResult2}) time.Sleep(100 * time.Millisecond) diff --git a/types/block.go b/types/block.go index 313eb6b75..55709ad60 100644 --- a/types/block.go +++ b/types/block.go @@ -198,7 +198,7 @@ func (b *Block) Hash() cmn.HexBytes { b.mtx.Lock() defer b.mtx.Unlock() - if b == nil || b.LastCommit == nil { + if b.LastCommit == nil { return nil } b.fillHeader() diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 64caa3f4c..833c7dc38 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -90,7 +90,7 @@ func TestABCIHeader(t *testing.T) { height, numTxs, []byte("lastCommitHash"), []byte("dataHash"), []byte("evidenceHash"), ) - protocolVersion := version.Consensus{7, 8} + protocolVersion := version.Consensus{Block: 7, App: 8} timestamp := time.Now() lastBlockID := BlockID{ Hash: []byte("hash"), From 2e5b2a9537fd68d32c39693b810d2da61c94b73b Mon Sep 17 00:00:00 2001 From: needkane Date: Fri, 21 Jun 2019 19:18:49 +0800 Subject: [PATCH 237/259] abci/examples: switch from hex to base64 pubkey in kvstore (#3641) * abci/example: use base64 for update validator set * update kvstore/README.md * update CHANGELOG_PENDING.md and abci/example/kvstore/README.md --- CHANGELOG_PENDING.md | 1 + abci/example/kvstore/README.md | 6 +++--- abci/example/kvstore/persistent_kvstore.go | 24 ++++++++++++---------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index ed835aebc..74aa72c6c 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -34,6 +34,7 @@ ### IMPROVEMENTS: - [p2p] \#3666 Add per channel telemetry to improve reactor observability - [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) +- [abci/examples] \#3659 Change validator update tx format (incl. expected pubkey format, which is base64 now) (@needkane) ### BUG FIXES: - [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) diff --git a/abci/example/kvstore/README.md b/abci/example/kvstore/README.md index e988eadb0..bed81a598 100644 --- a/abci/example/kvstore/README.md +++ b/abci/example/kvstore/README.md @@ -22,10 +22,10 @@ and the Handshake allows any necessary blocks to be replayed. Validator set changes are effected using the following transaction format: ``` -val:pubkey1/power1,addr2/power2,addr3/power3" +"val:pubkey1!power1,pubkey2!power2,pubkey3!power3" ``` -where `power1` is the new voting power for the validator with `pubkey1` (possibly a new one). +where `pubkeyN` is a base64-encoded 32-byte ed25519 key and `powerN` is a new voting power for the validator with `pubkeyN` (possibly a new one). +To remove a validator from the validator set, set power to `0`. There is no sybil protection against new validators joining. -Validators can be removed by setting their power to `0`. diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index b7484a4aa..ba0b53896 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -2,7 +2,7 @@ package kvstore import ( "bytes" - "encoding/hex" + "encoding/base64" "fmt" "strconv" "strings" @@ -60,10 +60,10 @@ func (app *PersistentKVStoreApplication) SetOption(req types.RequestSetOption) t return app.app.SetOption(req) } -// tx is either "val:pubkey/power" or "key=value" or just arbitrary bytes +// tx is either "val:pubkey!power" or "key=value" or just arbitrary bytes func (app *PersistentKVStoreApplication) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx { // if it starts with "val:", update the validator set - // format is "val:pubkey/power" + // format is "val:pubkey!power" if isValidatorTx(req.Tx) { // update validators in the merkle tree // and in app.ValUpdates @@ -129,33 +129,34 @@ func (app *PersistentKVStoreApplication) Validators() (validators []types.Valida } func MakeValSetChangeTx(pubkey types.PubKey, power int64) []byte { - return []byte(fmt.Sprintf("val:%X/%d", pubkey.Data, power)) + pubStr := base64.StdEncoding.EncodeToString(pubkey.Data) + return []byte(fmt.Sprintf("val:%s!%d", pubStr, power)) } func isValidatorTx(tx []byte) bool { return strings.HasPrefix(string(tx), ValidatorSetChangePrefix) } -// format is "val:pubkey/power" -// pubkey is raw 32-byte ed25519 key +// format is "val:pubkey!power" +// pubkey is a base64-encoded 32-byte ed25519 key func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx { tx = tx[len(ValidatorSetChangePrefix):] //get the pubkey and power - pubKeyAndPower := strings.Split(string(tx), "/") + pubKeyAndPower := strings.Split(string(tx), "!") if len(pubKeyAndPower) != 2 { return types.ResponseDeliverTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Expected 'pubkey/power'. Got %v", pubKeyAndPower)} + Log: fmt.Sprintf("Expected 'pubkey!power'. Got %v", pubKeyAndPower)} } pubkeyS, powerS := pubKeyAndPower[0], pubKeyAndPower[1] // decode the pubkey - pubkey, err := hex.DecodeString(pubkeyS) + pubkey, err := base64.StdEncoding.DecodeString(pubkeyS) if err != nil { return types.ResponseDeliverTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Pubkey (%s) is invalid hex", pubkeyS)} + Log: fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS)} } // decode the power @@ -176,9 +177,10 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate if v.Power == 0 { // remove validator if !app.app.state.db.Has(key) { + pubStr := base64.StdEncoding.EncodeToString(v.PubKey.Data) return types.ResponseDeliverTx{ Code: code.CodeTypeUnauthorized, - Log: fmt.Sprintf("Cannot remove non-existent validator %X", key)} + Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)} } app.app.state.db.Delete(key) } else { From 59497c362bdd21a3cb5b2dc1afe402496eac7776 Mon Sep 17 00:00:00 2001 From: Ismail Khoffi Date: Fri, 21 Jun 2019 22:29:16 +0200 Subject: [PATCH 238/259] Prepare nuking develop (#3726) * Update Readme and contrib. guidelines: remove traces of master Signed-off-by: Ismail Khoffi * add simple example Signed-off-by: Ismail Khoffi * update contributing.md * add link * Update CONTRIBUTING.md * add a note on master and releases Signed-off-by: Ismail Khoffi * update readme --- CONTRIBUTING.md | 65 ++++++++++++++++++++++++++++++++----------------- README.md | 15 ++++++------ 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e68e6d1ee..77a625504 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,8 @@ Thank you for considering making contributions to Tendermint and related repositories! Start by taking a look at the [coding repo](https://github.com/tendermint/coding) for overall information on repository workflow and standards. -Please follow standard github best practices: fork the repo, branch from the tip of develop, make some commits, and submit a pull request to develop. See the [open issues](https://github.com/tendermint/tendermint/issues) for things we need help with! +Please follow standard github best practices: fork the repo, branch from the tip of `master`, make some commits, and submit a pull request to `master`. +See the [open issues](https://github.com/tendermint/tendermint/issues) for things we need help with! Before making a pull request, please open an issue describing the change you would like to make. If an issue for your change already exists, @@ -112,32 +113,36 @@ removed from the header in rpc responses as well. ## Branching Model and Release -We follow a variant of [git flow](http://nvie.com/posts/a-successful-git-branching-model/). -This means that all pull-requests should be made against develop. Any merge to -master constitutes a tagged release. +The main development branch is master. -Note all pull requests should be squash merged except for merging to master and -merging master back to develop. This keeps the commit history clean and makes it +Every release is maintained in a release branch named `vX.Y.Z`. + +Note all pull requests should be squash merged except for merging to a release branch (named `vX.Y`). This keeps the commit history clean and makes it easy to reference the pull request where a change was introduced. -### Development Procedure: -- the latest state of development is on `develop` -- `develop` must never fail `make test` -- never --force onto `develop` (except when reverting a broken commit, which should seldom happen) +### Development Procedure + +- the latest state of development is on `master` +- `master` must never fail `make test` +- never --force onto `master` (except when reverting a broken commit, which should seldom happen) - create a development branch either on github.com/tendermint/tendermint, or your fork (using `git remote add origin`) - make changes and update the `CHANGELOG_PENDING.md` to record your change -- before submitting a pull request, run `git rebase` on top of the latest `develop` +- before submitting a pull request, run `git rebase` on top of the latest `master` -### Pull Merge Procedure: -- ensure pull branch is based on a recent develop +### Pull Merge Procedure + +- ensure pull branch is based on a recent `master` - run `make test` to ensure that all tests pass - squash merge pull request - the `unstable` branch may be used to aggregate pull merges before fixing tests -### Release Procedure: -- start on `develop` -- run integration tests (see `test_integrations` in Makefile) -- prepare release in a pull request against develop (to be squash merged): +### Release Procedure + +#### Major Release + +1. start on `master` +2. run integration tests (see `test_integrations` in Makefile) +3. prepare release in a pull request against `master` (to be squash merged): - copy `CHANGELOG_PENDING.md` to top of `CHANGELOG.md` - run `python ./scripts/linkify_changelog.py CHANGELOG.md` to add links for all issues @@ -147,14 +152,28 @@ easy to reference the pull request where a change was introduced. ./scripts/authors.sh ` - reset the `CHANGELOG_PENDING.md` - bump versions -- push latest develop with prepared release details to release/vX.X.X to run the extended integration tests on the CI -- if necessary, make pull requests against release/vX.X.X and squash merge them -- merge to master (don't squash merge!) -- merge master back to develop (don't squash merge!) +4. push your changes with prepared release details to `vX.X` (this will trigger the release `vX.X.0`) +5. merge back to master (don't squash merge!) -### Hotfix Procedure: +#### Minor Release -- follow the normal development and release procedure without any differences +If there were no breaking changes and you need to create a release nonetheless, +the procedure is almost exactly like with a new release above. + +The only difference is that in the end you create a pull request against the existing `X.X` branch. +The branch name should match the release number you want to create. +Merging this PR will trigger the next release. +For example, if the PR is against an existing 0.34 branch which already contains a v0.34.0 release/tag, +the patch version will be incremented and the created release will be v0.34.1. + +#### Backport Release + +1. start from the existing release branch you want to backport changes to (e.g. v0.30) +Branch to a release/vX.X.X branch locally (e.g. release/v0.30.7) +2. cherry pick the commit(s) that contain the changes you want to backport (usually these commits are from squash-merged PRs which were already reviewed) +3. steps 2 and 3 from [Major Release](#major-release) +4. push changes to release/vX.X.X branch +5. open a PR against the existing vX.X branch ## Testing diff --git a/README.md b/README.md index ad4fc1308..ec7b28cc0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,6 @@ https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/6874 Branch | Tests | Coverage ----------|-------|---------- master | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/master.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/master) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/master/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint) -develop | [![CircleCI](https://circleci.com/gh/tendermint/tendermint/tree/develop.svg?style=shield)](https://circleci.com/gh/tendermint/tendermint/tree/develop) | [![codecov](https://codecov.io/gh/tendermint/tendermint/branch/develop/graph/badge.svg)](https://codecov.io/gh/tendermint/tendermint) Tendermint Core is Byzantine Fault Tolerant (BFT) middleware that takes a state transition machine - written in any programming language - and securely replicates it on many machines. @@ -27,13 +26,15 @@ For protocol details, see [the specification](/docs/spec). For detailed analysis of the consensus protocol, including safety and liveness proofs, see our recent paper, "[The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)". -## A Note on Production Readiness +## Releases -While Tendermint is being used in production in private, permissioned -environments, we are still working actively to harden and audit it in preparation -for use in public blockchains, such as the [Cosmos Network](https://cosmos.network/). -We are also still making breaking changes to the protocol and the APIs. -Thus, we tag the releases as *alpha software*. +NOTE: The master branch is now an active development branch (starting with `v0.32`). Please, do not depend on it and +use [releases](https://github.com/tendermint/tendermint/releases) instead. + +Tendermint is being used in production in both private and public environments, +most notably the blockchains of the [Cosmos Network](https://cosmos.network/). +However, we are still making breaking changes to the protocol and the APIs and have not yet released v1.0. +See below for more details about [versioning](#versioning). In any case, if you intend to run Tendermint in production, please [contact us](mailto:partners@tendermint.com) and [join the chat](https://riot.im/app/#/room/#tendermint:matrix.org). From 228bba799ddc14604788899c3493cf3534e2fdfd Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Fri, 21 Jun 2019 17:29:29 -0400 Subject: [PATCH 239/259] state: add more tests for block validation (#3674) * Expose priv validators for use in testing * Generalize block header validation test past height 1 * Remove ineffectual assignment * Remove redundant SaveState call * Reorder comment for clarity * Use the block executor ApplyBlock function instead of implementing a stripped-down version of it * Remove commented-out code * Remove unnecessary test The required tests already appear to be implemented (implicitly) through the TestValidateBlockHeader test. * Allow for catching of specific error types during TestValidateBlockCommit * Make return error testable * Clean up and add TestValidateBlockCommit code * Fix formatting * Extract function to create a new mock test app * Update comment for clarity * Fix comment * Add skeleton code for evidence-related test * Allow for addressing priv val by address * Generalize test beyond a single validator * Generalize TestValidateBlockEvidence past first height * Reorder code to clearly separate tests and utility code * Use a common constant for stop height for testing in state/validation_test.go * Refactor errors to resemble existing conventions * Fix formatting * Extract common helper functions Having the tests littered with helper functions makes them less easily readable imho, so I've pulled them out into a separate file. This also makes it easier to see what helper functions are available during testing, so we minimize the chance of duplication when writing new tests. * Remove unused parameter * Remove unused parameters * Add field keys * Remove unused height constant * Fix typo * Fix incorrect return error * Add field keys * Use separate package for tests This refactors all of the state package's tests into a state_test package, so as to keep any usage of the state package's internal methods explicit. Any internal methods/constants used by tests are now explicitly exported in state/export_test.go * Refactor: extract helper function to make, validate, execute and commit a block * Rename state function to makeState * Remove redundant constant for number of validators * Refactor mock evidence registration into TestMain * Remove extraneous nVals variable * Replace function-level TODOs with file-level TODO and explanation * Remove extraneous comment * Fix linting issues brought up by GolangCI (pulled in from latest merge from develop) --- state/execution_test.go | 117 ++-------------- state/export_test.go | 62 +++++++++ state/helpers_test.go | 280 +++++++++++++++++++++++++++++++++++++++ state/main_test.go | 13 ++ state/services.go | 2 +- state/state_test.go | 219 ++++++++++-------------------- state/store_test.go | 31 ++--- state/tx_filter_test.go | 19 +-- state/validation.go | 5 +- state/validation_test.go | 213 ++++++++++++++++++----------- types/errors.go | 41 ++++++ types/validator_set.go | 4 +- 12 files changed, 636 insertions(+), 370 deletions(-) create mode 100644 state/export_test.go create mode 100644 state/helpers_test.go create mode 100644 state/main_test.go create mode 100644 types/errors.go diff --git a/state/execution_test.go b/state/execution_test.go index ac7b9cc38..38301df73 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -1,22 +1,20 @@ -package state +package state_test import ( "context" - "fmt" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/abci/example/kvstore" abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" - dbm "github.com/tendermint/tendermint/libs/db" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/proxy" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" ) @@ -34,10 +32,10 @@ func TestApplyBlock(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB := state(1, 1) + state, stateDB, _ := makeState(1, 1) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), - mock.Mempool{}, MockEvidencePool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), + mock.Mempool{}, sm.MockEvidencePool{}) block := makeBlock(state, 1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} @@ -58,7 +56,7 @@ func TestBeginBlockValidators(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB := state(2, 2) + state, stateDB, _ := makeState(2, 2) prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} @@ -84,7 +82,7 @@ func TestBeginBlockValidators(t *testing.T) { // block for height 2 block, _ := state.MakeBlock(2, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) - _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) + _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) require.Nil(t, err, tc.desc) // -> app receives a list of validators with a bool indicating if they signed @@ -111,7 +109,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB := state(2, 12) + state, stateDB, _ := makeState(2, 12) prevHash := state.LastBlockID.Hash prevParts := types.PartSetHeader{} @@ -145,7 +143,7 @@ func TestBeginBlockByzantineValidators(t *testing.T) { block, _ := state.MakeBlock(10, makeTxs(2), lastCommit, nil, state.Validators.GetProposer().Address) block.Time = now block.Evidence.Evidence = tc.evidence - _, err = ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) + _, err = sm.ExecCommitBlock(proxyApp.Consensus(), block, log.TestingLogger(), stateDB) require.Nil(t, err, tc.desc) // -> app must receive an index of the byzantine validator @@ -213,7 +211,7 @@ func TestValidateValidatorUpdates(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - err := validateValidatorUpdates(tc.abciUpdates, tc.validatorParams) + err := sm.ValidateValidatorUpdates(tc.abciUpdates, tc.validatorParams) if tc.shouldErr { assert.Error(t, err) } else { @@ -307,9 +305,9 @@ func TestEndBlockValidatorUpdates(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB := state(1, 1) + state, stateDB, _ := makeState(1, 1) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{}) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) eventBus := types.NewEventBus() err = eventBus.Start() @@ -365,8 +363,8 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { require.Nil(t, err) defer proxyApp.Stop() - state, stateDB := state(1, 1) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, MockEvidencePool{}) + state, stateDB, _ := makeState(1, 1) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) block := makeBlock(state, 1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} @@ -381,90 +379,3 @@ func TestEndBlockValidatorUpdatesResultingInEmptySet(t *testing.T) { assert.NotEmpty(t, state.NextValidators.Validators) } - -//---------------------------------------------------------------------------- - -// make some bogus txs -func makeTxs(height int64) (txs []types.Tx) { - for i := 0; i < nTxsPerBlock; i++ { - txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) - } - return txs -} - -func state(nVals, height int) (State, dbm.DB) { - vals := make([]types.GenesisValidator, nVals) - for i := 0; i < nVals; i++ { - secret := []byte(fmt.Sprintf("test%d", i)) - pk := ed25519.GenPrivKeyFromSecret(secret) - vals[i] = types.GenesisValidator{ - Address: pk.PubKey().Address(), - PubKey: pk.PubKey(), - Power: 1000, - Name: fmt.Sprintf("test%d", i), - } - } - s, _ := MakeGenesisState(&types.GenesisDoc{ - ChainID: chainID, - Validators: vals, - AppHash: nil, - }) - - // save validators to db for 2 heights - stateDB := dbm.NewMemDB() - SaveState(stateDB, s) - - for i := 1; i < height; i++ { - s.LastBlockHeight++ - s.LastValidators = s.Validators.Copy() - SaveState(stateDB, s) - } - return s, stateDB -} - -func makeBlock(state State, height int64) *types.Block { - block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit), nil, state.Validators.GetProposer().Address) - return block -} - -//---------------------------------------------------------------------------- - -type testApp struct { - abci.BaseApplication - - CommitVotes []abci.VoteInfo - ByzantineValidators []abci.Evidence - ValidatorUpdates []abci.ValidatorUpdate -} - -var _ abci.Application = (*testApp)(nil) - -func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { - return abci.ResponseInfo{} -} - -func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { - app.CommitVotes = req.LastCommitInfo.Votes - app.ByzantineValidators = req.ByzantineValidators - return abci.ResponseBeginBlock{} -} - -func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { - return abci.ResponseEndBlock{ValidatorUpdates: app.ValidatorUpdates} -} - -func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { - return abci.ResponseDeliverTx{Events: []abci.Event{}} -} - -func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { - return abci.ResponseCheckTx{} -} - -func (app *testApp) Commit() abci.ResponseCommit { - return abci.ResponseCommit{} -} - -func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { - return -} diff --git a/state/export_test.go b/state/export_test.go new file mode 100644 index 000000000..af7f5cc23 --- /dev/null +++ b/state/export_test.go @@ -0,0 +1,62 @@ +package state + +import ( + abci "github.com/tendermint/tendermint/abci/types" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/types" +) + +// +// TODO: Remove dependence on all entities exported from this file. +// +// Every entity exported here is dependent on a private entity from the `state` +// package. Currently, these functions are only made available to tests in the +// `state_test` package, but we should not be relying on them for our testing. +// Instead, we should be exclusively relying on exported entities for our +// testing, and should be refactoring exported entities to make them more +// easily testable from outside of the package. +// + +const ValSetCheckpointInterval = valSetCheckpointInterval + +// UpdateState is an alias for updateState exported from execution.go, +// exclusively and explicitly for testing. +func UpdateState( + state State, + blockID types.BlockID, + header *types.Header, + abciResponses *ABCIResponses, + validatorUpdates []*types.Validator, +) (State, error) { + return updateState(state, blockID, header, abciResponses, validatorUpdates) +} + +// ValidateValidatorUpdates is an alias for validateValidatorUpdates exported +// from execution.go, exclusively and explicitly for testing. +func ValidateValidatorUpdates(abciUpdates []abci.ValidatorUpdate, params types.ValidatorParams) error { + return validateValidatorUpdates(abciUpdates, params) +} + +// CalcValidatorsKey is an alias for the private calcValidatorsKey method in +// store.go, exported exclusively and explicitly for testing. +func CalcValidatorsKey(height int64) []byte { + return calcValidatorsKey(height) +} + +// SaveABCIResponses is an alias for the private saveABCIResponses method in +// store.go, exported exclusively and explicitly for testing. +func SaveABCIResponses(db dbm.DB, height int64, abciResponses *ABCIResponses) { + saveABCIResponses(db, height, abciResponses) +} + +// SaveConsensusParamsInfo is an alias for the private saveConsensusParamsInfo +// method in store.go, exported exclusively and explicitly for testing. +func SaveConsensusParamsInfo(db dbm.DB, nextHeight, changeHeight int64, params types.ConsensusParams) { + saveConsensusParamsInfo(db, nextHeight, changeHeight, params) +} + +// SaveValidatorsInfo is an alias for the private saveValidatorsInfo method in +// store.go, exported exclusively and explicitly for testing. +func SaveValidatorsInfo(db dbm.DB, height, lastHeightChanged int64, valSet *types.ValidatorSet) { + saveValidatorsInfo(db, height, lastHeightChanged, valSet) +} diff --git a/state/helpers_test.go b/state/helpers_test.go new file mode 100644 index 000000000..e8cb27585 --- /dev/null +++ b/state/helpers_test.go @@ -0,0 +1,280 @@ +package state_test + +import ( + "bytes" + "fmt" + + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/ed25519" + dbm "github.com/tendermint/tendermint/libs/db" + "github.com/tendermint/tendermint/proxy" + sm "github.com/tendermint/tendermint/state" + "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" +) + +type paramsChangeTestCase struct { + height int64 + params types.ConsensusParams +} + +// always returns true if asked if any evidence was already committed. +type mockEvPoolAlwaysCommitted struct{} + +func (m mockEvPoolAlwaysCommitted) PendingEvidence(int64) []types.Evidence { return nil } +func (m mockEvPoolAlwaysCommitted) AddEvidence(types.Evidence) error { return nil } +func (m mockEvPoolAlwaysCommitted) Update(*types.Block, sm.State) {} +func (m mockEvPoolAlwaysCommitted) IsCommitted(types.Evidence) bool { return true } + +func newTestApp() proxy.AppConns { + app := &testApp{} + cc := proxy.NewLocalClientCreator(app) + return proxy.NewAppConns(cc) +} + +func makeAndCommitGoodBlock( + state sm.State, + height int64, + lastCommit *types.Commit, + proposerAddr []byte, + blockExec *sm.BlockExecutor, + privVals map[string]types.PrivValidator, + evidence []types.Evidence) (sm.State, types.BlockID, *types.Commit, error) { + // A good block passes + state, blockID, err := makeAndApplyGoodBlock(state, height, lastCommit, proposerAddr, blockExec, evidence) + if err != nil { + return state, types.BlockID{}, nil, err + } + + // Simulate a lastCommit for this block from all validators for the next height + commit, err := makeValidCommit(height, blockID, state.Validators, privVals) + if err != nil { + return state, types.BlockID{}, nil, err + } + return state, blockID, commit, nil +} + +func makeAndApplyGoodBlock(state sm.State, height int64, lastCommit *types.Commit, proposerAddr []byte, + blockExec *sm.BlockExecutor, evidence []types.Evidence) (sm.State, types.BlockID, error) { + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr) + if err := blockExec.ValidateBlock(state, block); err != nil { + return state, types.BlockID{}, err + } + blockID := types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}} + state, err := blockExec.ApplyBlock(state, blockID, block) + if err != nil { + return state, types.BlockID{}, err + } + return state, blockID, nil +} + +func makeVote(height int64, blockID types.BlockID, valSet *types.ValidatorSet, privVal types.PrivValidator) (*types.Vote, error) { + addr := privVal.GetPubKey().Address() + idx, _ := valSet.GetByAddress(addr) + vote := &types.Vote{ + ValidatorAddress: addr, + ValidatorIndex: idx, + Height: height, + Round: 0, + Timestamp: tmtime.Now(), + Type: types.PrecommitType, + BlockID: blockID, + } + if err := privVal.SignVote(chainID, vote); err != nil { + return nil, err + } + return vote, nil +} + +func makeValidCommit(height int64, blockID types.BlockID, vals *types.ValidatorSet, privVals map[string]types.PrivValidator) (*types.Commit, error) { + sigs := make([]*types.CommitSig, 0) + for i := 0; i < vals.Size(); i++ { + _, val := vals.GetByIndex(i) + vote, err := makeVote(height, blockID, vals, privVals[val.Address.String()]) + if err != nil { + return nil, err + } + sigs = append(sigs, vote.CommitSig()) + } + return types.NewCommit(blockID, sigs), nil +} + +// make some bogus txs +func makeTxs(height int64) (txs []types.Tx) { + for i := 0; i < nTxsPerBlock; i++ { + txs = append(txs, types.Tx([]byte{byte(height), byte(i)})) + } + return txs +} + +func makeState(nVals, height int) (sm.State, dbm.DB, map[string]types.PrivValidator) { + vals := make([]types.GenesisValidator, nVals) + privVals := make(map[string]types.PrivValidator, nVals) + for i := 0; i < nVals; i++ { + secret := []byte(fmt.Sprintf("test%d", i)) + pk := ed25519.GenPrivKeyFromSecret(secret) + valAddr := pk.PubKey().Address() + vals[i] = types.GenesisValidator{ + Address: valAddr, + PubKey: pk.PubKey(), + Power: 1000, + Name: fmt.Sprintf("test%d", i), + } + privVals[valAddr.String()] = types.NewMockPVWithParams(pk, false, false) + } + s, _ := sm.MakeGenesisState(&types.GenesisDoc{ + ChainID: chainID, + Validators: vals, + AppHash: nil, + }) + + stateDB := dbm.NewMemDB() + sm.SaveState(stateDB, s) + + for i := 1; i < height; i++ { + s.LastBlockHeight++ + s.LastValidators = s.Validators.Copy() + sm.SaveState(stateDB, s) + } + return s, stateDB, privVals +} + +func makeBlock(state sm.State, height int64) *types.Block { + block, _ := state.MakeBlock(height, makeTxs(state.LastBlockHeight), new(types.Commit), nil, state.Validators.GetProposer().Address) + return block +} + +func genValSet(size int) *types.ValidatorSet { + vals := make([]*types.Validator, size) + for i := 0; i < size; i++ { + vals[i] = types.NewValidator(ed25519.GenPrivKey().PubKey(), 10) + } + return types.NewValidatorSet(vals) +} + +func makeConsensusParams( + blockBytes, blockGas int64, + blockTimeIotaMs int64, + evidenceAge int64, +) types.ConsensusParams { + return types.ConsensusParams{ + Block: types.BlockParams{ + MaxBytes: blockBytes, + MaxGas: blockGas, + TimeIotaMs: blockTimeIotaMs, + }, + Evidence: types.EvidenceParams{ + MaxAge: evidenceAge, + }, + } +} + +func makeHeaderPartsResponsesValPubKeyChange(state sm.State, pubkey crypto.PubKey) (types.Header, types.BlockID, *sm.ABCIResponses) { + + block := makeBlock(state, state.LastBlockHeight+1) + abciResponses := &sm.ABCIResponses{ + EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, + } + + // If the pubkey is new, remove the old and add the new. + _, val := state.NextValidators.GetByIndex(0) + if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) { + abciResponses.EndBlock = &abci.ResponseEndBlock{ + ValidatorUpdates: []abci.ValidatorUpdate{ + types.TM2PB.NewValidatorUpdate(val.PubKey, 0), + types.TM2PB.NewValidatorUpdate(pubkey, 10), + }, + } + } + + return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses +} + +func makeHeaderPartsResponsesValPowerChange(state sm.State, power int64) (types.Header, types.BlockID, *sm.ABCIResponses) { + + block := makeBlock(state, state.LastBlockHeight+1) + abciResponses := &sm.ABCIResponses{ + EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, + } + + // If the pubkey is new, remove the old and add the new. + _, val := state.NextValidators.GetByIndex(0) + if val.VotingPower != power { + abciResponses.EndBlock = &abci.ResponseEndBlock{ + ValidatorUpdates: []abci.ValidatorUpdate{ + types.TM2PB.NewValidatorUpdate(val.PubKey, power), + }, + } + } + + return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses +} + +func makeHeaderPartsResponsesParams(state sm.State, params types.ConsensusParams) (types.Header, types.BlockID, *sm.ABCIResponses) { + + block := makeBlock(state, state.LastBlockHeight+1) + abciResponses := &sm.ABCIResponses{ + EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)}, + } + return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses +} + +func randomGenesisDoc() *types.GenesisDoc { + pubkey := ed25519.GenPrivKey().PubKey() + return &types.GenesisDoc{ + GenesisTime: tmtime.Now(), + ChainID: "abc", + Validators: []types.GenesisValidator{ + { + Address: pubkey.Address(), + PubKey: pubkey, + Power: 10, + Name: "myval", + }, + }, + ConsensusParams: types.DefaultConsensusParams(), + } +} + +//---------------------------------------------------------------------------- + +type testApp struct { + abci.BaseApplication + + CommitVotes []abci.VoteInfo + ByzantineValidators []abci.Evidence + ValidatorUpdates []abci.ValidatorUpdate +} + +var _ abci.Application = (*testApp)(nil) + +func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) { + return abci.ResponseInfo{} +} + +func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock { + app.CommitVotes = req.LastCommitInfo.Votes + app.ByzantineValidators = req.ByzantineValidators + return abci.ResponseBeginBlock{} +} + +func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock { + return abci.ResponseEndBlock{ValidatorUpdates: app.ValidatorUpdates} +} + +func (app *testApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx { + return abci.ResponseDeliverTx{Events: []abci.Event{}} +} + +func (app *testApp) CheckTx(req abci.RequestCheckTx) abci.ResponseCheckTx { + return abci.ResponseCheckTx{} +} + +func (app *testApp) Commit() abci.ResponseCommit { + return abci.ResponseCommit{} +} + +func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) { + return +} diff --git a/state/main_test.go b/state/main_test.go new file mode 100644 index 000000000..00ecf2686 --- /dev/null +++ b/state/main_test.go @@ -0,0 +1,13 @@ +package state_test + +import ( + "os" + "testing" + + "github.com/tendermint/tendermint/types" +) + +func TestMain(m *testing.M) { + types.RegisterMockEvidencesGlobal() + os.Exit(m.Run()) +} diff --git a/state/services.go b/state/services.go index 98f6afce3..10b389ee7 100644 --- a/state/services.go +++ b/state/services.go @@ -43,7 +43,7 @@ type EvidencePool interface { IsCommitted(types.Evidence) bool } -// MockEvidencePool is an empty implementation of a Mempool, useful for testing. +// MockEvidencePool is an empty implementation of EvidencePool, useful for testing. type MockEvidencePool struct{} func (m MockEvidencePool) PendingEvidence(int64) []types.Evidence { return nil } diff --git a/state/state_test.go b/state/state_test.go index 1ff09ccdb..a0f7a4a2a 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -1,4 +1,4 @@ -package state +package state_test import ( "bytes" @@ -10,23 +10,22 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - abci "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" + sm "github.com/tendermint/tendermint/state" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/types" ) // setupTestCase does setup common to all test cases. -func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, State) { +func setupTestCase(t *testing.T) (func(t *testing.T), dbm.DB, sm.State) { config := cfg.ResetTestRoot("state_") dbType := dbm.DBBackendType(config.DBBackend) stateDB := dbm.NewDB("state", dbType, config.DBDir()) - state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) + state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) assert.NoError(t, err, "expected no error on LoadStateFromDBOrGenesisFile") tearDown := func(t *testing.T) { os.RemoveAll(config.RootDir) } @@ -59,7 +58,7 @@ func TestMakeGenesisStateNilValidators(t *testing.T) { Validators: nil, } require.Nil(t, doc.ValidateAndComplete()) - state, err := MakeGenesisState(&doc) + state, err := sm.MakeGenesisState(&doc) require.Nil(t, err) require.Equal(t, 0, len(state.Validators.Validators)) require.Equal(t, 0, len(state.NextValidators.Validators)) @@ -73,9 +72,9 @@ func TestStateSaveLoad(t *testing.T) { assert := assert.New(t) state.LastBlockHeight++ - SaveState(stateDB, state) + sm.SaveState(stateDB, state) - loadedState := LoadState(stateDB) + loadedState := sm.LoadState(stateDB) assert.True(state.Equals(loadedState), fmt.Sprintf("expected state and its copy to be identical.\ngot: %v\nexpected: %v\n", loadedState, state)) @@ -92,15 +91,15 @@ func TestABCIResponsesSaveLoad1(t *testing.T) { // Build mock responses. block := makeBlock(state, 2) - abciResponses := NewABCIResponses(block) + abciResponses := sm.NewABCIResponses(block) abciResponses.DeliverTx[0] = &abci.ResponseDeliverTx{Data: []byte("foo"), Events: nil} abciResponses.DeliverTx[1] = &abci.ResponseDeliverTx{Data: []byte("bar"), Log: "ok", Events: nil} abciResponses.EndBlock = &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{ types.TM2PB.NewValidatorUpdate(ed25519.GenPrivKey().PubKey(), 10), }} - saveABCIResponses(stateDB, block.Height, abciResponses) - loadedABCIResponses, err := LoadABCIResponses(stateDB, block.Height) + sm.SaveABCIResponses(stateDB, block.Height, abciResponses) + loadedABCIResponses, err := sm.LoadABCIResponses(stateDB, block.Height) assert.Nil(err) assert.Equal(abciResponses, loadedABCIResponses, fmt.Sprintf("ABCIResponses don't match:\ngot: %v\nexpected: %v\n", @@ -155,24 +154,24 @@ func TestABCIResponsesSaveLoad2(t *testing.T) { // Query all before, this should return error. for i := range cases { h := int64(i + 1) - res, err := LoadABCIResponses(stateDB, h) + res, err := sm.LoadABCIResponses(stateDB, h) assert.Error(err, "%d: %#v", i, res) } // Add all cases. for i, tc := range cases { h := int64(i + 1) // last block height, one below what we save - responses := &ABCIResponses{ + responses := &sm.ABCIResponses{ DeliverTx: tc.added, EndBlock: &abci.ResponseEndBlock{}, } - saveABCIResponses(stateDB, h, responses) + sm.SaveABCIResponses(stateDB, h, responses) } // Query all before, should return expected value. for i, tc := range cases { h := int64(i + 1) - res, err := LoadABCIResponses(stateDB, h) + res, err := sm.LoadABCIResponses(stateDB, h) assert.NoError(err, "%d", i) assert.Equal(tc.expected.Hash(), res.ResultsHash(), "%d", i) } @@ -186,26 +185,26 @@ func TestValidatorSimpleSaveLoad(t *testing.T) { assert := assert.New(t) // Can't load anything for height 0. - v, err := LoadValidators(stateDB, 0) - assert.IsType(ErrNoValSetForHeight{}, err, "expected err at height 0") + v, err := sm.LoadValidators(stateDB, 0) + assert.IsType(sm.ErrNoValSetForHeight{}, err, "expected err at height 0") // Should be able to load for height 1. - v, err = LoadValidators(stateDB, 1) + v, err = sm.LoadValidators(stateDB, 1) assert.Nil(err, "expected no err at height 1") assert.Equal(v.Hash(), state.Validators.Hash(), "expected validator hashes to match") // Should be able to load for height 2. - v, err = LoadValidators(stateDB, 2) + v, err = sm.LoadValidators(stateDB, 2) assert.Nil(err, "expected no err at height 2") assert.Equal(v.Hash(), state.NextValidators.Hash(), "expected validator hashes to match") // Increment height, save; should be able to load for next & next next height. state.LastBlockHeight++ nextHeight := state.LastBlockHeight + 1 - saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) - vp0, err := LoadValidators(stateDB, nextHeight+0) + sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) + vp0, err := sm.LoadValidators(stateDB, nextHeight+0) assert.Nil(err, "expected no err") - vp1, err := LoadValidators(stateDB, nextHeight+1) + vp1, err := sm.LoadValidators(stateDB, nextHeight+1) assert.Nil(err, "expected no err") assert.Equal(vp0.Hash(), state.Validators.Hash(), "expected validator hashes to match") assert.Equal(vp1.Hash(), state.NextValidators.Hash(), "expected next validator hashes to match") @@ -234,13 +233,13 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) { changeIndex++ power++ } - header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, i, power) + header, blockID, responses := makeHeaderPartsResponsesValPowerChange(state, power) validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) require.NoError(t, err) - state, err = updateState(state, blockID, &header, responses, validatorUpdates) + state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) require.NoError(t, err) nextHeight := state.LastBlockHeight + 1 - saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) + sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) } // On each height change, increment the power by one. @@ -258,7 +257,7 @@ func TestOneValidatorChangesSaveLoad(t *testing.T) { } for i, power := range testCases { - v, err := LoadValidators(stateDB, int64(i+1+1)) // +1 because vset changes delayed by 1 block. + v, err := sm.LoadValidators(stateDB, int64(i+1+1)) // +1 because vset changes delayed by 1 block. assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", i)) assert.Equal(t, v.Size(), 1, "validator set size is greater than 1: %d", v.Size()) _, val := v.GetByIndex(0) @@ -405,12 +404,12 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { block := makeBlock(state, state.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) - updatedState, err := updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) curTotal := val1VotingPower // one increment step and one validator: 0 + power - total_power == 0 @@ -422,7 +421,7 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { updateAddVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(val2PubKey), Power: val2VotingPower} validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal}) assert.NoError(t, err) - updatedState2, err := updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState2, err := sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState2.NextValidators.Validators), 2) @@ -461,7 +460,7 @@ func TestProposerPriorityDoesNotGetResetToZero(t *testing.T) { // this will cause the diff of priorities (77) // to be larger than threshold == 2*totalVotingPower (22): - updatedState3, err := updateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState3.NextValidators.Validators), 2) @@ -516,13 +515,13 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { block := makeBlock(state, state.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} // no updates: - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) - updatedState, err := updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err := sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) // 0 + 10 (initial prio) - 10 (avg) - 10 (mostest - total) = -10 @@ -537,7 +536,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{updateAddVal}) assert.NoError(t, err) - updatedState2, err := updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState2, err := sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) require.Equal(t, len(updatedState2.NextValidators.Validators), 2) @@ -574,7 +573,7 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) - updatedState3, err := updateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState3, err := sm.UpdateState(updatedState2, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) assert.Equal(t, updatedState3.Validators.Proposer.Address, updatedState3.NextValidators.Proposer.Address) @@ -598,13 +597,13 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { // no changes in voting power and both validators have same voting power // -> proposers should alternate: oldState := updatedState3 - abciResponses = &ABCIResponses{ + abciResponses = &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) - oldState, err = updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + oldState, err = sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) expectedVal1Prio2 = 1 expectedVal2Prio2 = -1 @@ -613,13 +612,13 @@ func TestProposerPriorityProposerAlternates(t *testing.T) { for i := 0; i < 1000; i++ { // no validator updates: - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) - updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) assert.NoError(t, err) // alternate (and cyclic priorities): assert.NotEqual(t, updatedState.Validators.Proposer.Address, updatedState.NextValidators.Proposer.Address, "iter: %v", i) @@ -660,7 +659,7 @@ func TestLargeGenesisValidator(t *testing.T) { oldState := state for i := 0; i < 10; i++ { // no updates: - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -669,7 +668,7 @@ func TestLargeGenesisValidator(t *testing.T) { block := makeBlock(oldState, oldState.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) // no changes in voting power (ProposerPrio += VotingPower == Voting in 1st round; than shiftByAvg == 0, // than -Total == -Voting) @@ -689,18 +688,18 @@ func TestLargeGenesisValidator(t *testing.T) { firstAddedVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(firstAddedValPubKey), Power: firstAddedValVotingPower} validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{firstAddedVal}) assert.NoError(t, err) - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{firstAddedVal}}, } block := makeBlock(oldState, oldState.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - updatedState, err := updateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err := sm.UpdateState(oldState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) lastState := updatedState for i := 0; i < 200; i++ { // no updates: - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -709,7 +708,7 @@ func TestLargeGenesisValidator(t *testing.T) { block := makeBlock(lastState, lastState.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - updatedStateInner, err := updateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedStateInner, err := sm.UpdateState(lastState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) lastState = updatedStateInner } @@ -734,26 +733,26 @@ func TestLargeGenesisValidator(t *testing.T) { validatorUpdates, err := types.PB2TM.ValidatorUpdates([]abci.ValidatorUpdate{addedVal}) assert.NoError(t, err) - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{addedVal}}, } block := makeBlock(oldState, oldState.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - state, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) + state, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) } require.Equal(t, 10+2, len(state.NextValidators.Validators)) // remove genesis validator: removeGenesisVal := abci.ValidatorUpdate{PubKey: types.TM2PB.PubKey(genesisPubKey), Power: 0} - abciResponses = &ABCIResponses{ + abciResponses = &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: []abci.ValidatorUpdate{removeGenesisVal}}, } block = makeBlock(oldState, oldState.LastBlockHeight+1) blockID = types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) - updatedState, err = updateState(state, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err = sm.UpdateState(state, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) // only the first added val (not the genesis val) should be left assert.Equal(t, 11, len(updatedState.NextValidators.Validators)) @@ -764,14 +763,14 @@ func TestLargeGenesisValidator(t *testing.T) { count := 0 isProposerUnchanged := true for isProposerUnchanged { - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err = types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) require.NoError(t, err) block = makeBlock(curState, curState.LastBlockHeight+1) blockID = types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - curState, err = updateState(curState, blockID, &block.Header, abciResponses, validatorUpdates) + curState, err = sm.UpdateState(curState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) if !bytes.Equal(curState.Validators.Proposer.Address, curState.NextValidators.Proposer.Address) { isProposerUnchanged = false @@ -787,7 +786,7 @@ func TestLargeGenesisValidator(t *testing.T) { proposers := make([]*types.Validator, numVals) for i := 0; i < 100; i++ { // no updates: - abciResponses := &ABCIResponses{ + abciResponses := &sm.ABCIResponses{ EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, } validatorUpdates, err := types.PB2TM.ValidatorUpdates(abciResponses.EndBlock.ValidatorUpdates) @@ -796,7 +795,7 @@ func TestLargeGenesisValidator(t *testing.T) { block := makeBlock(updatedState, updatedState.LastBlockHeight+1) blockID := types.BlockID{Hash: block.Hash(), PartsHeader: block.MakePartSet(testPartSize).Header()} - updatedState, err = updateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) + updatedState, err = sm.UpdateState(updatedState, blockID, &block.Header, abciResponses, validatorUpdates) require.NoError(t, err) if i > numVals { // expect proposers to cycle through after the first iteration (of numVals blocks): if proposers[i%numVals] == nil { @@ -814,15 +813,15 @@ func TestStoreLoadValidatorsIncrementsProposerPriority(t *testing.T) { defer tearDown(t) state.Validators = genValSet(valSetSize) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) - SaveState(stateDB, state) + sm.SaveState(stateDB, state) nextHeight := state.LastBlockHeight + 1 - v0, err := LoadValidators(stateDB, nextHeight) + v0, err := sm.LoadValidators(stateDB, nextHeight) assert.Nil(t, err) acc0 := v0.Validators[0].ProposerPriority - v1, err := LoadValidators(stateDB, nextHeight+1) + v1, err := sm.LoadValidators(stateDB, nextHeight+1) assert.Nil(t, err) acc1 := v1.Validators[0].ProposerPriority @@ -838,28 +837,27 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { require.Equal(t, int64(0), state.LastBlockHeight) state.Validators = genValSet(valSetSize) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) - SaveState(stateDB, state) + sm.SaveState(stateDB, state) _, valOld := state.Validators.GetByIndex(0) var pubkeyOld = valOld.PubKey pubkey := ed25519.GenPrivKey().PubKey() - const height = 1 // Swap the first validator with a new one (validator set size stays the same). - header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, height, pubkey) + header, blockID, responses := makeHeaderPartsResponsesValPubKeyChange(state, pubkey) // Save state etc. var err error var validatorUpdates []*types.Validator validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) require.NoError(t, err) - state, err = updateState(state, blockID, &header, responses, validatorUpdates) + state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) require.Nil(t, err) nextHeight := state.LastBlockHeight + 1 - saveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) + sm.SaveValidatorsInfo(stateDB, nextHeight+1, state.LastHeightValidatorsChanged, state.NextValidators) // Load nextheight, it should be the oldpubkey. - v0, err := LoadValidators(stateDB, nextHeight) + v0, err := sm.LoadValidators(stateDB, nextHeight) assert.Nil(t, err) assert.Equal(t, valSetSize, v0.Size()) index, val := v0.GetByAddress(pubkeyOld.Address()) @@ -869,7 +867,7 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { } // Load nextheight+1, it should be the new pubkey. - v1, err := LoadValidators(stateDB, nextHeight+1) + v1, err := sm.LoadValidators(stateDB, nextHeight+1) assert.Nil(t, err) assert.Equal(t, valSetSize, v1.Size()) index, val = v1.GetByAddress(pubkey.Address()) @@ -879,14 +877,6 @@ func TestManyValidatorChangesSaveLoad(t *testing.T) { } } -func genValSet(size int) *types.ValidatorSet { - vals := make([]*types.Validator, size) - for i := 0; i < size; i++ { - vals[i] = types.NewValidator(ed25519.GenPrivKey().PubKey(), 10) - } - return types.NewValidatorSet(vals) -} - func TestStateMakeBlock(t *testing.T) { tearDown, _, state := setupTestCase(t) defer tearDown(t) @@ -932,14 +922,14 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { changeIndex++ cp = params[changeIndex] } - header, blockID, responses := makeHeaderPartsResponsesParams(state, i, cp) + header, blockID, responses := makeHeaderPartsResponsesParams(state, cp) validatorUpdates, err = types.PB2TM.ValidatorUpdates(responses.EndBlock.ValidatorUpdates) require.NoError(t, err) - state, err = updateState(state, blockID, &header, responses, validatorUpdates) + state, err = sm.UpdateState(state, blockID, &header, responses, validatorUpdates) require.Nil(t, err) nextHeight := state.LastBlockHeight + 1 - saveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams) + sm.SaveConsensusParamsInfo(stateDB, nextHeight, state.LastHeightConsensusParamsChanged, state.ConsensusParams) } // Make all the test cases by using the same params until after the change. @@ -957,32 +947,15 @@ func TestConsensusParamsChangesSaveLoad(t *testing.T) { } for _, testCase := range testCases { - p, err := LoadConsensusParams(stateDB, testCase.height) + p, err := sm.LoadConsensusParams(stateDB, testCase.height) assert.Nil(t, err, fmt.Sprintf("expected no err at height %d", testCase.height)) assert.Equal(t, testCase.params, p, fmt.Sprintf(`unexpected consensus params at height %d`, testCase.height)) } } -func makeParams( - blockBytes, blockGas int64, - blockTimeIotaMs int64, - evidenceAge int64, -) types.ConsensusParams { - return types.ConsensusParams{ - Block: types.BlockParams{ - MaxBytes: blockBytes, - MaxGas: blockGas, - TimeIotaMs: blockTimeIotaMs, - }, - Evidence: types.EvidenceParams{ - MaxAge: evidenceAge, - }, - } -} - func TestApplyUpdates(t *testing.T) { - initParams := makeParams(1, 2, 3, 4) + initParams := makeConsensusParams(1, 2, 3, 4) cases := [...]struct { init types.ConsensusParams @@ -998,14 +971,14 @@ func TestApplyUpdates(t *testing.T) { MaxGas: 55, }, }, - makeParams(44, 55, 3, 4)}, + makeConsensusParams(44, 55, 3, 4)}, 3: {initParams, abci.ConsensusParams{ Evidence: &abci.EvidenceParams{ MaxAge: 66, }, }, - makeParams(1, 2, 3, 66)}, + makeConsensusParams(1, 2, 3, 66)}, } for i, tc := range cases { @@ -1013,61 +986,3 @@ func TestApplyUpdates(t *testing.T) { assert.Equal(t, tc.expected, res, "case %d", i) } } - -func makeHeaderPartsResponsesValPubKeyChange(state State, height int64, - pubkey crypto.PubKey) (types.Header, types.BlockID, *ABCIResponses) { - - block := makeBlock(state, state.LastBlockHeight+1) - abciResponses := &ABCIResponses{ - EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, - } - - // If the pubkey is new, remove the old and add the new. - _, val := state.NextValidators.GetByIndex(0) - if !bytes.Equal(pubkey.Bytes(), val.PubKey.Bytes()) { - abciResponses.EndBlock = &abci.ResponseEndBlock{ - ValidatorUpdates: []abci.ValidatorUpdate{ - types.TM2PB.NewValidatorUpdate(val.PubKey, 0), - types.TM2PB.NewValidatorUpdate(pubkey, 10), - }, - } - } - - return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses -} - -func makeHeaderPartsResponsesValPowerChange(state State, height int64, - power int64) (types.Header, types.BlockID, *ABCIResponses) { - - block := makeBlock(state, state.LastBlockHeight+1) - abciResponses := &ABCIResponses{ - EndBlock: &abci.ResponseEndBlock{ValidatorUpdates: nil}, - } - - // If the pubkey is new, remove the old and add the new. - _, val := state.NextValidators.GetByIndex(0) - if val.VotingPower != power { - abciResponses.EndBlock = &abci.ResponseEndBlock{ - ValidatorUpdates: []abci.ValidatorUpdate{ - types.TM2PB.NewValidatorUpdate(val.PubKey, power), - }, - } - } - - return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses -} - -func makeHeaderPartsResponsesParams(state State, height int64, - params types.ConsensusParams) (types.Header, types.BlockID, *ABCIResponses) { - - block := makeBlock(state, state.LastBlockHeight+1) - abciResponses := &ABCIResponses{ - EndBlock: &abci.ResponseEndBlock{ConsensusParamUpdates: types.TM2PB.ConsensusParams(¶ms)}, - } - return block.Header, types.BlockID{Hash: block.Hash(), PartsHeader: types.PartSetHeader{}}, abciResponses -} - -type paramsChangeTestCase struct { - height int64 - params types.ConsensusParams -} diff --git a/state/store_test.go b/state/store_test.go index 06adeefab..0cf217722 100644 --- a/state/store_test.go +++ b/state/store_test.go @@ -1,4 +1,4 @@ -package state +package state_test import ( "fmt" @@ -10,6 +10,7 @@ import ( cfg "github.com/tendermint/tendermint/config" dbm "github.com/tendermint/tendermint/libs/db" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" ) @@ -19,9 +20,9 @@ func TestStoreLoadValidators(t *testing.T) { vals := types.NewValidatorSet([]*types.Validator{val}) // 1) LoadValidators loads validators using a height where they were last changed - saveValidatorsInfo(stateDB, 1, 1, vals) - saveValidatorsInfo(stateDB, 2, 1, vals) - loadedVals, err := LoadValidators(stateDB, 2) + sm.SaveValidatorsInfo(stateDB, 1, 1, vals) + sm.SaveValidatorsInfo(stateDB, 2, 1, vals) + loadedVals, err := sm.LoadValidators(stateDB, 2) require.NoError(t, err) assert.NotZero(t, loadedVals.Size()) @@ -30,13 +31,13 @@ func TestStoreLoadValidators(t *testing.T) { // TODO(melekes): REMOVE in 0.33 release // https://github.com/tendermint/tendermint/issues/3543 // for releases prior to v0.31.4, it uses last height changed - valInfo := &ValidatorsInfo{ - LastHeightChanged: valSetCheckpointInterval, + valInfo := &sm.ValidatorsInfo{ + LastHeightChanged: sm.ValSetCheckpointInterval, } - stateDB.Set(calcValidatorsKey(valSetCheckpointInterval), valInfo.Bytes()) + stateDB.Set(sm.CalcValidatorsKey(sm.ValSetCheckpointInterval), valInfo.Bytes()) assert.NotPanics(t, func() { - saveValidatorsInfo(stateDB, valSetCheckpointInterval+1, 1, vals) - loadedVals, err := LoadValidators(stateDB, valSetCheckpointInterval+1) + sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval+1, 1, vals) + loadedVals, err := sm.LoadValidators(stateDB, sm.ValSetCheckpointInterval+1) if err != nil { t.Fatal(err) } @@ -46,9 +47,9 @@ func TestStoreLoadValidators(t *testing.T) { }) // ENDREMOVE - saveValidatorsInfo(stateDB, valSetCheckpointInterval, 1, vals) + sm.SaveValidatorsInfo(stateDB, sm.ValSetCheckpointInterval, 1, vals) - loadedVals, err = LoadValidators(stateDB, valSetCheckpointInterval) + loadedVals, err = sm.LoadValidators(stateDB, sm.ValSetCheckpointInterval) require.NoError(t, err) assert.NotZero(t, loadedVals.Size()) } @@ -60,20 +61,20 @@ func BenchmarkLoadValidators(b *testing.B) { defer os.RemoveAll(config.RootDir) dbType := dbm.DBBackendType(config.DBBackend) stateDB := dbm.NewDB("state", dbType, config.DBDir()) - state, err := LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) + state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile()) if err != nil { b.Fatal(err) } state.Validators = genValSet(valSetSize) state.NextValidators = state.Validators.CopyIncrementProposerPriority(1) - SaveState(stateDB, state) + sm.SaveState(stateDB, state) for i := 10; i < 10000000000; i *= 10 { // 10, 100, 1000, ... - saveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators) + sm.SaveValidatorsInfo(stateDB, int64(i), state.LastHeightValidatorsChanged, state.NextValidators) b.Run(fmt.Sprintf("height=%d", i), func(b *testing.B) { for n := 0; n < b.N; n++ { - _, err := LoadValidators(stateDB, int64(i)) + _, err := sm.LoadValidators(stateDB, int64(i)) if err != nil { b.Fatal(err) } diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index e48ad2c3e..bd3243168 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -1,4 +1,4 @@ -package state +package state_test import ( "os" @@ -7,11 +7,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/ed25519" cmn "github.com/tendermint/tendermint/libs/common" dbm "github.com/tendermint/tendermint/libs/db" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" - tmtime "github.com/tendermint/tendermint/types/time" ) func TestTxFilter(t *testing.T) { @@ -34,10 +33,10 @@ func TestTxFilter(t *testing.T) { for i, tc := range testCases { stateDB := dbm.NewDB("state", "memdb", os.TempDir()) - state, err := LoadStateFromDBOrGenesisDoc(stateDB, genDoc) + state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc) require.NoError(t, err) - f := TxPreCheck(state) + f := sm.TxPreCheck(state) if tc.isErr { assert.NotNil(t, f(tc.tx), "#%v", i) } else { @@ -45,13 +44,3 @@ func TestTxFilter(t *testing.T) { } } } - -func randomGenesisDoc() *types.GenesisDoc { - pubkey := ed25519.GenPrivKey().PubKey() - return &types.GenesisDoc{ - GenesisTime: tmtime.Now(), - ChainID: "abc", - Validators: []types.GenesisValidator{{Address: pubkey.Address(), PubKey: pubkey, Power: 10, Name: "myval"}}, - ConsensusParams: types.DefaultConsensusParams(), - } -} diff --git a/state/validation.go b/state/validation.go index 3c63c35b7..1d365e90c 100644 --- a/state/validation.go +++ b/state/validation.go @@ -94,10 +94,7 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, block } } else { if len(block.LastCommit.Precommits) != state.LastValidators.Size() { - return fmt.Errorf("Invalid block commit size. Expected %v, got %v", - state.LastValidators.Size(), - len(block.LastCommit.Precommits), - ) + return types.NewErrInvalidCommitPrecommits(state.LastValidators.Size(), len(block.LastCommit.Precommits)) } err := state.LastValidators.VerifyCommit( state.ChainID, state.LastBlockID, block.Height-1, block.LastCommit) diff --git a/state/validation_test.go b/state/validation_test.go index 705f843df..c53cf0102 100644 --- a/state/validation_test.go +++ b/state/validation_test.go @@ -1,31 +1,30 @@ -package state +package state_test import ( "testing" "time" "github.com/stretchr/testify/require" + "github.com/tendermint/tendermint/mock" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" "github.com/tendermint/tendermint/libs/log" + sm "github.com/tendermint/tendermint/state" "github.com/tendermint/tendermint/types" + tmtime "github.com/tendermint/tendermint/types/time" ) -// TODO(#2589): -// - generalize this past the first height -// - add txs and build up full State properly -// - test block.Time (see #2587 - there are no conditions on time for the first height) +const validationTestsStopHeight int64 = 10 + func TestValidateBlockHeader(t *testing.T) { - var height int64 = 1 // TODO(#2589): generalize - state, stateDB := state(1, int(height)) + proxyApp := newTestApp() + require.NoError(t, proxyApp.Start()) + defer proxyApp.Stop() - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, nil) - - // A good block passes. - block := makeBlock(state, height) - err := blockExec.ValidateBlock(state, block) - require.NoError(t, err) + state, stateDB, privVals := makeState(3, 1) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) + lastCommit := types.NewCommit(types.BlockID{}, nil) // some bad values wrongHash := tmhash.Sum([]byte("this hash is wrong")) @@ -43,7 +42,7 @@ func TestValidateBlockHeader(t *testing.T) { {"Version wrong2", func(block *types.Block) { block.Version = wrongVersion2 }}, {"ChainID wrong", func(block *types.Block) { block.ChainID = "not-the-real-one" }}, {"Height wrong", func(block *types.Block) { block.Height += 10 }}, - {"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 3600 * 24) }}, + {"Time wrong", func(block *types.Block) { block.Time = block.Time.Add(-time.Second * 1) }}, {"NumTxs wrong", func(block *types.Block) { block.NumTxs += 10 }}, {"TotalTxs wrong", func(block *types.Block) { block.TotalTxs += 10 }}, @@ -62,78 +61,145 @@ func TestValidateBlockHeader(t *testing.T) { {"Proposer invalid", func(block *types.Block) { block.ProposerAddress = []byte("wrong size") }}, } - for _, tc := range testCases { - block := makeBlock(state, height) - tc.malleateBlock(block) - err := blockExec.ValidateBlock(state, block) - require.Error(t, err, tc.name) + // Build up state for multiple heights + for height := int64(1); height < validationTestsStopHeight; height++ { + proposerAddr := state.Validators.GetProposer().Address + /* + Invalid blocks don't pass + */ + for _, tc := range testCases { + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, proposerAddr) + tc.malleateBlock(block) + err := blockExec.ValidateBlock(state, block) + require.Error(t, err, tc.name) + } + + /* + A good block passes + */ + var err error + state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, nil) + require.NoError(t, err, "height %d", height) } } -/* - TODO(#2589): - - test Block.Data.Hash() == Block.DataHash - - test len(Block.Data.Txs) == Block.NumTxs -*/ -func TestValidateBlockData(t *testing.T) { -} - -/* - TODO(#2589): - - test len(block.LastCommit.Precommits) == state.LastValidators.Size() - - test state.LastValidators.VerifyCommit -*/ func TestValidateBlockCommit(t *testing.T) { -} + proxyApp := newTestApp() + require.NoError(t, proxyApp.Start()) + defer proxyApp.Stop() -/* - TODO(#2589): - - test good/bad evidence in block -*/ -func TestValidateBlockEvidence(t *testing.T) { - var height int64 = 1 // TODO(#2589): generalize - state, stateDB := state(1, int(height)) + state, stateDB, privVals := makeState(1, 1) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) + lastCommit := types.NewCommit(types.BlockID{}, nil) + wrongPrecommitsCommit := types.NewCommit(types.BlockID{}, nil) + badPrivVal := types.NewMockPV() - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, nil) + for height := int64(1); height < validationTestsStopHeight; height++ { + proposerAddr := state.Validators.GetProposer().Address + if height > 1 { + /* + #2589: ensure state.LastValidators.VerifyCommit fails here + */ + // should be height-1 instead of height + wrongHeightVote, err := makeVote(height, state.LastBlockID, state.Validators, privVals[proposerAddr.String()]) + require.NoError(t, err, "height %d", height) + wrongHeightCommit := types.NewCommit(state.LastBlockID, []*types.CommitSig{wrongHeightVote.CommitSig()}) + block, _ := state.MakeBlock(height, makeTxs(height), wrongHeightCommit, nil, proposerAddr) + err = blockExec.ValidateBlock(state, block) + _, isErrInvalidCommitHeight := err.(types.ErrInvalidCommitHeight) + require.True(t, isErrInvalidCommitHeight, "expected ErrInvalidCommitHeight at height %d but got: %v", height, err) - // make some evidence - addr, _ := state.Validators.GetByIndex(0) - goodEvidence := types.NewMockGoodEvidence(height, 0, addr) + /* + #2589: test len(block.LastCommit.Precommits) == state.LastValidators.Size() + */ + block, _ = state.MakeBlock(height, makeTxs(height), wrongPrecommitsCommit, nil, proposerAddr) + err = blockExec.ValidateBlock(state, block) + _, isErrInvalidCommitPrecommits := err.(types.ErrInvalidCommitPrecommits) + require.True(t, isErrInvalidCommitPrecommits, "expected ErrInvalidCommitPrecommits at height %d but got: %v", height, err) + } - // A block with a couple pieces of evidence passes. - block := makeBlock(state, height) - block.Evidence.Evidence = []types.Evidence{goodEvidence, goodEvidence} - block.EvidenceHash = block.Evidence.Hash() - err := blockExec.ValidateBlock(state, block) - require.NoError(t, err) + /* + A good block passes + */ + var err error + var blockID types.BlockID + state, blockID, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, nil) + require.NoError(t, err, "height %d", height) - // A block with too much evidence fails. - maxBlockSize := state.ConsensusParams.Block.MaxBytes - maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize) - require.True(t, maxNumEvidence > 2) - for i := int64(0); i < maxNumEvidence; i++ { - block.Evidence.Evidence = append(block.Evidence.Evidence, goodEvidence) + /* + wrongPrecommitsCommit is fine except for the extra bad precommit + */ + goodVote, err := makeVote(height, blockID, state.Validators, privVals[proposerAddr.String()]) + require.NoError(t, err, "height %d", height) + badVote := &types.Vote{ + ValidatorAddress: badPrivVal.GetPubKey().Address(), + ValidatorIndex: 0, + Height: height, + Round: 0, + Timestamp: tmtime.Now(), + Type: types.PrecommitType, + BlockID: blockID, + } + err = badPrivVal.SignVote(chainID, goodVote) + require.NoError(t, err, "height %d", height) + wrongPrecommitsCommit = types.NewCommit(blockID, []*types.CommitSig{goodVote.CommitSig(), badVote.CommitSig()}) } - block.EvidenceHash = block.Evidence.Hash() - err = blockExec.ValidateBlock(state, block) - require.Error(t, err) - _, ok := err.(*types.ErrEvidenceOverflow) - require.True(t, ok) } -// always returns true if asked if any evidence was already committed. -type mockEvPoolAlwaysCommitted struct{} +func TestValidateBlockEvidence(t *testing.T) { + proxyApp := newTestApp() + require.NoError(t, proxyApp.Start()) + defer proxyApp.Stop() -func (m mockEvPoolAlwaysCommitted) PendingEvidence(int64) []types.Evidence { return nil } -func (m mockEvPoolAlwaysCommitted) AddEvidence(types.Evidence) error { return nil } -func (m mockEvPoolAlwaysCommitted) Update(*types.Block, State) {} -func (m mockEvPoolAlwaysCommitted) IsCommitted(types.Evidence) bool { return true } + state, stateDB, privVals := makeState(3, 1) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mock.Mempool{}, sm.MockEvidencePool{}) + lastCommit := types.NewCommit(types.BlockID{}, nil) + + for height := int64(1); height < validationTestsStopHeight; height++ { + proposerAddr := state.Validators.GetProposer().Address + proposerIdx, _ := state.Validators.GetByAddress(proposerAddr) + goodEvidence := types.NewMockGoodEvidence(height, proposerIdx, proposerAddr) + if height > 1 { + /* + A block with too much evidence fails + */ + maxBlockSize := state.ConsensusParams.Block.MaxBytes + maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize) + require.True(t, maxNumEvidence > 2) + evidence := make([]types.Evidence, 0) + // one more than the maximum allowed evidence + for i := int64(0); i <= maxNumEvidence; i++ { + evidence = append(evidence, goodEvidence) + } + block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, evidence, proposerAddr) + err := blockExec.ValidateBlock(state, block) + _, ok := err.(*types.ErrEvidenceOverflow) + require.True(t, ok, "expected error to be of type ErrEvidenceOverflow at height %d", height) + } + + /* + A good block with several pieces of good evidence passes + */ + maxBlockSize := state.ConsensusParams.Block.MaxBytes + maxNumEvidence, _ := types.MaxEvidencePerBlock(maxBlockSize) + require.True(t, maxNumEvidence > 2) + evidence := make([]types.Evidence, 0) + // precisely the amount of allowed evidence + for i := int64(0); i < maxNumEvidence; i++ { + evidence = append(evidence, goodEvidence) + } + + var err error + state, _, lastCommit, err = makeAndCommitGoodBlock(state, height, lastCommit, proposerAddr, blockExec, privVals, evidence) + require.NoError(t, err, "height %d", height) + } +} func TestValidateFailBlockOnCommittedEvidence(t *testing.T) { var height int64 = 1 - state, stateDB := state(1, int(height)) + state, stateDB, _ := makeState(1, int(height)) - blockExec := NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, mockEvPoolAlwaysCommitted{}) + blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), nil, nil, mockEvPoolAlwaysCommitted{}) // A block with a couple pieces of evidence passes. block := makeBlock(state, height) addr, _ := state.Validators.GetByIndex(0) @@ -145,12 +211,3 @@ func TestValidateFailBlockOnCommittedEvidence(t *testing.T) { require.Error(t, err) require.IsType(t, err, &types.ErrEvidenceInvalid{}) } - -/* - TODO(#2589): - - test unmarshalling BlockParts that are too big into a Block that - (note this logic happens in the consensus, not in the validation here). - - test making blocks from the types.MaxXXX functions works/fails as expected -*/ -func TestValidateBlockSize(t *testing.T) { -} diff --git a/types/errors.go b/types/errors.go new file mode 100644 index 000000000..603ac51d7 --- /dev/null +++ b/types/errors.go @@ -0,0 +1,41 @@ +package types + +import "fmt" + +type ( + // ErrInvalidCommitHeight is returned when we encounter a commit with an + // unexpected height. + ErrInvalidCommitHeight struct { + Expected int64 + Actual int64 + } + + // ErrInvalidCommitPrecommits is returned when we encounter a commit where + // the number of precommits doesn't match the number of validators. + ErrInvalidCommitPrecommits struct { + Expected int + Actual int + } +) + +func NewErrInvalidCommitHeight(expected, actual int64) ErrInvalidCommitHeight { + return ErrInvalidCommitHeight{ + Expected: expected, + Actual: actual, + } +} + +func (e ErrInvalidCommitHeight) Error() string { + return fmt.Sprintf("Invalid commit -- wrong height: %v vs %v", e.Expected, e.Actual) +} + +func NewErrInvalidCommitPrecommits(expected, actual int) ErrInvalidCommitPrecommits { + return ErrInvalidCommitPrecommits{ + Expected: expected, + Actual: actual, + } +} + +func (e ErrInvalidCommitPrecommits) Error() string { + return fmt.Sprintf("Invalid commit -- wrong set size: %v vs %v", e.Expected, e.Actual) +} diff --git a/types/validator_set.go b/types/validator_set.go index 9e78fbc77..65358714d 100644 --- a/types/validator_set.go +++ b/types/validator_set.go @@ -594,10 +594,10 @@ func (vals *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height i return err } if vals.Size() != len(commit.Precommits) { - return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", vals.Size(), len(commit.Precommits)) + return NewErrInvalidCommitPrecommits(vals.Size(), len(commit.Precommits)) } if height != commit.Height() { - return fmt.Errorf("Invalid commit -- wrong height: %v vs %v", height, commit.Height()) + return NewErrInvalidCommitHeight(height, commit.Height()) } if !blockID.Equals(commit.BlockID) { return fmt.Errorf("Invalid commit -- wrong block id: want %v got %v", From 8fc83684384423eafbf2a3f421de3ff09216c0bc Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 22 Jun 2019 10:30:23 +0400 Subject: [PATCH 240/259] node: run whole func in goroutine, not just logger.Error fn (#3743) * node: run whole func in goroutine, not just logger.Error fn Fixes #3741 * add a changelog entry --- CHANGELOG_PENDING.md | 1 + node/node.go | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 74aa72c6c..1bff64b64 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -40,3 +40,4 @@ - [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) - [libs/db] \#3718 Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) - [node] \#3716 Fix a bug where `nil` is recorded as node's address +- [node] \#3741 Fix profiler blocking the entire node diff --git a/node/node.go b/node/node.go index 85fef5ee7..c992e2424 100644 --- a/node/node.go +++ b/node/node.go @@ -630,7 +630,9 @@ func NewNode(config *cfg.Config, } if config.ProfListenAddress != "" { - go logger.Error("Profile server", "err", http.ListenAndServe(config.ProfListenAddress, nil)) + go func() { + logger.Error("Profile server", "err", http.ListenAndServe(config.ProfListenAddress, nil)) + }() } node := &Node{ From 1b77bf6f209fae35cb4693123572263f76c0a0bf Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 22 Jun 2019 19:44:12 +0400 Subject: [PATCH 241/259] rpc/lib: write a test for TLS server (#3703) * rpc/lib: write a test for TLS server Refs #3700 * do not regenerate certificates * add nolint --- Makefile | 29 ++++++++++------- libs/db/remotedb/test.crt | 40 ++++++++++++++---------- libs/db/remotedb/test.key | 50 +++++++++++++++--------------- libs/test.sh | 6 ---- rpc/lib/server/http_server_test.go | 33 +++++++++++++------- rpc/lib/server/test.crt | 25 +++++++++++++++ rpc/lib/server/test.key | 27 ++++++++++++++++ 7 files changed, 140 insertions(+), 70 deletions(-) create mode 100644 rpc/lib/server/test.crt create mode 100644 rpc/lib/server/test.key diff --git a/Makefile b/Makefile index 1980ac861..3dff35b9c 100644 --- a/Makefile +++ b/Makefile @@ -115,24 +115,31 @@ get_deps_bin_size: protoc_libs: libs/common/types.pb.go +# generates certificates for TLS testing in remotedb and RPC server gen_certs: clean_certs - ## Generating certificates for TLS testing... certstrap init --common-name "tendermint.com" --passphrase "" - certstrap request-cert -ip "::" --passphrase "" - certstrap sign "::" --CA "tendermint.com" --passphrase "" - mv out/::.crt out/::.key db/remotedb - -clean_certs: - ## Cleaning TLS testing certificates... + certstrap request-cert --common-name "remotedb" -ip "127.0.0.1" --passphrase "" + certstrap sign "remotedb" --CA "tendermint.com" --passphrase "" + mv out/remotedb.crt libs/db/remotedb/test.crt + mv out/remotedb.key libs/db/remotedb/test.key + certstrap request-cert --common-name "server" -ip "127.0.0.1" --passphrase "" + certstrap sign "server" --CA "tendermint.com" --passphrase "" + mv out/server.crt rpc/lib/server/test.crt + mv out/server.key rpc/lib/server/test.key rm -rf out - rm -f db/remotedb/::.crt db/remotedb/::.key -test_libs: gen_certs +# deletes generated certificates +clean_certs: + rm -f libs/db/remotedb/test.crt + rm -f libs/db/remotedb/test.key + rm -f rpc/lib/server/test.crt + rm -f rpc/lib/server/test.key + +test_libs: go test -tags clevedb boltdb $(PACKAGES) - make clean_certs grpc_dbserver: - protoc -I db/remotedb/proto/ db/remotedb/proto/defs.proto --go_out=plugins=grpc:db/remotedb/proto + protoc -I libs/db/remotedb/proto/ libs/db/remotedb/proto/defs.proto --go_out=plugins=grpc:libs/db/remotedb/proto protoc_grpc: rpc/grpc/types.pb.go diff --git a/libs/db/remotedb/test.crt b/libs/db/remotedb/test.crt index 06ffec1d2..1090e73d7 100644 --- a/libs/db/remotedb/test.crt +++ b/libs/db/remotedb/test.crt @@ -1,19 +1,25 @@ -----BEGIN CERTIFICATE----- -MIIDAjCCAeqgAwIBAgIJAOGCVedOwRbOMA0GCSqGSIb3DQEBBQUAMCExCzAJBgNV -BAYTAlVTMRIwEAYDVQQDDAlsb2NhbGhvc3QwHhcNMTkwMjExMTU0NjQ5WhcNMjAw -MjExMTU0NjQ5WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJbG9jYWxob3N0MIIB -IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA60S/fNUWoHm1PYI/yrlnZNtr -dRqDORHe0hPwl/lttLz7+a7HzQZFnpiXnuxbDJtpIq/h1vhAl0sFy86Ip26LhbWc -GjxJL24tVwiOwqYRzTPZ/rK3JYuNcIvcztXjMqdzPrHSZy5YZgrQB6yhTiqpBc4D -h/XgWjEt4DhpHwf/zuIK9XkJw0IaTWjFmoyKRoWW3q4bHzoKNxS9bXP117Tz7tn0 -AdsQCjt1GKcIROkcOGUHqByINJ2XlBkb7SQPjQVBLDVJKdRDUt+yHkkdbn97UDhq -HRTCt5UELWs/53Gj1ffNuhjECOVjG1HkZweLgZjJRQYe8X2OOLNOyfVY1KsDnQID -AQABoz0wOzAMBgNVHRMEBTADAQH/MCsGA1UdEQQkMCKCCWxvY2FsaG9zdIIJbG9j -YWxob3N0hwQAAAAAhwR/AAABMA0GCSqGSIb3DQEBBQUAA4IBAQCe2A5gDc3jiZwT -a5TJrc2J2KouqxB/PCddw5VY8jPsZJfsr9gxHi+Xa5g8p3oqmEOIlqM5BVhrZRUG -RWHDmL+bCsuzMoA/vGHtHmUIwLeZQLWgT3kv12Dc8M9flNNjmXWxdMR9lOMwcL83 -F0CdElxSmaEbNvCIJBDetJJ7vMCqS2lnTLWurbH4ZGeGwvjzNgpgGCKwbyK/gU+j -UXiTQbVvPQ3WWACDnfH6rg0TpxU9jOBkd+4/9tUrBG7UclQBfGULk3sObLO9kx4N -8RxJmtp8jljIXVPX3udExI05pz039pAgvaeZWtP17QSbYcKF1jFtKo6ckrv2GKXX -M5OXGXdw +MIIEOjCCAiKgAwIBAgIQYO+jRR0Sbs+WzU/hj2aoxzANBgkqhkiG9w0BAQsFADAZ +MRcwFQYDVQQDEw50ZW5kZXJtaW50LmNvbTAeFw0xOTA2MDIxMTAyMDdaFw0yMDEy +MDIxMTAyMDRaMBMxETAPBgNVBAMTCHJlbW90ZWRiMIIBIjANBgkqhkiG9w0BAQEF +AAOCAQ8AMIIBCgKCAQEAt7YkYMJ5X5X3MT1tWG1KFO3uyZl962fInl+43xVESydp +qYYHYei7b3T8c/3Ww6f3aKkkCHrvPtqHZjU6o+wp/AQMNlyUoyRN89+6Oj67u2C7 +iZjzAJ+Pk87jMaStubvmZ9J+uk4op4rv5Rt4ns/Kg70RaMvqYR8tGqPcy3o8fWS+ +hCbuwAS8b65yp+AgbnThDEBUnieN3OFLfDV//45qw2OlqlM/gHOVT2JMRbl14Y7x +tW3/Xe+lsB7B3+OC6NQ2Nu7DEA1X+TBNyItIGnQH6DwK2ZBRtyQEk26FAWVj8fHd +A5I4+RcGWXz4T6gJmDZN7+47WHO0ProjARbUV0GIuQIDAQABo4GDMIGAMA4GA1Ud +DwEB/wQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwHQYDVR0O +BBYEFOA8wzCYhoZmy0WHgnv/0efijUMKMB8GA1UdIwQYMBaAFNSTPe743aIx7rIp +vn5HV3gJ4z1hMA8GA1UdEQQIMAaHBH8AAAEwDQYJKoZIhvcNAQELBQADggIBAKZf +EVo0i9nMZv6ZJjbmAlMfo5FH41/oBYC8pyGAnJKl42raXKJAbl45h80iGn3vNggf +7HJjN+znAHDFYjIwK2IV2WhHPyxK6uk+FA5uBR/aAPcw+zhRfXUMYdhNHr6KBlZZ +bvD7Iq4UALg+XFQz/fQkIi7QvTBwkYyPNA2+a/TGf6myMp26hoz73DQXklqm6Zle +myPs1Vp9bTgOv/3l64BMUV37FZ2TyiisBkV1qPEoDxT7Fbi8G1K8gMDLd0wu0jvX +nz96nk30TDnZewV1fhkMJVKKGiLbaIgHcu1lWsWJZ0tdc+MF7R9bLBO5T0cTDgNy +V8/51g+Cxu5SSHKjFkT0vBBONhjPmRqzJpxOQfHjiv8mmHwwiaNNy2VkJHj5GHer +64r67fQTSqAifzgwAbXYK+ObUbx4PnHvSYSF5dbcR1Oj6UTVtGAgdmN2Y03AIc1B +CiaojcMVuMRz/SvmPWl34GBvvT5/h9VCpHEB3vV6bQxJb5U1fLyo4GABA2Ic3DHr +kV5p7CZI06UNbyQyFtnEb5XoXywRa4Df7FzDIv3HL13MtyXrYrJqC1eAbn+3jGdh +bQa510mWYAlQQmzHSf/SLKott4QKR3SmhOGqGKNvquAYJ9XLdYdsPmKKGH6iGUD8 +n7yEi0KMD/BHsPQNNLatsR2SxqGDeLhbLR0w2hig -----END CERTIFICATE----- diff --git a/libs/db/remotedb/test.key b/libs/db/remotedb/test.key index e1adb3e1e..b30bf809a 100644 --- a/libs/db/remotedb/test.key +++ b/libs/db/remotedb/test.key @@ -1,27 +1,27 @@ -----BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEA60S/fNUWoHm1PYI/yrlnZNtrdRqDORHe0hPwl/lttLz7+a7H -zQZFnpiXnuxbDJtpIq/h1vhAl0sFy86Ip26LhbWcGjxJL24tVwiOwqYRzTPZ/rK3 -JYuNcIvcztXjMqdzPrHSZy5YZgrQB6yhTiqpBc4Dh/XgWjEt4DhpHwf/zuIK9XkJ -w0IaTWjFmoyKRoWW3q4bHzoKNxS9bXP117Tz7tn0AdsQCjt1GKcIROkcOGUHqByI -NJ2XlBkb7SQPjQVBLDVJKdRDUt+yHkkdbn97UDhqHRTCt5UELWs/53Gj1ffNuhjE -COVjG1HkZweLgZjJRQYe8X2OOLNOyfVY1KsDnQIDAQABAoIBAAb5n8+8pZIWaags -L2X8PzN/Sd1L7u4HOJrz2mM3EuiT3ciWRPgwImpETeJ5UW27Qc+0dTahX5DcuYxE -UErefSZ2ru0cMnNEifWVnF3q/IYf7mudss5bJ9NZYi+Dqdu7mTAXp4xFlHtaALbp -iFK/8wjoBbTHNmKWKK0IHx27Z/sjK+7QnoKij+rRzvhmNyN2r3dT7EO4VePriesr -zyVaGexNPFhtd1HLJLQ5GqRAidtLM4x1ubvp3NLTCvvoQKKYFOg7WqKycZ2VllOg -ApcpZb/kB/sNTacLvum5HgMNWuWwgREISuQJR+esz/5WaSTQ04L2+vMVomGM18X+ -9n4KYwECgYEA/Usajzl3tWv1IIairSk9Md7Z2sbaPVBNKv4IDJy3mLwt+2VN2mqo -fpeV5rBaFNWzJR0M0JwLbdlsvSfXgVFkUePg1UiJyFqOKmMO8Bd/nxV9NAewVg1D -KXQLsfrojBfka7HtFmfk/GA2swEMCGzUcY23bwah1JUTLhvbl19GNMECgYEA7chW -Ip/IvYBiaaD/qgklwJE8QoAVzi9zqlI1MOJJNf1r/BTeZ2R8oXlRk8PVxFglliuA -vMgwCkfuqxA8irIdHReLzqcLddPtaHo6R8zKP2cpYBo61C3CPzEAucasaOXQFpjs -DPnp4QFeboNPgiEGLVGHFvD5TwZpideBpWTwud0CgYEAy04MDGfJEQKNJ0VJr4mJ -R80iubqgk1QwDFEILu9fYiWxFrbSTX0Mr0eGlzp3o39/okt17L9DYTGCWTVwgajN -x/kLjsYBaaJdt+H4rHeABTWfYDLHs9pDTTOK65mELGZE/rg6n6BWqMelP/qYKO8J -efeRA3mkTVg2o+zSTea4GEECgYEA3DB4EvgD2/fXKhl8puhxnTDgrHQPvS8T3NTj -jLD/Oo/CP1zT1sqm3qCJelwOyBMYO0dtn2OBmQOjb6VJauYlL5tuS59EbYgigG0v -Ku3pG21cUzH26CS3i+zEz0O6xCiL2WEitaF3gnTSDWRrbAVIww6MGiJru1IkyRBX -beFbScECf1n00W9qrXnqsWefk73ucggfV0gQQmDnauMA9J7B96+MvGprE54Tx9vl -SBodgvJsCod9Y9Q7QsMcXb4CuEgTgWKDBp5cA/KUOQmK5buOrysosLnnm12LaHiF -O7IIh8Cmb9TbdldgW+8ndZ4EQ3lfIS0zN3/7rWD34bs19JDYkRY= +MIIEpQIBAAKCAQEAt7YkYMJ5X5X3MT1tWG1KFO3uyZl962fInl+43xVESydpqYYH +Yei7b3T8c/3Ww6f3aKkkCHrvPtqHZjU6o+wp/AQMNlyUoyRN89+6Oj67u2C7iZjz +AJ+Pk87jMaStubvmZ9J+uk4op4rv5Rt4ns/Kg70RaMvqYR8tGqPcy3o8fWS+hCbu +wAS8b65yp+AgbnThDEBUnieN3OFLfDV//45qw2OlqlM/gHOVT2JMRbl14Y7xtW3/ +Xe+lsB7B3+OC6NQ2Nu7DEA1X+TBNyItIGnQH6DwK2ZBRtyQEk26FAWVj8fHdA5I4 ++RcGWXz4T6gJmDZN7+47WHO0ProjARbUV0GIuQIDAQABAoIBAQCEVFAZ3puc7aIU +NuIXqwmMz+KMFuMr+SL6aYr6LhB2bhpfQSr6LLEu1L6wMm1LnCbLneJVtW+1/6U+ +SyNFRmzrmmLNmZx7c0AvZb14DQ4fJ8uOjryje0vptUHT1YJJ4n5R1L7yJjCElsC8 +cDBPfO+sOzlaGmBmuxU7NkNp0k/WJc1Wnn5WFCKKk8BCH1AUKvn/vwbRV4zl/Be7 +ApywPUouV+GJlTAG5KLb15CWKSqFNJxUJ6K7NnmfDoy7muUUv8MtrTn59XTH4qK7 +p/3A8tdNpR/RpEJ8+y3kS9CDZBVnsk0j0ptT//jdt1vSsylXxrf7vjLnyguRZZ5H +Vwe2POotAoGBAOY1UaFjtIz2G5qromaUtrPb5EPWRU8fiLtUXUDKG8KqNAqsGbDz +Stw1mVFyyuaFMReO18djCvcja1xxF3TZbdpV1k7RfcpEZXiFzBAPgeEGdA3Tc3V2 +byuJQthWamCBxF/7OGUmH/E/kH0pv5g9+eIitK/CUC2YUhCnubhchGAXAoGBAMxL +O7mnPqDJ2PqxVip/lL6VnchtF1bx1aDNr83rVTf+BEsOgCIFoDEBIVKDnhXlaJu7 +8JN4la/esytq4j3nM1cl6mjvw2ixYmwQtKiDuNiyb88hhQ+nxVsbIpYxtbhsj+u5 +hOrMN6jKd0GVWsYpdNvY/dXZG1MXhbWwExjRAY+vAoGBAKBu3jHUU5q9VWWIYciN +sXpNL5qbNHg86MRsugSSFaCnj1c0sz7ffvdSn0Pk9USL5Defw/9fpd+wHn0xD4DO +msFDevQ5CSoyWmkRDbLPq9sP7UdJariczkMQCLbOGpqhNSMS6C2N0UsG2oJv2ueV +oZUYTMYEbG4qLl8PFN5IE7UHAoGAGwEq4OyZm7lyxBii8jUxHUw7sh2xgx2uhnYJ +8idUeXVLbfx5tYWW2kNy+yxIvk432LYsI+JBryC6AFg9lb81CyUI6lwfMXyZLP28 +U7Ytvf9ARloA88PSk6tvk/j4M2uuTpOUXVEnXll9EB9FA4LBXro9O4JaWU53rz+a +FqKyGSMCgYEAuYCGC+Fz7lIa0aE4tT9mwczQequxGYsL41KR/4pDO3t9QsnzunLY +fvCFhteBOstwTBBdfBaKIwSp3zI2QtA4K0Jx9SAJ9q0ft2ciB9ukUFBhC9+TqzXg +gSz3XpRtI8PhwAxZgCnov+NPQV8IxvD4ZgnnEiRBHrYnSEsaMLoVnkw= -----END RSA PRIVATE KEY----- diff --git a/libs/test.sh b/libs/test.sh index 64898b0d2..d0618768b 100755 --- a/libs/test.sh +++ b/libs/test.sh @@ -4,9 +4,6 @@ set -e # run the linter # make lint -# setup certs -make gen_certs - # run the unit tests with coverage echo "" > coverage.txt for d in $(go list ./... | grep -v vendor); do @@ -16,6 +13,3 @@ for d in $(go list ./... | grep -v vendor); do rm profile.out fi done - -# cleanup certs -make clean_certs diff --git a/rpc/lib/server/http_server_test.go b/rpc/lib/server/http_server_test.go index 7f47a30b3..b463aa6a8 100644 --- a/rpc/lib/server/http_server_test.go +++ b/rpc/lib/server/http_server_test.go @@ -1,16 +1,18 @@ package rpcserver import ( + "crypto/tls" "fmt" "io" "io/ioutil" + "net" "net/http" - "os" "sync" "sync/atomic" "testing" "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/libs/log" @@ -66,18 +68,27 @@ func TestMaxOpenConnections(t *testing.T) { } func TestStartHTTPAndTLSServer(t *testing.T) { - config := DefaultConfig() - config.MaxOpenConnections = 1 - // set up fixtures - listenerAddr := "tcp://0.0.0.0:0" - listener, err := Listen(listenerAddr, config) + ln, err := net.Listen("tcp", "localhost:0") require.NoError(t, err) + defer ln.Close() + mux := http.NewServeMux() - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {}) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, "some body") + }) - // test failure - err = StartHTTPAndTLSServer(listener, mux, "", "", log.TestingLogger(), config) - require.IsType(t, (*os.PathError)(nil), err) + go StartHTTPAndTLSServer(ln, mux, "test.crt", "test.key", log.TestingLogger(), DefaultConfig()) - // TODO: test that starting the server can actually work + tr := &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // nolint: gosec + } + c := &http.Client{Transport: tr} + res, err := c.Get("https://" + ln.Addr().String()) + require.NoError(t, err) + defer res.Body.Close() + assert.Equal(t, http.StatusOK, res.StatusCode) + + body, err := ioutil.ReadAll(res.Body) + require.NoError(t, err) + assert.Equal(t, []byte("some body"), body) } diff --git a/rpc/lib/server/test.crt b/rpc/lib/server/test.crt new file mode 100644 index 000000000..e4ab1965d --- /dev/null +++ b/rpc/lib/server/test.crt @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEODCCAiCgAwIBAgIQWDHUrd4tOM2xExWhzOEJ7DANBgkqhkiG9w0BAQsFADAZ +MRcwFQYDVQQDEw50ZW5kZXJtaW50LmNvbTAeFw0xOTA2MDIxMTAyMDdaFw0yMDEy +MDIxMTAyMDRaMBExDzANBgNVBAMTBnNlcnZlcjCCASIwDQYJKoZIhvcNAQEBBQAD +ggEPADCCAQoCggEBANBaa6dc9GZcIhAHWqVrx0LONYf+IlbvTP7yrV45ws0ix8TX +1NUOiDY1cwzKH8ay/HYX45e2fFLrtLidc9h+apsC55k3Vdcy00+Ksr/adjR8D4A/ +GpnTS+hVDHTlqINe9a7USok34Zr1rc3fh4Imu5RxEurjMwkA/36k6+OpXMp2qlKY +S1fGqwn2KGhXkp/yTWZILEMXBazNxGx4xfqYXzWm6boeyJAXpM2DNkv7dtwa/CWY +WacUQJApNInwn5+B8LLoo+pappkfZOjAD9/aHKsyFTSWmmWeg7V//ouB3u5vItqf +GP+3xmPgeYeEyOIe/P2f8bRuQs+GGwSCmi6F1GUCAwEAAaOBgzCBgDAOBgNVHQ8B +Af8EBAMCA7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQW +BBSpBFIMbkBR4xVYQZtUJQQwzPmbHjAfBgNVHSMEGDAWgBTUkz3u+N2iMe6yKb5+ +R1d4CeM9YTAPBgNVHREECDAGhwR/AAABMA0GCSqGSIb3DQEBCwUAA4ICAQBCqdzS +tPHkMYWjYs6aREwob9whjyG8a4Qp6IkP1SYHCwpzsTeWLi9ybEcDRb3jZ4iRxbZg +7GFxjqHoWgBZHAIyICMsHupOJEtXq5hx86NuMwk/12bx1eNj0yTIAnVOA+em/ZtB +zR38OwB8xXmjKd0Ow1Y7zCh5zE2gU+sR0JOJSfxXUZrJvwDNrbcmZPQ+kwuq4cyv +fxZnvZf/owbyOLQFdbiPQbbiZ7JSv8q7GCMleULCEygrsWClYkULUByhKykCHJIU +wfq1owge9EqG/4CDCCjB9vBFmUyv3FJhgWnzd6tPQckFoHSoD0Bjsv/pQFcsGLcg ++e/Mm6hZgCXXwI2WHYbxqz5ToOaRQQYo6N77jWejOBMecOZmPDyQ2nz73aJd11GW +NiDT7pyMlBJA8W4wAvVP4ow2ugqsPjqZ6EyismIGFUTqMp+NtXOsLPK+sEMhKhJ9 +ulczRpPEf25roBt6aEk2fTAfAPmbpvNamBLSbBU23mzJ38RmfhxLOlOgCGbBBX4d +kE+/+En8UJO4X8CKaKRo/c5G2UZ6++2cjp6SPrsGENDMW5yBGegrDw+ow8/bLxIr +OjWpSe2cygovy3aHE6UBOgkxw9KIaSEqFgjQZ0i+xO6l6qQoljQgUGXfecVMR+7C +4KsyVVTMlK9/thA7Zfc8a5z8ZCtIKkT52XsJhw== +-----END CERTIFICATE----- diff --git a/rpc/lib/server/test.key b/rpc/lib/server/test.key new file mode 100644 index 000000000..bb9af06b0 --- /dev/null +++ b/rpc/lib/server/test.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEoQIBAAKCAQEA0Fprp1z0ZlwiEAdapWvHQs41h/4iVu9M/vKtXjnCzSLHxNfU +1Q6INjVzDMofxrL8dhfjl7Z8Uuu0uJ1z2H5qmwLnmTdV1zLTT4qyv9p2NHwPgD8a +mdNL6FUMdOWog171rtRKiTfhmvWtzd+Hgia7lHES6uMzCQD/fqTr46lcynaqUphL +V8arCfYoaFeSn/JNZkgsQxcFrM3EbHjF+phfNabpuh7IkBekzYM2S/t23Br8JZhZ +pxRAkCk0ifCfn4Hwsuij6lqmmR9k6MAP39ocqzIVNJaaZZ6DtX/+i4He7m8i2p8Y +/7fGY+B5h4TI4h78/Z/xtG5Cz4YbBIKaLoXUZQIDAQABAoH/NodzpVmunRt/zrIe +By0t+U3+tJjOY/I9NHxO41o6oXV40wupqBkljQpwEejUaCxv5nhaGFqqLwmBQs/y +gbaUL/2Sn4bb8HZc13R1U8DZLuNJK0dYrumd9DBOEkoI0FkJ87ebyk3VvbiOxFK8 +JFP+w9rUGKVdtf2M4JhJJEwu/M2Yawx9/8CrCIY2G6ufaylrIysLeQMsxrogF8n4 +hq7fyqveWRzxhqUxS2fp9Ynpx4jnd1lMzv+z3i8eEsW+gB9yke7UkXZMbtZg1xfB +JjiEfcDVfSwSihhgOYttgQ9hkIdohDUak7OzRSWVBuoxWUhMfrQxw/HZlgZJL9Vf +rGdlAoGBANOGmgEGky+acV33WTWGV5OdAw6B/SlBEoORJbj6UzQiUz3hFH/Tgpbj +JOKHWGbGd8OtOYbt9JoofGlNgHA/4nAEYAc2HGa+q0fBwMUflU0DudAxXis4jDmE +D76moGmyJoSgwVrp1W/vwNixA5RpcZ3Wst2nf9RKLr+DxypHTit/AoGBAPwpDeqc +rwXOTl0KR/080Nc11Z03VIVZAGfA59J73HmADF9bBVlmReQdkwX0lERchdzD0lfa +XqbqBLr4FS5Uqyn5f3DCaMnOeKfvtGw2z6LnY+w03mii4PEW/vNKLlB18NdduPwL +KeAc08Zh+qJFMKD1PoEQOH+Y7NybBbaQL8IbAoGAfPPUYaq6o7I+Kd4FysKTVVW5 +CobrP8V65FGH0R++qttkBPfDHkeZqvx/O3nsVLoE4YigpP5IMhCcfbAUoTp7zuQm +vdvPJzqW/4qLD2c60QXUbBHdqPZ8jzVd/6d6tzVP36T+02+yb69XYiofDTrErRK5 +EorxzjwMJYH40xbQLI0CgYBh7d/FucwPSSwN3ixPIQtKSVIImLBuiT4rDTP6/reF +SEGF1ueg7KNAEGxE59OdKQGj1zkdfWU9Fa14n1g6gg9nYcoolJf1qAYb0nAThsXk +0lBwL6ggowERIIkrGygZf3Rlb7SjzgIZU5i7dtnLo2tbV2NK5G3MwCtdEaeKWzzw ++QKBgQC7+JPHoqbnNgis2vCGLKMOU3HpJK/rYEU/8ZUegc9lshEFZYsRbtKQQJQs +nqsChrG8UoK84frujEBkO/Nzsil85p8ar79wZguGnVvswTWaTuKvl8H/qQQ/JSHZ +OHGQD4qwTCkdRr8Vf8NfuCoZlJDnHncLJZNWjrb5feqCnJ/YIQ== +-----END RSA PRIVATE KEY----- From 3e7752c29d33c1c11bb2ba0ba9ac6985d47d83e1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Sat, 22 Jun 2019 19:48:01 +0400 Subject: [PATCH 242/259] cs: exit if SwitchToConsensus fails (#3706) Refs #3656 --- CHANGELOG_PENDING.md | 1 + consensus/reactor.go | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 1bff64b64..5e9a222d1 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -32,6 +32,7 @@ ### FEATURES: ### IMPROVEMENTS: +- [consensus] \#3656 Exit if SwitchToConsensus fails - [p2p] \#3666 Add per channel telemetry to improve reactor observability - [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) - [abci/examples] \#3659 Change validator update tx format (incl. expected pubkey format, which is base64 now) (@needkane) diff --git a/consensus/reactor.go b/consensus/reactor.go index f690a407d..dc3514b21 100644 --- a/consensus/reactor.go +++ b/consensus/reactor.go @@ -116,8 +116,13 @@ func (conR *ConsensusReactor) SwitchToConsensus(state sm.State, blocksSynced int } err := conR.conS.Start() if err != nil { - conR.Logger.Error("Error starting conS", "err", err) - return + panic(fmt.Sprintf(`Failed to start consensus state: %v + +conS: +%+v + +conR: +%+v`, err, conR.conS, conR)) } } From a44c621d2dee57e083641df05b51ad4fd90f0c14 Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 24 Jun 2019 15:48:21 +0200 Subject: [PATCH 243/259] Remove double lint (#3748) - Circel CI and Golangci were doing linting jobs, removed circleci Signed-off-by: Marko Baricevic --- .circleci/config.yml | 65 ++++++++++++++++---------------------------- 1 file changed, 23 insertions(+), 42 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 04a03eb25..5836b4546 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,22 +67,6 @@ jobs: export PATH="$GOBIN:$PATH" make build-slate - lint: - <<: *defaults - steps: - - attach_workspace: - at: /tmp/workspace - - restore_cache: - key: v4-pkg-cache - - restore_cache: - key: v3-tree-{{ .Environment.CIRCLE_SHA1 }} - - run: - name: metalinter - command: | - set -ex - export PATH="$GOBIN:$PATH" - make lint - test_abci_apps: <<: *defaults steps: @@ -98,8 +82,8 @@ jobs: export PATH="$GOBIN:$PATH" bash abci/tests/test_app/test.sh -# if this test fails, fix it and update the docs at: -# https://github.com/tendermint/tendermint/blob/develop/docs/abci-cli.md + # if this test fails, fix it and update the docs at: + # https://github.com/tendermint/tendermint/blob/develop/docs/abci-cli.md test_abci_cli: <<: *defaults steps: @@ -169,24 +153,24 @@ jobs: command: bash test/persist/test_failure_indices.sh localnet: - working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint - machine: - image: circleci/classic:latest - environment: - GOBIN: /home/circleci/.go_workspace/bin - GOPATH: /home/circleci/.go_workspace/ - GOOS: linux - GOARCH: amd64 - parallelism: 1 - steps: - - checkout - - run: - name: run localnet and exit on failure - command: | - set -x - docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux - make localnet-start & - ./scripts/localnet-blocks-test.sh 40 5 10 localhost + working_directory: /home/circleci/.go_workspace/src/github.com/tendermint/tendermint + machine: + image: circleci/classic:latest + environment: + GOBIN: /home/circleci/.go_workspace/bin + GOPATH: /home/circleci/.go_workspace/ + GOOS: linux + GOARCH: amd64 + parallelism: 1 + steps: + - checkout + - run: + name: run localnet and exit on failure + command: | + set -x + docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang make build-linux + make localnet-start & + ./scripts/localnet-blocks-test.sh 40 5 10 localhost test_p2p: environment: @@ -273,9 +257,9 @@ jobs: paths: - "release-version.source" - save_cache: - key: v2-release-deps-{{ checksum "go.sum" }} - paths: - - "/go/pkg/mod" + key: v2-release-deps-{{ checksum "go.sum" }} + paths: + - "/go/pkg/mod" build_artifacts: <<: *defaults @@ -358,9 +342,6 @@ workflows: - master - develop - setup_dependencies - - lint: - requires: - - setup_dependencies - test_abci_apps: requires: - setup_dependencies From c37faf3ac18b02b961430d0b1159197912ea7d2b Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 24 Jun 2019 17:49:49 +0400 Subject: [PATCH 244/259] docs: (rpc/broadcast_tx_*) write expectations for a client (#3749) Refs #3322 --- rpc/core/mempool.go | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rpc/core/mempool.go b/rpc/core/mempool.go index 967466e73..1a0954438 100644 --- a/rpc/core/mempool.go +++ b/rpc/core/mempool.go @@ -19,6 +19,19 @@ import ( // Returns right away, with no response. Does not wait for CheckTx nor // DeliverTx results. // +// If you want to be sure that the transaction is included in a block, you can +// subscribe for the result using JSONRPC via a websocket. See +// https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html +// If you haven't received anything after a couple of blocks, resend it. If the +// same happens again, send it to some other node. A few reasons why it could +// happen: +// +// 1. malicious node can drop or pretend it had committed your tx +// 2. malicious proposer (not necessary the one you're communicating with) can +// drop transactions, which might become valid in the future +// (https://github.com/tendermint/tendermint/issues/3322) +// 3. node can be offline +// // Please refer to // https://tendermint.com/docs/tendermint-core/using-tendermint.html#formatting // for formatting/encoding rules. @@ -69,6 +82,18 @@ func BroadcastTxAsync(ctx *rpctypes.Context, tx types.Tx) (*ctypes.ResultBroadca // Returns with the response from CheckTx. Does not wait for DeliverTx result. // +// If you want to be sure that the transaction is included in a block, you can +// subscribe for the result using JSONRPC via a websocket. See +// https://tendermint.com/docs/app-dev/subscribing-to-events-via-websocket.html +// If you haven't received anything after a couple of blocks, resend it. If the +// same happens again, send it to some other node. A few reasons why it could +// happen: +// +// 1. malicious node can drop or pretend it had committed your tx +// 2. malicious proposer (not necessary the one you're communicating with) can +// drop transactions, which might become valid in the future +// (https://github.com/tendermint/tendermint/issues/3322) +// // Please refer to // https://tendermint.com/docs/tendermint-core/using-tendermint.html#formatting // for formatting/encoding rules. From d88a639838d03dcc76c0b1f04b589267228db5cd Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Mon, 24 Jun 2019 10:32:12 -0400 Subject: [PATCH 245/259] Make RPC bind to localhost by default (#3746) * Make RPC bind to localhost by default * Add CHANGELOG_PENDING entry * Allow testnet command to override RPC listen address * Update localnet test to bind RPC to 0.0.0.0 * Update p2p test to bind RPC to 0.0.0.0 * Remove rpc-laddr parameter * Update localnet to use config template with RPC listen address override * Use config template override method for RPC listen address * Build config template into localnode image * Build localnode image locally before starting localnet * Move testnet config overrides into templates * Revert deletion of config overrides * Remove extraneous config parameter overrides --- CHANGELOG_PENDING.md | 1 + Makefile | 4 ++-- config/config.go | 2 +- networks/local/localnode/Dockerfile | 2 +- networks/local/localnode/config-template.toml | 2 ++ test/docker/Dockerfile | 7 ++++++- test/docker/config-template.toml | 2 ++ 7 files changed, 15 insertions(+), 5 deletions(-) create mode 100644 networks/local/localnode/config-template.toml create mode 100644 test/docker/config-template.toml diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 5e9a222d1..26fb14d1f 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -36,6 +36,7 @@ - [p2p] \#3666 Add per channel telemetry to improve reactor observability - [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) - [abci/examples] \#3659 Change validator update tx format (incl. expected pubkey format, which is base64 now) (@needkane) +- [rpc] \#3724 RPC now binds to `127.0.0.1` by default instead of `0.0.0.0` ### BUG FIXES: - [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) diff --git a/Makefile b/Makefile index 3dff35b9c..35baf7d3a 100644 --- a/Makefile +++ b/Makefile @@ -275,8 +275,8 @@ build-docker-localnode: @cd networks/local && make # Run a 4-node testnet locally -localnet-start: localnet-stop - @if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2 ; fi +localnet-start: localnet-stop build-docker-localnode + @if ! [ -f build/node0/config/genesis.json ]; then docker run --rm -v $(CURDIR)/build:/tendermint:Z tendermint/localnode testnet --config /etc/tendermint/config-template.toml --v 4 --o . --populate-persistent-peers --starting-ip-address 192.167.10.2; fi docker-compose up # Stop testnet diff --git a/config/config.go b/config/config.go index 6c4654fed..aa25cff6b 100644 --- a/config/config.go +++ b/config/config.go @@ -369,7 +369,7 @@ type RPCConfig struct { // DefaultRPCConfig returns a default configuration for the RPC server func DefaultRPCConfig() *RPCConfig { return &RPCConfig{ - ListenAddress: "tcp://0.0.0.0:26657", + ListenAddress: "tcp://127.0.0.1:26657", CORSAllowedOrigins: []string{}, CORSAllowedMethods: []string{"HEAD", "GET", "POST"}, CORSAllowedHeaders: []string{"Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"}, diff --git a/networks/local/localnode/Dockerfile b/networks/local/localnode/Dockerfile index 3942cecd6..03af5aa3c 100644 --- a/networks/local/localnode/Dockerfile +++ b/networks/local/localnode/Dockerfile @@ -13,4 +13,4 @@ CMD ["node", "--proxy_app", "kvstore"] STOPSIGNAL SIGTERM COPY wrapper.sh /usr/bin/wrapper.sh - +COPY config-template.toml /etc/tendermint/config-template.toml diff --git a/networks/local/localnode/config-template.toml b/networks/local/localnode/config-template.toml new file mode 100644 index 000000000..a90eb7bd5 --- /dev/null +++ b/networks/local/localnode/config-template.toml @@ -0,0 +1,2 @@ +[rpc] +laddr = "tcp://0.0.0.0:26657" diff --git a/test/docker/Dockerfile b/test/docker/Dockerfile index 77cc515e5..b39277bd9 100644 --- a/test/docker/Dockerfile +++ b/test/docker/Dockerfile @@ -27,7 +27,12 @@ RUN make install_abci # install Tendermint RUN make install -RUN tendermint testnet --node-dir-prefix="mach" --v=4 --populate-persistent-peers=false --o=$REPO/test/p2p/data +RUN tendermint testnet \ + --config $REPO/test/docker/config-template.toml \ + --node-dir-prefix="mach" \ + --v=4 \ + --populate-persistent-peers=false \ + --o=$REPO/test/p2p/data # Now copy in the code # NOTE: this will overwrite whatever is in vendor/ diff --git a/test/docker/config-template.toml b/test/docker/config-template.toml new file mode 100644 index 000000000..a90eb7bd5 --- /dev/null +++ b/test/docker/config-template.toml @@ -0,0 +1,2 @@ +[rpc] +laddr = "tcp://0.0.0.0:26657" From 4c4bf0f1316e0e9e1dfdda516cd1179a30d5adfb Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 24 Jun 2019 16:32:24 +0200 Subject: [PATCH 246/259] Added flags '-s -w' to buildflags (#3742) * Added flags '-s -w' to buildflags Signed-off-by: Marko Baricevic * added a condition for no strip * minor changes * on call discussions * on call discussion v2 --- Makefile | 3 ++- go.mod | 45 ++++++++++++++++++++++----------------------- go.sum | 24 ++++++++++++++++++++---- 3 files changed, 44 insertions(+), 28 deletions(-) diff --git a/Makefile b/Makefile index 35baf7d3a..f16e62560 100644 --- a/Makefile +++ b/Makefile @@ -11,7 +11,8 @@ export GO111MODULE = on INCLUDE = -I=. -I=${GOPATH}/src -I=${GOPATH}/src/github.com/gogo/protobuf/protobuf BUILD_TAGS?='tendermint' -BUILD_FLAGS = -mod=readonly -ldflags "-X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD`" +LD_FLAGS = -X github.com/tendermint/tendermint/version.GitCommit=`git rev-parse --short=8 HEAD` -s -w +BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)" all: check build test install diff --git a/go.mod b/go.mod index 8fe1a124b..f4a4c6dda 100644 --- a/go.mod +++ b/go.mod @@ -3,50 +3,49 @@ module github.com/tendermint/tendermint go 1.12 require ( - github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/VividCortex/gohistogram v1.0.0 // indirect + github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 // indirect github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a - github.com/davecgh/go-spew v1.1.1 github.com/etcd-io/bbolt v1.3.2 github.com/fortytw2/leaktest v1.2.0 - github.com/fsnotify/fsnotify v1.4.7 github.com/go-kit/kit v0.6.0 github.com/go-logfmt/logfmt v0.3.0 - github.com/go-stack/stack v1.8.0 + github.com/go-stack/stack v1.8.0 // indirect github.com/gogo/protobuf v1.2.1 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect github.com/golang/protobuf v1.3.0 - github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db + github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect + github.com/google/gofuzz v1.0.0 // indirect github.com/gorilla/websocket v1.2.0 - github.com/hashicorp/hcl v1.0.0 - github.com/inconshreveable/mousetrap v1.0.0 + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jmhodges/levigo v1.0.0 - github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 + github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 // indirect github.com/magiconair/properties v1.8.0 - github.com/matttproud/golang_protobuf_extensions v1.0.1 - github.com/mitchellh/mapstructure v1.1.2 - github.com/pelletier/go-toml v1.2.0 + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect github.com/pkg/errors v0.8.0 - github.com/pmezard/go-difflib v1.0.0 github.com/prometheus/client_golang v0.9.1 - github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 - github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 - github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d + github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 // indirect + github.com/prometheus/common v0.0.0-20181020173914-7e9e6cabbd39 // indirect + github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d // indirect github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 github.com/rs/cors v1.6.0 - github.com/spf13/afero v1.1.2 - github.com/spf13/cast v1.3.0 + github.com/spf13/afero v1.1.2 // indirect + github.com/spf13/cast v1.3.0 // indirect github.com/spf13/cobra v0.0.1 - github.com/spf13/jwalterweatherman v1.0.0 - github.com/spf13/pflag v1.0.3 + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/spf13/pflag v1.0.3 // indirect github.com/spf13/viper v1.0.0 github.com/stretchr/testify v1.2.2 github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e github.com/tendermint/go-amino v0.14.1 + go.etcd.io/bbolt v1.3.3 // indirect golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd - golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a - golang.org/x/text v0.3.0 - google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 + google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 // indirect google.golang.org/grpc v1.13.0 - gopkg.in/yaml.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index fee691de9..2a349d300 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,13 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= +github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d h1:xG8Pj6Y6J760xwETNmMzmlt38QSwz0BLp1cZ09g27uw= github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= -github.com/btcsuite/btcutil v0.0.0-20180524032703-d4cc87b86016/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a h1:RQMUrEILyYJEoAT34XS/kLu40vC0+po/UfxrBBA4qZE= github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= @@ -29,23 +32,31 @@ github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.0 h1:kbxbvI4Un1LUWKxufD+BiE6AEExYYgkQLQmLFqA1LFk= github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/gorilla/websocket v1.2.0 h1:VJtLvh6VQym50czpZzx07z/kw9EgAxI3x1ZB8taTMQQ= github.com/gorilla/websocket v1.2.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U= github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -54,7 +65,9 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5 github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3 h1:RE1xgDvH7imwFD45h+u2SgIfERHlS2yNG4DObb5BSKU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -86,22 +99,22 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.0.0 h1:RUA/ghS2i64rlnn4ydTfblY8Og8QzcPtCcHvgMn+w/I= github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e h1:91EeXI4y4ShkyzkMqZ7QP/ZTIqwXp3RuDu5WFzxcFAs= github.com/syndtr/goleveldb v0.0.0-20181012014443-6b91fda63f2e/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0= github.com/tendermint/go-amino v0.14.1 h1:o2WudxNfdLNBwMyl2dqOJxiro5rfrEaU0Ugs6offJMk= github.com/tendermint/go-amino v0.14.1/go.mod h1:i/UKE5Uocn+argJJBb12qTZsCDBcAYMbR92AaJVmKso= +go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25 h1:jsG6UpNLt9iAsb0S2AGW28DveNzzgmbXR+ENoPjUeIU= golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20180710023853-292b43bbf7cb/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181030150119-7e31e0c00fa0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= @@ -112,8 +125,11 @@ google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2 h1:67iHsV9djwGdZpd google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/grpc v1.13.0 h1:bHIbVsCwmvbArgCJmLdgOdHFXlKqTOVjbibbS19cXHc= google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= From e93e8e730d8d3044db7d7ae78d20af036332fafa Mon Sep 17 00:00:00 2001 From: Marko Date: Mon, 24 Jun 2019 20:13:25 +0200 Subject: [PATCH 247/259] Added a disclaimer so the user is aware of binary (#3751) * Added a disaclaimer so the user is aware of binary - added disclaimer for `-s -w` Signed-off-by: Marko Baricevic * move up to compile --- docs/introduction/install.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/introduction/install.md b/docs/introduction/install.md index 4f35ffef7..00e04fa0b 100644 --- a/docs/introduction/install.md +++ b/docs/introduction/install.md @@ -45,6 +45,8 @@ make build to put the binary in `./build`. +_DISCLAIMER_ The binary of tendermint is build/installed without the DWARF symbol table. If you would like to build/install tendermint with the DWARF symbol and debug information, remove `-s -w` from `BUILD_FLAGS` in the make file. + The latest `tendermint version` is now installed. ## Run From eb08609de1b6a057af1d9e42591bb5215f5fb169 Mon Sep 17 00:00:00 2001 From: Mengjay Gao <1955889005@qq.com> Date: Tue, 25 Jun 2019 14:56:03 +0800 Subject: [PATCH 248/259] docs: update JS section of abci-cli.md (#3747) --- docs/app-dev/abci-cli.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/app-dev/abci-cli.md b/docs/app-dev/abci-cli.md index b09b9a11b..7e9db91b9 100644 --- a/docs/app-dev/abci-cli.md +++ b/docs/app-dev/abci-cli.md @@ -326,10 +326,18 @@ application easily in any language. We have implemented the counter in a number of languages [see the example directory](https://github.com/tendermint/tendermint/tree/develop/abci/example). -To run the Node JS version, `cd` to `example/js` and run +To run the Node.js version, fist download & install [the Javascript ABCI server](https://github.com/tendermint/js-abci): ``` -node app.js +git clone https://github.com/tendermint/js-abci.git +cd js-abci +npm install abci +``` + +Now you can start the app: + +```bash +node example/counter.js ``` (you'll have to kill the other counter application process). In another From 747f99fdc198d7ae6456b010c9b8857aae97e25f Mon Sep 17 00:00:00 2001 From: Ethan Buchman Date: Tue, 25 Jun 2019 07:57:50 -0400 Subject: [PATCH 249/259] changelog and version (#3750) * changelog and version * Add section on ABCI changes * Update ABCI upgrade section to include events examples * update upgrading * more upgrading and changelog * update changelog from pending * refer to #rpc_changes * minor word changes --- CHANGELOG.md | 63 ++++++++++++++++++++++++++ CHANGELOG_PENDING.md | 31 +++---------- UPGRADING.md | 102 ++++++++++++++++++++++++++++++++++++++++++- version/version.go | 2 +- 4 files changed, 171 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 068d99c3c..76f547a05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,68 @@ # Changelog +## v0.32.0 + +*June 25, 2019* + +Special thanks to external contributors on this release: +@needkane, @SebastianElvis, @andynog, @Yawning, @wooparadog + +This release contains breaking changes to our build and release processes, ABCI, +and the RPC, namely: +- Use Go modules instead of dep +- Bring active development to the `master` Github branch +- ABCI Tags are now Events - see + [docs](https://github.com/tendermint/tendermint/blob/60827f75623b92eff132dc0eff5b49d2025c591e/docs/spec/abci/abci.md#events) +- Bind RPC to localhost by default, not to the public interface [UPGRADING/RPC_Changes](./UPGRADING.md#rpc_changes) + +Friendly reminder, we have a [bug bounty +program](https://hackerone.com/tendermint). + +### BREAKING CHANGES: + +* CLI/RPC/Config + - [cli] \#3613 Switch from golang/dep to Go Modules to resolve dependencies: + It is recommended to switch to Go Modules if your project has tendermint as + a dependency. Read more on Modules here: + https://github.com/golang/go/wiki/Modules + - [config] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed `leveldb` as generic + option for `db_backend`. Must be `goleveldb` or `cleveldb`. + - [rpc] \#3616 Fix field names for `/block_results` response (eg. `results.DeliverTx` + -> `results.deliver_tx`). See docs for details. + - [rpc] \#3724 RPC now binds to `127.0.0.1` by default instead of `0.0.0.0` + +* Apps + - [abci] \#1859 `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`, + and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event` + contains a `type` and a list of `attributes` (list of key-value pairs) + allowing for inclusion of multiple distinct events in each response. + +* Go API + - [abci] \#3193 Use RequestDeliverTx and RequestCheckTx in the ABCI + Application interface + - [libs/db] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed deprecated `LevelDBBackend` const + If you have `db_backend` set to `leveldb` in your config file, please + change it to `goleveldb` or `cleveldb`. + - [p2p] \#3521 Remove NewNetAddressStringWithOptionalID + +* Blockchain Protocol + +* P2P Protocol + +### FEATURES: + +### IMPROVEMENTS: +- [abci/examples] \#3659 Change validator update tx format in the `persistent_kvstore` to use base64 for pubkeys instead of hex (@needkane) +- [consensus] \#3656 Exit if SwitchToConsensus fails +- [p2p] \#3666 Add per channel telemetry to improve reactor observability +- [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) + +### BUG FIXES: +- [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) +- [libs/db] \#3718 Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) +- [node] \#3716 Fix a bug where `nil` is recorded as node's address +- [node] \#3741 Fix profiler blocking the entire node + ## v0.31.7 *June 3, 2019* diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 26fb14d1f..45d1c7899 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -1,29 +1,19 @@ -## v0.31.8 +## v0.32.1 ** +Special thanks to external contributors on this release: + +Friendly reminder, we have a [bug bounty +program](https://hackerone.com/tendermint). + ### BREAKING CHANGES: * CLI/RPC/Config - - [cli] \#3613 Switch from golang/dep to Go Modules to resolve dependencies: - It is recommended to switch to Go Modules if your project has tendermint as - a dependency. Read more on Modules here: - https://github.com/golang/go/wiki/Modules - - [rpc] \#3616 Improve `/block_results` response format (`results.DeliverTx` - -> `results.deliver_tx`). See docs for details. * Apps - - [abci] \#1859 `ResponseCheckTx`, `ResponseDeliverTx`, `ResponseBeginBlock`, - and `ResponseEndBlock` now include `Events` instead of `Tags`. Each `Event` - contains a `type` and a list of `attributes` (list of key-value pairs) - allowing for inclusion of multiple distinct events in each response. * Go API - - [libs/db] [\#3632](https://github.com/tendermint/tendermint/pull/3632) Removed deprecated `LevelDBBackend` const - If you have `db_backend` set to `leveldb` in your config file, please - change it to `goleveldb` or `cleveldb`. - - [p2p] \#3521 Remove NewNetAddressStringWithOptionalID - - [abci] \#3193 Use RequestDeliverTx and RequestCheckTx in the ABCI interface * Blockchain Protocol @@ -32,14 +22,5 @@ ### FEATURES: ### IMPROVEMENTS: -- [consensus] \#3656 Exit if SwitchToConsensus fails -- [p2p] \#3666 Add per channel telemetry to improve reactor observability -- [rpc] [\#3686](https://github.com/tendermint/tendermint/pull/3686) `HTTPClient#Call` returns wrapped errors, so a caller could use `errors.Cause` to retrieve an error code. (@wooparadog) -- [abci/examples] \#3659 Change validator update tx format (incl. expected pubkey format, which is base64 now) (@needkane) -- [rpc] \#3724 RPC now binds to `127.0.0.1` by default instead of `0.0.0.0` ### BUG FIXES: -- [libs/db] \#3717 Fixed the BoltDB backend's Batch.Delete implementation (@Yawning) -- [libs/db] \#3718 Fixed the BoltDB backend's Get and Iterator implementation (@Yawning) -- [node] \#3716 Fix a bug where `nil` is recorded as node's address -- [node] \#3741 Fix profiler blocking the entire node diff --git a/UPGRADING.md b/UPGRADING.md index 5a77e0729..af42d2a66 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -3,14 +3,114 @@ This guide provides steps to be followed when you upgrade your applications to a newer version of Tendermint Core. - ## v0.32.0 +This release is compatible with previous blockchains, +however the new ABCI Events mechanism may create some complexity +for nodes wishing to continue operation with v0.32 from a previous version. +There are some minor breaking changes to the RPC. + ### Config Changes If you have `db_backend` set to `leveldb` in your config file, please change it to `goleveldb` or `cleveldb`. +### RPC Changes + +The default listen address for the RPC is now `127.0.0.1`. If you want to expose +it publicly, you have to explicitly configure it. Note exposing the RPC to the +public internet may not be safe - endpoints which return a lot of data may +enable resource exhaustion attacks on your node, causing the process to crash. + +Any consumers of `/block_results` need to be mindful of the change in all field +names from CamelCase to Snake case, eg. `results.DeliverTx` is now `results.deliver_tx`. +This is a fix, but it's breaking. + +### ABCI Changes + +ABCI responses which previously had a `Tags` field now have an `Events` field +instead. The original `Tags` field was simply a list of key-value pairs, where +each key effectively represented some attribute of an event occuring in the +blockchain, like `sender`, `receiver`, or `amount`. However, it was difficult to +represent the occurence of multiple events (for instance, multiple transfers) in a single list. +The new `Events` field contains a list of `Event`, where each `Event` is itself a list +of key-value pairs, allowing for more natural expression of multiple events in +eg. a single DeliverTx or EndBlock. Note each `Event` also includes a `Type`, which is meant to categorize the +event. + +For transaction indexing, the index key is +prefixed with the event type: `{eventType}.{attributeKey}`. +If the same event type and attribute key appear multiple times, the values are +appended in a list. + +To make queries, include the event type as a prefix. For instance if you +previously queried for `recipient = 'XYZ'`, and after the upgrade you name your event `transfer`, +the new query would be for `transfer.recipient = 'XYZ'`. + +Note that transactions indexed on a node before upgrading to v0.32 will still be indexed +using the old scheme. For instance, if a node upgraded at height 100, +transactions before 100 would be queried with `recipient = 'XYZ'` and +transactions after 100 would be queried with `transfer.recipient = 'XYZ'`. +While this presents additional complexity to clients, it avoids the need to +reindex. Of course, you can reset the node and sync from scratch to re-index +entirely using the new scheme. + +We illustrate further with a more complete example. + +Prior to the update, suppose your `ResponseDeliverTx` look like: + +```go +abci.ResponseDeliverTx{ + Tags: []cmn.KVPair{ + {Key: []byte("sender"), Value: []byte("foo")}, + {Key: []byte("recipient"), Value: []byte("bar")}, + {Key: []byte("amount"), Value: []byte("35")}, + } +} +``` + +The following queries would match this transaction: + +```go +query.MustParse("tm.event = 'Tx' AND sender = 'foo'") +query.MustParse("tm.event = 'Tx' AND recipient = 'bar'") +query.MustParse("tm.event = 'Tx' AND sender = 'foo' AND recipient = 'bar'") +``` + +Following the upgrade, your `ResponseDeliverTx` would look something like: +the following `Events`: + +```go +abci.ResponseDeliverTx{ + Events: []abci.Event{ + { + Type: "transfer", + Attributes: cmn.KVPairs{ + {Key: []byte("sender"), Value: []byte("foo")}, + {Key: []byte("recipient"), Value: []byte("bar")}, + {Key: []byte("amount"), Value: []byte("35")}, + }, + } +} +``` + +Now the following queries would match this transaction: + +```go +query.MustParse("tm.event = 'Tx' AND transfer.sender = 'foo'") +query.MustParse("tm.event = 'Tx' AND transfer.recipient = 'bar'") +query.MustParse("tm.event = 'Tx' AND transfer.sender = 'foo' AND transfer.recipient = 'bar'") +``` + +For further documentation on `Events`, see the [docs](https://github.com/tendermint/tendermint/blob/60827f75623b92eff132dc0eff5b49d2025c591e/docs/spec/abci/abci.md#events). + +### Go Applications + +The ABCI Application interface changed slightly so the CheckTx and DeliverTx +methods now take Request structs. The contents of these structs are just the raw +tx bytes, which were previously passed in as the argument. + + ## v0.31.6 There are no breaking changes in this release except Go API of p2p and diff --git a/version/version.go b/version/version.go index 1a15717f7..2d1c41fd5 100644 --- a/version/version.go +++ b/version/version.go @@ -20,7 +20,7 @@ const ( // Must be a string because scripts like dist.sh read this file. // XXX: Don't change the name of this variable or you will break // automation :) - TMCoreSemVer = "0.31.7" + TMCoreSemVer = "0.32.0" // ABCISemVer is the semantic version of the ABCI library ABCISemVer = "0.16.0" From 1014f8c84bb10300f43f246642d69f46bd8954fd Mon Sep 17 00:00:00 2001 From: Marko Date: Fri, 28 Jun 2019 17:32:15 +0200 Subject: [PATCH 250/259] docs: update to contributing.md (#3760) * Update to contributing.md - Add in section for Draft PR, instead of WIP - Add make lint as part of the commit process. Signed-off-by: Marko Baricevic * mistake * minor word change --- CONTRIBUTING.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 77a625504..ef992fee6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,9 @@ to write a more detailed design document in the form of an Architectural Decision Record (ie. see [here](./docs/architecture/)) before submitting code changes. -Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. +Please open a [Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/), even if your contribution is incomplete, this inidicates to the community you're working on something and allows them to provide comments early in the development process. When the code is complete it can be marked as ready-for-review. + +Please make sure to use `gofmt` before every commit - the easiest way to do this is have your editor run it for you upon saving a file. Additionally please ensure that your code is lint compliant by running `make lint` ## Forking From b2afc6590858c0804bdca478a76ea9d36f0181c6 Mon Sep 17 00:00:00 2001 From: Peng Zhong <172531+nylira@users.noreply.github.com> Date: Sat, 29 Jun 2019 03:06:44 -0400 Subject: [PATCH 251/259] docs: add readme image (#3763) --- README.md | 1 + docs/tendermint-core-image.jpg | Bin 0 -> 125536 bytes 2 files changed, 1 insertion(+) create mode 100755 docs/tendermint-core-image.jpg diff --git a/README.md b/README.md index ec7b28cc0..994ca63b2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Tendermint +![banner](docs/tendermint-core-image.jpg) [Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance) [State Machines](https://en.wikipedia.org/wiki/State_machine_replication). diff --git a/docs/tendermint-core-image.jpg b/docs/tendermint-core-image.jpg new file mode 100755 index 0000000000000000000000000000000000000000..75832e602dbb36010e1812de3e5dd0c5969a47f9 GIT binary patch literal 125536 zcmbq*c{tSF8~1mZ7(zpmEylhTMhp@rJ6S?V){G^4*2p%p@5@liR`%>m##ThOu~w30 zlzk^z%UF89Jx|Z?egA*YjNzKOuDL(wzR$g!&$*A^;ExD52M4!^gs`Bn#B~(vy0(V4 zt!*Ua|Mv@icL4M-s0<~R9KsEd(L>1TA-_9;O8`JdMh^Ld^}hijBZr=$poCHVo&;e3 ze(-A+8i^Dv=JOWPnGj|yWWkS1${IF&xebuRQT;*r@7F07WG8BK^0eQq$!qzU$~0ab zp2F<-c?^Epxi-UQ4FhZ_U}zXRoD9lK1z_PYDBxf}e-EQp&3+4-hk#@M5e?}D*ogo` zJUXo}`+EkRFXok&4jw>m?7ct&?V3qtD}i_(+h+pypB}|nHUv*6&ditgO)O742o1W) zyk%-a)?ift3K#?iMQFf75lT={=~$KKiZjjiXBa!NHgKwcr3j`nuwmRB=^0a13WevG zET|2$U!xW@u?&&Zh8i>gr21*~VPynuO%q*!^M1;}!Ikg69D} z2sk1d2IoeEBcNC`*~I1paU>!cj{ZD@MdLjF5tOQph9EMimCmQ}uR$W7bd(G&L~&sU zpD@9JcVy^cm9k+7ZFmN>`R$fbcI^|3^AZwnxN5%~+S2#T?N~A_Ac<9i^OC`_8k#R* zWC*e|4yJ`t$Z~*-Md}(BN}q=QBZOj35+J|K3MrI%q6E-f`krPci0@{Ix-n1$0JPP) zZ~W|;$*CBimlKyOrWTg_Otkz_()`xH_#5C9G|Z2Y0OTLjCsUE3Lt$_PTC~M5IPpTK z4w-fuH$WwU_#*=N003u7c*7?c*5(itOhogum!rpJr*Q?~N6FHY?#HDFK(m*DCP{1E z*Vx4&g(j1CY0f#2lN@$W0M5&s1}-shbtPk!pvhchP%`+HLpL191#Hv>gg%<=yv9Ew zSi#pOn6+fF@H;fQVPl2v3JveiyTSk~z;;i}e_Q81KMn7@Xg0AsQyX4*`sNAk40WSf z=J8t;-CT)6Y6t{~UWoxhrU4BngClO#O<|n(Yf6A^&mX{k<(>!^?RK z053{n`r)>`mo=K_+2 zeDC%jmW%=ruJAGq04@OkXBLoXfNbS8L#%DFAygc#bwh;D+?~p`p0qa(;F6!MUhqHW z1;!&G42Z40B+FK!{RCkX^=ZrxUl}u$rIG=V#V~h)M&RY5`m?T};r!vb4q>=Kj8YB2 z6{{bn0qFcy7AVY=n+FyV4tdNQPPFZ@sazc$*#TDqBs6=yRX%<^lbMEro2%b(BYX47 z(B+*<^x*1V)`9$pkF^RjmW*B>8;($7fQN&w7)k~Yy%dC6ssC7kfXb?tPysTp>Hjqe zHj9?<`6X<;h3%H!SGqV>4Nkq3((W}H=6ASuQb9JEc+F?#^ z&HT0-o?uk*7=S;f7OsIQp_6$*Z|5arfSfHHkRzj6Sw&zan` zL$BX4V?Av%;ol>^3W%7eLLvMLEs7E4$(gPRk5%;Pn-)o&4dQf^gHN6Z*DPn?EqriQ zhDrdOkMZR#dMFne1L%z!;O}df3Ll*YOL4&Aj3RuyLprIu<1uht{YgHX^|>Q&8=@%(VM-^V`|0BXkN0| zga=CSWG*fk1C$b)NWRP~FPbHH*HRb|+So)Wxtsvzp*xa_IM~_R;?x6Vj1`&zXQjdB+ zxQ5aVW;OU@P@CakFjavk78i6yU5wL)0x6P(P^d~fJp}l>@B!c~ob0SKx|0Udz~3HT zX8e!t^f%qvi)9A3Vy#OvgG`}V2)z#~0}ZfD!g zzJX>(LtTpJg%c-Et?Kg(TJU)1SXlH8I69Py>fq2{X3Cf83I@=~C@%$I=T&Zz0nz^n zKF^BQ#sWy7!;_6n+udPgZ8gj03Gv>G&+}iM;x22pFsmWcY7x6EoWBvE~4(9-QpGT_z0t` zah{qwwQi?%ACQ3uzX7ktkH@3LEV26K9cJo&PtP6x2$QB0`Rbne+0hD0!YRZco`>0l zo(mQYhSIttbDcIhg0M9V!lYZqa|;Wrp+{4N-vYDEKLa9;CVT<^LJ_Tr+|Pve*y3A+ zJHO1>G--Ky_2Lo?;CaF?1<@`zs#L=Ch7?IYsoF=c_f5WI%d+(8Sl5bE`cJELXL*s; z(>M8xzk#&jg5ZL|vf)OHm4G9ATs(T0Ac(K z0RS-+&G3_1x8u8M>#4+Pruy$au4?4f^{iD67Y^>f++%g3&uTdhhtlU+`{E z;Qc6P^x1$4poOEcj-A~9iHCMVWL{7JFx!G|iCfAx)&3XIo^2xBy-l*>5h~`Bw+xh& zJUleg{hkqn^C$c`FEsY~$zN=7KCED@^+YME1{@EuE$NiFQ;r}*DFa0Zr{5VJ+H{%3@X+zHWekre-O^Y&!opgfV48HdUpv*ujp3`!dGqGhfXlPlh@?-|4HpRXC((CXv@dSH*0 z8S@_h4*>%!i(s@idX5%9g-ro$3Fn)O5b@_HTy}X9)p%{R5E|SClKGU{Tr`;yo{7Hw z8%Eu-(@L&Cq^-1k!vh7DkNJBsv(HRcny8uFM>w_YMwW7U4hKXkrMh66Y&m6gq{BB_ z4`yI!wf-5i1OQ6!!oxWC;e!gS8vR!V=Ka5%rT!% zoHwiU+fVhEolC{TRYXWx8`>udk>xHA(8-04#)h5IWRQsquo##%8bbBrF9ND)s&+}b z%WxEtjd?~&Jb9;-1yfpJRo9(5wd}l@;p@Hr(ZbLaTmknRg*_X7myMvQ!(bo5Sa zSXq-^4j%6=`~5Vt5Z7w&im*2XxJ#gS0d52Y@fQUdn(h`fllz_Z1V0V82qZOquTNPZfyzbV!Lnm|Qx#|Z zKn@qx*Pv&zcP0a-WfMMaU(koW=i#x63bdmxCCH!qcG+@uDfphnj~=he*m>&h?bRGh zpQ&lehznwYqy`^7t{tm5BkIK5pz_wc7DjjO%)vvMtc)t7JFWmi5N05}a|XuxKhIz| z&x^Lb1Ec6BT2I05o+q35^0mPE$#l@iCH<<8YxgYI9Y%HU_()$a+voHl(mCiWW$jVIdMm3}2KY+A-Fprjo?6H24`S2Y0i!}(a~C+1HsE;x!c!{i?B z6$ZDH?<02CvvZCDY8%YD9^4ge%j0oDS~Qs=dvj>7(HdPCqn7a9r;_)PdU8+l9O>a4@s#!0;KiQa%WDe7H+KcnI^-@H_bZ#`u320mzAcMQ@SeXnJExz7%YN!v z_4Lcq?5yqAEk(WMs@zkV*||JwLB4cfiDt=mDq8OGuAA%-^3+=z0PMwokf7LXnC%G#*&*&xEanyZ0y6$CZSzXVpWg%T( zvrhrn*VItC>QUa6gr3ii4+I-nn;E+I#heD}Zq8X+=zMw}kIJN4ml9Lxb=j8svDUh= z|4v3PuIc`$`Mo5T$pqanAfdt3JwKq$kpnjH-p;#Ah8eWbOhE`D++&^&!z)o zF`<^%ul)vU56oQ@9_={IYD-w0$!Y2Gtjiau`t&s}wDfDx_BZ#ythRgk-nb+`2P&jy?m-PIKzCnm*c-?kC#_Kkh;k`K=3Dl9A!os}o}nDswBK@hd{ z)>H+w@x0>_(8%+BT`p-^#X^0)T83Vr72T|wHcC35xF)hs%ieKVt+$}aje0;*UyFCo zXni!I)r9#N_-blPvRTf#b~C~((ZS;N(Qlxoc%dzmB$lV}DJm=Yp_id%v=Njt9r=r3=YTg%3=BXtFv;N;Ms~NfuMFNrI-Q!w< zZ1zULRMsD5d3mNSEDBAy+pXI-oaM1|`I4FN%IxWjPw%yk#4w~x!s)pzQ*6hrW|^Ao zy7a@zP4nQL{gPv&8I8$+;*~Qw4-ab!%6Z?#xV)GX0{pc;{*Vo!4XLJj^N* zFO7cA&zk@@Y}XA-U(>6GTZhL`A^tOA+LX086ychX5ca@;dB?*ZHB~6o=b2V0q~(dr zTC`VhaQjh>lt4{L+_lt-#ZS8GR#9H`_}*sV_Z#S}dmb**L+H8{F5sF%SQVOgCgIkp zTjdU4>tAh+IhwSj+*VK!MO_h(VVkx84VV=-XZo_Subw`BB;E5H5Hq>WW6oXd6ywwQ z+61njqv|tW@))|vTwAS@^1t$r+_&@-8+wjNL4-8EtephWWi1M68Kf2e^CEI(D+t9J z#1U-YFQFEfHs-c>_szAMAE(Jbr#XKeg`^i(GODvSFG@^|HYVxx-Iufan3V+i70QJm zO|Ne@w4bW7QmK*Gu?e~Rojov9>b-Z0^_k`lT!)P@_pO?k*M9HrF1#W52FL1)8G~!t z@G!rW(Ene;fe6@nJ=Q@2Ie`6m^5BmO0NX*!G>3e!G)^5o>KpjIt`|vGNO0mpiO4*5JN)iX~C9{dmGR z!D0%(VBaFgBuASldY$WB)ADX^lHOe5%eW`AoKxA8J>GxZJjyP2w8$+r8@{!0YstCN*ek?oVaa@YIH66$gk z>gc4k&sDax!G3tcD_LZpJ)K!E3D+>9AqthQVoWH_FO`x#*5cw-;iml;6#!_h&BZiQ zp=jKcA&rP+kjm5S^ZEnJ0GE|jC_7wnaIt862wWDJXEluUewf)wn_E+>Po9A5~U*OF{rZDL6BmM#t@BxH$tCg7-#-ZzRIBufb z4gNn%?2ii&`V2g?c20J$QVvSV(=EF{t4U{FwQ$W+Og|CYFU}42#~3BaJr6G@cn`_= z`E*Q=Boq^@u0U0^$SwB|0z;&vTCyr+lj5E>4mUramBEN`PJK(-H5oQBO)?g}a(fiI z^u3PN{~=~>)W}D+rn`iXaeOWn{eQRFb_Q&!$!H0kYRYTxvJIIR7~FJ*j5QksWdA`Y za-#kwGOV-Z&D*6BOsbPxSZ$zs;fdXnRj#IC%Gv7bx>{>{v(Um)k(|i6Tm0>f-XgDtExMDX4Y{~+9Wx=9Q-_X;^0X^~Vfhl)i z)7>wlDT#K&BdyrobHjokgP9B?s%1HJX6geL} z8fTkD$&E&7A^9j!n#KG?99}qXIhLgyouyp!lkdyAFRu4=? z*^2^2+Z^Yygpx(#iP_0n<%~s(0z$I*WjVQAD7IP2$4aDx|gf=e!s%)6J`sT4^i|mm}tyden zgT3RMT+5R1E2AnAe&QPnQ+v$Tew4$3RB-5}vDP0}4%Wvy!ydAYEEjE;b>V3#7QY&& zF^}3pkC!7<*>prNdmS|4BA$JonYS@y?$YY;DlIM5BKFW2f-l= z6&&j#ls$)&mX<}0BNp)e+$NNipLBnq8I#_J;0pKWfmRCG^ zw~ByKy_Vp=U@4uL!zZ&#kz}4!p9{^{*m#lnr9~OSV3TGO0Z+uGe_MaM%gNoD7^3%K zBS?DafWzfsb}|0QIBd~e#==o0Ie3RumN)e*?is z(r0%?9Sk0MDf5^QNwR9>b1P#5+?u_ zMBTPoguUPjd-LZancbZW$3(}d1WOJnMkDGuHF^zwIO0F*ebbsN#UObj^|9T@@;GMH zc=uE$&ePM=b+8FF6(f6d?8wsW{p)8FhCNjxR`uV^IP%xry_Zi-lAijomB-92dz^Ab zr9}N4uG?wLnW7+KNH@g7kK@Wuc&xJD@D`K&Gka}09LG)ezVVzU%!ObL| z+(T1;JNcpah_b7hKe8QO2lcf6!YzKDCsD2B2yBa5tx=3Dh&?fB3L+#Oz9$4d-1~Xu zu;yaQp1{{Yo=&ND?o2(>`OxoIaaifQdBhz&Is zr{FRZwjTWk6jral?jfmv*E!d>Jr}s#Hf=v0>T8Kx{tbLgQkqeeSY-^3I-=mr%IcZm zRMuaR;TTIi#XK;i)tafTF1egdG|J~J+!tbxI} z8MyZ8G=a-D6ATcKE*5wMTMZ~3d73n1{KrBTh|B>E#K5ru{-n;ILEr7$SET*FUi=LI z74ZIWU+Q zM62Cldy9Z8uq+^V!f?3%uttdt`GZ;t%J$2ba7>aiMHf^aAr#A*Ewc+E`3;NptC zvxaT2!PC|d2u;uqa|TJ!jP6$s@5RE_L$7E4i|VTOX~$ttP&c zt`DKirc2(E=NwPX?n*L!2x8eE3_0sB-@3TY#(DXNuw2N8f$8VLY7#G9f1W0iqGE$X zW~Wo)5^HCoekSD|6LxEahSt~%rUcR_?Pf?E7)Gx1tnsdtX}YD3d4)P!)V#M+8Q_0v zBNTYf{4R$2O0Os2))j;5;;KnVXb=DwD$diz6;Dh)5>uHEth1sb$_6l5#Xm~Qvb8@k z+^dQ{zG~vMf0z@|FuD3`=;;>AFRQdetAttlD<_bx*POY5>zSsqiZ3snR_$6Dx|x5` zK8-#KIlGg2m)v(l+&zEO-STjH*#)LM(P5x}`Js=Qg}dE{yQ!5(pSks4RdUU-Wj&P_ zO1k*yVbRGd_W^(%3jyNZo#zqY>UB}A*XM6fW$q?`RVASbwa`s;Q)X|WJBr?or(PCP zG+8|j+W!q)?HwSMt#e#l&=rViu&e*-b0TGFwH9K}3E$Y5Zw>gOBIo{reOmFCqNz=r z!1vtX&g0*}j@i!TaF^l$(lf=OVb;E|ROQKqlb*HTfZ#DPSfp8tCZqr0XiC2L#iXFB z6sw1KmLmN%$C5nyTbjHGeQvP)h2Q~zn_tNlswpdtpfdl#%b@In!SI5f1Uf%g6K$=s zd(GCHt+00nY4=t*(s!R$9Traf25twrw+Hg9u@^^v#f?0)3>D@4a$4B&i}-wFNPXGj zk$U|}VF>w;?~&i5z7Icq8X9<*#c1vLdQ$t`NNdu(So+tZU^PDa>^8rn;(o=;b1pu+ zRC!%|Zap4h*7mxnEUyy!m7Hf?LoXrQC19 z6}!0uelm%~7&`6rw+gS^-1pcEu<=f1XUz&n{5!QU`pjM2i0C|SG{E2jz{#RnXwdu% z1Qc|~X8?j1$GE1tDf5D;LCdq*wj?h1HfeimJwFJZo_RaW!#-SeS~?pd|F$+*dX|&I@E&LW$<^Kvh3sFq%d=LU z*TLhL!`$Vvn)t?7i3WVG&nP~xT`TL1xb4*b$-oh;*>v;DVl{bF88o6l(0MAuxgd;? z9jH$>r)druEzt>V4MhQT@lxZ`!2Em*lUXUq6i_ZTYGX9@nKSfxGDJ?6wLLS8t%* zGU-u$(>fEw;E~>^Q@g|W{&8|H9dWqS1_3Usbnlk0>3!U<^kXU$=|s<6C0O&Of;cra zY3aO}!Q*XiEDcuYMxOyc(WW;qG@cDiqYL9b0!aK+3=POTTjD`Z&Zkq zq*wLCel>i0>%N>2Y#F#MuWq-ZXfnyJNc(ck#xbzxZ1c}Hr~GAI95KUY`}&usbWB^# z-$n$*uTu_s1cs#b>-TsPPl(?FD|RhT=qi6@)YR)tq`Tmm+0-Atv>Oyu%krDWVCL>Q z)su8XXn9i@c*__xn{+~XlX;U>fipYG@wOTQsP?Z_H4GHp&`1p&hAq=IQ3pRxbg3JW z=3uGhVKT(`w2!wBPoGB22aB$p5W5atzDB%$FL&cpkm;UUCjd+3Sf z*SukyoC+7xa^tayrlO(-02ZoRVV#;L2G6=PVHgaVwksY*ObZ2V4bt~0j)?R*zxy$) zbwb55?o%gmnTJ=M*Ube)e2I>IK8(BHzed#`4d?l6oGF>P-rtWzWnW$#|9L4yJ(Z$3 zXWoy)xcP;M@Q$UPaI?U<#vq>M;U}2OQ_Dd{x0ch*clxUO(;U|y4Mf4x?~Nve#p%*P ztG6F)|L{PHRMPSar9hFqsc7L303;a}3laddHPZ>OgfQ#wZY^atK7P%F27WML(#6%k z-wC8w?I?fFVU#S7`~2EU!hZkx=9y~y&!Wd z?@d;t&V-c3g9PF^okZkDF+dU$K%;uGVO(&o zsk|eb*G>(Gr7BcA-eft~UanMlx_~r4Xg!)Yb1a{_miA_kWD3x% zRehfO`PE)0eI(A3pH6|dq^ql^CokS{ZvM;5r1%B;$Jo@z5aBs6oF``jo@p5qA`c+( zyyR?bu3+vaf>{xCo5Gc^Nom`pBOV5L8>hrh35oLM%rBh@)w7OY{oC~8`oG4cq~kY_ z$L`haPppxSOJxN0DhH}8Z`dvvmbEvCh$)QeYky2997Y!Q>~W3+YaZqcm^0aL+~;9) z_Zu@WWUr$rw|w8wpCiR+GdpicjRcRpiZb4FG&|oJ6`p5SLTU-#V4VTeHw&Pu3l8OH zIv#Ay@BZW(Hk@tb0)aLG-(gw*<(Hy-j3D0`U)0Br#)p?G^ZS;oyY+{_z{5ZO-g5gD zVRG--E$CTml+2w8^W*wFXWFHHGq(hteCiyjpF7%bQ0NaqY#q;-$IKQyJq{n=N{|`!eB-_hj`XVn*rSSKmz(5i_36 zT<1{CO^EW5VKEENOdf14|B4^`?3;d=Bj4C$#{T9`pCOHTc3Op3W}OJ4(*WIWL9s%81zjiDp~WqaQ$yA*fVHP5o)UvBG;alwJ>??-3mRSi>o4rTox zitK&!A^7+@<2kB2J&i?*UIrVJm?ur`7W5O%*LoABp1--~iGOQv<~=u-l*B4jDmAKO z)KQwBbX!Ox9yVeCVg$$z)UJa39{<#$vE&^M7XX8C#ZN^NU3)REO_GZ%7GrV?q|*Gm z38s=JO+G3{zL%0-Ec}=n%W(_JeaCU>Q@rT3y8fj|rr&^T{_x(=H>L;9Zu{onqfFwb zrar4#WahSwi>Z+?g}ZuWhYzy_Y9{t>iIi?07-bDyqgs1;R@CpApYfbEX0&>2ljK~O z#9~Cfy%zDHsPrE7OC2fUxmi`I7yqDKg&TT$;=Ix}$KY%jYtVCCQux7)IKF^dpbM^; zJbXxASwxMDRHdKNV$^yz5mI?%;##>&r>M8kIBFr4+VD%}Oe0Y=-{?|Qp6^m_!oq8B zGoRFG+6E!i{B%&lD);ue++RapsxQV`P-Z%nx(9J}9+Lh7H-=T~&I@0l$SE<=Il7eN zg3J=-VO^_aG%1MBs#OpW;?fdWToikgtunIl)iMNt*Yj{3I9a|^QS^R#kR{o;&aO2d? zs&F-$J;)-OO2VJu1_JUol#q&bAWZ&b|GQqAE7i-er>Po5(j`%w)zKoyN+3ET$BcN{y+g%kkJ9 zs8KhLCBr#-0wc83`9G+<8kicx*m!~N7;&(G1ws9JJ}o=QG3{K133im_pr(0f^w_IJrvhu? zIX@>W_jCDw=$tuZmQ#iDFW9ME= zwB&7kaTDbqAF;XIyfh!7f}v5>vAvf>J%5IoXwGxN?!lj}>7T)r!UytasKu1Z2N)_E zW&%~1E0{CD#71<%Jv@>&nYl)5s}KA5^s>di%=RVW*4(p9z1@;F2MVvYu0ILu_Pd;I zIW6*~;Z(U~umT3S)>H@Rl?JE0*$GMV<7K!fAY|&Gm(sZ0sH?EKMDZKovAYu2(Gd`2 z*Cpc=^PVX_{#riu{a320P}RHtW8(z`Q^B~F>DhL7ghGRjLIXY_z)z4+#{G#L%~fiB z+c^)Zc=L_5X%FhTj4ph@n5tOUZ2R6EYd&suH_F@9?Vt2(lkP)@qKNmc^;Q(irEBI- z)wfYJY}Ql7_DAm7>Xa|OKL9sR=K@q(2JB=ppL<7RQrMk5R%-p~F=H1lz-ss)^fv!$ z2gt}OSmYw+DB{y%AM@qh9V05iu^`g`3%@j{h|FV}awYMe z?kRPVa~}ibrR#mNOa>ptH)A#2bgGr)l{d~QNwtXvj>eM%)(aCtuFX-I%V{oGC9gke zTJmjSQB|Y9w=X{ao>7w8MjOO-pELN8Sk4blVb6Er1>@r% znnNm11}rIe9Ila3kW*-2p1dzlTp#iw%6G9V%CQ9*OuiDyTQReJ;4_LbVpeAm=V4sc zgVZmPEd|&3ER4#nXHHGJc&# zKJNuYgfn!EtivJJ*+C91aea z^TSpSQB}(_u?_NFYtpl7P1DMqW0?7l=OytPUDpgW{v%rf8c6EHQQNaK#^VH9EPNMEJD+2hOle3p4?T7Rh8sDlNyJfDKRZ^1V%{PbjLkj1C>&%yqz z5sVG}_mI2ZTUm!!+vh^D;s6F<-Z|2C?SV9}dr!)EUV9!8^!iG7nZ%&69Z31JF+IN@ zZ<8nZCG~vle@d|dm>(bZ;k(DXX%oV1DE@V5SvmQzpylH&{i-$pL~_&kTBmUr@4*zK z?v400c1&E7LXwE#!-^l1SF-(DIExh0`jaw`Kgi z&a!}fl76hx{~DJ-Ji0T?-QCFW$2vv5VXmocZYmiIk4)72J*O6!hDaWsF1NZmN>q$I zTkbLT)j0M~E+q&MzolfdI6$v@c_7XkU|0?bWr< zEl=65*GT%Gz1ck2Q!^_=ydrOE_}S^R&5JY)Tm}7qbwOp@o)CtyrqfR0CF+xDgN#8< z!_yUc_w>m3Uqt?DNfnZ)Tw{ z$L-jcx{2F0F1PLy)~E8Sd013!=T)>2i2vjReCf=1i5m#(g{e#(LZUbYvQ`K0{u3+? zj9$f9+^QUSMr;_hn@Fj9LhI2*=$E{kci~~s(~OOE*vo{M2!H|rU{IJgz!;9AfwSjWPh~ zRB}Wx(@T^ z+|W>ZDA-qSO%CV9qR9ck5o_m5&CXHDrtnmbQb&!*)ZDZ@HjwR?W@ved4i2M_H-#ep z$^=*`YE!|v_J|0(>FRWQ;*uv|RCwB?(uo!KC7;prwHJC&gDvl}){)ykHhb&5GF60H zM*L3Qe#Ia>H9OIrC@f$NUiKgZm@@$2I z#%UPV9_APSi3K3#DGV%JHA8)T0{oL{1yfni%x|^F53laZPWr^g*1QTE?j=m#??-pO zFP64{k*8k2em%|(f}o&hu>L>;7{pm)mB;e$n&Io;-2gMza61ASm4FkiWe*S(cat9{f-nf|P2lI9&W%!V?PqI4dEhf?LhX z1{;P|=cog;Ng5p)0Y4)h(D2Ak-bgToh5=|SpveWGc#**SfY-yzO%}GbkK{+M>0D_V zGmG8qjS$Z(VIDO!(2xD!bqze2`B=Ph)(hjZNJ65D19&E~Fae9c_pEi|zQ^A8Taofc zYNl`PV_&J%v7P4>aZX8`rwK7#^kyo`>rhgHzk|L*Bcst+3N>*9xa2!l3TR9GrQC~! zon~}l_w=~++yh3(cMvuA(!;BTz=TiF`&YeZQo&6gg%(8*NFB2m5lT$?7p4jpdR5Tg z0axzj6-|B~l}Qtg6T96$8WVRV@>6JsT$0B2VE7FLPD6nRD-o;CP>kOVsWnTh%MXo&6#ALRZP$9y&p;RCUTg$52a2%Wl3Y>;-waUY>awnA6 zfo8D=uCmQwo5Ots9E_f1>o4Ez4jPfQh1lI?6~$MiClkg-6-s)l!$m6ZqT5E2f=Jmkz52Fvch$T=ig4 zM|qOLwrd*ND!(oJHa47=E9NnUxDuN4-XUee3qF$6c1Xt4(}-ior4lFr-s(eH(77x2 z&c7Jn;3@0Ng8_HDe;eQIA8abZ-|yQF#6+&~|lm;4eA07Km+0PsY6Jr?7p0+Oko zu7o}v0(jPo+AT~ksnX$J=zEnVy+0c?u`B&dElbgbbdhrDJKqWCZ0@axr);}vi9c7% zg8h!fcAw;bdz-Ji^HZWVX?M6V*r%ssFUev_;qEZ$VcvE~?$CO`B1fVx$MlOslN)=} zAFf9w9nVOuhbRRqq#jf`{i;-m+KZg;KJ|~;YoOY5=s$H|3DLF-Twk!^3@IO&c~I)n z^3r>E#L%5dMBrY8o@uVRppjB)bd6-VAy5iG2}T0wUe3v!%yDLwWC1}(3CIa%L6 zhuo|B9P&i+B&_@}v++o8x8H~qG(Roo`5RCvE7C!`;3}jC6`t625{(N?|v1>!V_+sWSn+;6o*L;N~$8;DfvwUp1Aga7CfX7HQq?*yW2&^(* zQ_XYfABEh@@W4^{{TVLfftt1#od?y8TT_aP+Za_i6E|Df)yR+~Iw!jQjo@82ZTa zmERbL>(Z6pbZs9UH2PE_f4&rXdF`sr_FB<}vAYRMvCuRP01=O-4Rbmp*$Hru)eZbNxZk6+e!c~`ps#(2HdV?X3KdSEk z)QfMs(aODOc5>->cQU8t;n-tgw959C$)Ka&BPGs;o^u`arM$AZ2RS?gw-AnWo+< zrKzjUl>wSUf~k1ZqrSC#mK!Dz-aL9u2qPIccAPex#|Q~X03h)zD-9+|S#S=0x_Y84 z&CQbAo-X<|%MNR9&%ilwn8d8L-42YgGko_k7#GN*$GM)fy&bip{{H&9qI#R`$;hYH zd*{Ebg=n^+BK9d-zzN`jUFuhJwE`8te;p&VsoNj-aTY2*FWay}Y!~pXzB=wmd9(Ef z2TnoqTxnBMPMdbf)tJ+aQ~ywP6l@*&@R+GKrF`@4H5Y$X_tMX3Sj|Fa2F8#vFFa#T zmEHy#PJbhZKpvkTr*$7c9v-QYO#G+2N3&0-PiNY-(XY8Q6Q^~x$@Rfm^6$A%k8|0I zPc9xWJ?7&46p|b$EjJo8KY#A^b(3zh6Q1L_4F$sSDSY@J<%d}8Mz?AH22iYf(}fD0 zKXrE(rt^1B?aqOphHWTvZ--La{p4KMy7n0Z&&vu8OtbdMXb{S7?FE zSXvgCwih;g3*Nu$WY2hc9p@0EI>PnSs_&bg@5sjSxzwkhPCXVBi>zo5gZ0V-s=6(v zr>t^GRv|}mJBhh1*Xx!7f~ya~w!#yE<8g-((sY*L3IFlLIc<{R>>qzP)gdWgT|%D- zAAhzoUTbxL#ck4@ORavZyy-rCMXV+0!jC6)D^=ddr;6HbSCyv~qJAmsZ+xv9u#S^s zp4)ERKGVqcirsg&yR)?S!-r1|m%|wVfH|BBydN(qt7Vw=xvyr;gaD14idOrgM%ek8EM z;x};X(OXVMKh6D~-r0?e94$`s^*SN?CJM?j@cN zzk_`q4L}PJfDMEU%!~lCZx)%Fit$VX`^Ujn1#q4PLN^BQ(&VWYA=VF>3Q5_!+s4t{0R+jSu)dl5B1Pd`wt? z3ZQ{PvdCfGLP{|1MK21?>e{Qd66w9!sX6Bd$1sM1Sh@)-H&z$@aIpdJyS+w6o_jhW zviE@n00(F@BLD_#7#h6OPez8I0{UE=fK?dI@|)66ieQE`!-Xfl0KMs0YIFB z25pZ_sKzBnD@i41pfnT5tr!gZy0Y=kH*1_<;mJ}`vO`reWV&0fvX`A<j3-#R@4_|y$N@ltxyV9rT=vd*)5) z{i&Y}*dQ?qF+<3|pjrkF6$C6pg@jKf-8s2>>qFchDeATd|G5D4c^_37;Fy1nxF0zf zdBeS7!|5WYz8;;;e50XaDXz8@l{?Daqm7m_44yX0D-n;SJ*hhGqHOUoM?euS7}Sr9 zq6+xJAQ?m|;DR%XRIn6XSQz~D_RjR#iBzN$%PSIPZfRy|Wv~A(f&dpxx7v@oIHtT& z7lHRdT@_`;7zc3`!Fl>=P7{Uhwvj$Qj;TZ}F4L;d=w>U-7%ol~h?J>7*q~tG^ws_& zsGzW9L@NCUNYO`BCpUY*GfWcS~>K@3?@i)GkIgz6|D zbWLG|rWS;(@T#nSB z)#(1&hM#4VF@-I_%R!;&saFaCU<4p50RjX8^bel{1;A_oY8|Ph!n``C>S#@=Z!Lfy`M^AAHp4W;&P2EE}hMu_nqM!nD7VU}%lv+_{yW z=!A_C54Dd&23ST4xCF;{ z&^b^Lfg=c31mH|wy(`09H{S--Egb_Xn#p=UbU`fl}DR4!y9uC~*yOL*Z;53{vVR%T&YykRi%t zX6rp-Bd@LCLIKg51&9xiNDT@@g_oj21aLr5u)X|pXIRx};J+qCGyOl?SEWI4e(i<0 z2ZFOi6hJ_?VBUy(7msX%W1djuR!Yyx&Nb z$zKfJRXxZ1^anFzdT==iI|+mQm6N+LmgP)(+W*83*h!CN; zcoBML&+q4ZMiAIbT$!ja-{Ro&1cE9e^0FZ&zT+T4wgBYzB$gk=G_~tX$mu8zGv&^Q zw6Yj;l(0|OW1yK@-v(4es1yjH7)?1HJmvKb)VqVdSUu94UZhX#W~K>T<3<^S6BoVp zkQt)}rdl;1uy-w*g!TkEWur`8JP6F>O`K14U(+Zi!_x(OU+o{_SkeF)9f64A!gbE7 z=}+F>EH$#6U27gkxBeaH>Ly>5@TejbMV|u#s7PdhM3W#AbfQwfDw?+J9Y6bQCjZVx zTi)n-ldXguFWo2VWXeR1lqpb@B4BOS*$}|#5|f3Fb!k~8vc@YbYk3$~{7$3H$nKda zii!GVqeq{>ii%+%&?cKQM|n?=kXrY;8ede0sn-vI+b4+#_ENDwfnJReYijEAtAjgp z_DO0QDiDIqLE-7!jrD2rma1}j>nc~5mq6cZ$GP&2H+CQUO2&GFz-e!IIA8cT;oT@jxZSLO3jmjvvi7T zT~glG_x5(Is(G$oz)29S%Q|Ko=~SnzlSJ~!7F>T)34nZcgq%L(S_ld6-rbb*W z&leJ0whAYMs_wS^r^*+&As!i-@(?MI2y&8xGI+|~3%s;bH#QLzf7ks-;e^-c{c%E2 zMFPqF{qY3sx53IAxw#q1%9ksawS^*~0pGh3-e@`qps+A>+juHZmfG~3jlbXewuzkA zN@$$;ZVZk^Z%nKW?(F5hY{x(KP`VbdHKlJonY1{Zw^n!T%}|=$@FdFygU)DGRAZe5 zjpmFv>)r_V{P6X?I{p)yP;8`}4avzA+zgIA+rN8u-I}1t^=*4cTYNVo&$|%N=_h8i!x3~!aNn(_RhzZ0 zG?O5_pEDUv7ffJPU}Mw~%U|%pQO?NE$EWu9AD_e18JEue>)yX0$@J`#Z=7bETO49$ zJ?uMWF>^KNb_wL%2_8UUAY6V*xv`^3%o6Ebi(XmP#DPY^py*9t0{OGwOCRTz5r0$F3)%SZ&VT!10NDde*7ka-Obf()@&}=CGSUOCul1! z6)W+Gq<}YkSx$`gDl1!`gIq`!Pw)FYUN%;a?yXGfm{weVi|mh$id>!Ijn9-#o;J5u zo`hu{j>70@=;EZ#ZK{eV7dtVG9OfL$TyrBh9_}S=U#?n3H;#7fPnPclGw`_wDL*QE zZg!#q$Wha7aUKwe;xyGS3^YqxW_t(#&xm=5Q@jKe`9&xrX2DfRD2x(`lrH=Oe7LhKvr&oi)9hNis{AYCK zWo9DlqSs@^yH_afv(JlZFYM)yOJLx$3J&jtp#MB?+pW|^lKtdFHz>Jn=HwO**{Y_>cTys&_IK!JFbUUtZ2 zBQ5dU_52FB^c}e}ITpXOUHok;IrQ|)dzqOf=S3_K)>+k-ICg&3Bb{?hG9w0IfM|j+ zrtE9XWU>mp)Qt3N1J-iRD{Uo8xy2>RegwQhR&3Vb%$_^1Br@Kw<7D@5!NW9Id2P$h z3)l+&lBFT7dSi#Pc>nGo;mkmTXC=-@qYPmg2v9jK={=0+!|+_$fi0u^c4i+ z)Us)>lAgv17QSE2C!FZb@!R&^ru3#-X&$v;0=A;omma^{Wf2?hna?AHm8D%(Y~M3L`t4e3KIxK5$=$#T+RXO4Pxf)Q{kl$$q}7E z2O$N6T>G*oE_N_sPW~i-`l3_f0s7h-mCSF^rET~4Jl!w`t=dMacg+L=rBCo6%#CY} z4`RRTFLK;R+;}rVpn9NlmBW@c>rx(#twhL7daZ&i&`}D(EWwPjcSk_FwpBp|e9{SC z^5Qf+bQJ3$6tf2AD-;ZWW~$fXyrAb?n<&B2 zVH@Cgo^E#9Z8$g^j2Vc`?lG{6D9(Fq+g3EPe0%>RbA+3LF7BA>YJ%37<*c^Kh`nbR+m`Q-LtI9(#a_~3NZbUWzM|C^tcxUWc;_tT5nv3<{xxBYW`i5%Ij;+7?9WFv^eGwtl5Xnr&*j zK1z;p4>=Y+^_b|M1%eA|DjfPBPu-Db+`Z1K{rH9A1h`6k#xB{e*=h1^$+q>!fd=^? zICwfZRN9T1K??571>)085Pba7Qdg&MY{`;G29c>pn3{dOpCJ>F--tq?3thZcDn*aA z1R)WU+SjJ1=s~PMyBb7ve#@-+a9od%2+W23Rh;Y^!XLFv=tu?9=4;|J)uMJ@U}&qp z6DR$4a_F27Le@l-l}h*>KoW1sAq9djdMomE$ru7@tFB(2?HVylu-ssR2cj`+?8*Cw z8xOU8RuBk5n~%3To}D9W$}sJ>I5)S8$h76>0#pzU54^0|?{2XLA3(q{6gQPDoC!Co zk36SfdFoAr*&aT@EKn)zXb=RU&y|Q4KsN;O)5@k{q{vr9Dsj%H2l(2m_xj}{NdYkr zFGI2DkDFT=5CBg*1S0+aA~Aqa|4?B66N&vNRRRB{L@IL?Sp+BNOmS+Np#mR-W(0ce z=$Xp?6~tDksT_;gkQ&Ef^Mz?0x`2` z%96M02%peFTNtQeO<6I>l}y@m34tO^F@?rJ@+DeEm*y5h95Gne@~EX&%SqD|H=Lzo zKaf2Yzfk+%^wPw~U_nBfXnB6R<(u$gn3=?7tW$w<_f79-JZUAxf7^o-HeZg;Vuy>( zWn1B?eK_1d+Ue4l(oCX_2s`uEe}P?a*P$EXNDL&gdxjeDkjGjE>6VMpb9(_~wy z{L)gvAT-eD;J7)MZas@rd-i7$-ZX9Zr_5)wTrGpC(|V;6wEBNUilL&TZ_mIDvCq|- zDKj~Iy-X>YyK(lRc+jsklTTL<4%jbBRg$HwJ`W_ef=5Rs4=Zx#9zTiwnD_1_Xs4ul zq9>I)pZPzVhSo}Ds+K6(#DjSqoNj%*?gNSVD(IKFefBqC{@XEQ$|^D?R3Lb$LDeVT zr0h(VBE4xH{lw|R`yTnt@1}HZ!tuAa{{GQfvxLwvU|Ael10}Y}jwokRE0%yO!KM>U zh|9hE_*>&;F~AW$Pv#inS9BNvfzS}O2}v4wAS6r6q}+hB-L$hj;K5&#bu^*ZFfkyW zOWNAl8m?Oa%t$jH9Hu(K^S1ez&t~)}u%uGS8x|ory<}6$Jz>gMo?S&htND*?Qqp*I zU(>;NuEH6QJ3$=t3aVxW2~Mf*0q?3#aRpt#rv!jp0Eh*O5L_9K^R!D3iH{4dx@rx} zb2+CI4AlW|YEG2A6&+URq5ry}BZhsQ#OWxL7&|^ZZLsL#FTvpz%(3?A$!6yg*BeQ6el2lSpekmghanIUaKDEypTy`tw*%evZr8sX3JA2ZPwO5j8(SP(P&?cT7LP zx|@=`YTyeha&}FvnuKHIIxC#5#@W(y?t5|H4^zB5Ssp<|^c9v>=B9^u#VeOk%Mn=vFbj7`gn~ zML(Un>?jN|EIaP)D@FA?COo^%T5fJG`3fHePpc_Shx*CEb26Re+gZ(ohaVx0tq!|~ zc7SBX4%Mme)+x+eAH1L#%tS?FD1)rYAYG<9=BAQ6>VmnXZA(C3taK;dqbZcdR?eR$G)7VG;#df!~t^7U1Pb{&TJ(VP*<>o z(wM!L^EIudNJR>M?Dcmko?s<(Z?NU@*jX!)$^2$(NF{^EU}<=AlFRqWu$HPSWe{5u z!w|e80$0t3N%LsB>HXpgyAsQ#5>l%%gv$Y&z5J80SII1>IEfS+l?qp;t?;(>_VzKT ztb{k0!C5ib>ojNN($_c{-GNu;+^88uK&K)Lm!Zs{eClM`5_^36%hoRJlay;!8tGy# zc{QSarNM$O2tHYM=`A0LY@rRLlP$CLu4>}Y>p7j!8r!k7V-NXan>QQLxXMdK5$Fh+ z(xt+cqD%9t;xQg?@%1&j?~mdo)5UPGEpdlGa|M462C>ko6@ZjL5PcPO@adEnw=Jin z%K&CTT_BDnk(S>yRqMI(9AJZ#!$8S!^jTf1_9oLAQ&yA;Rpo~Vu<3jh(yZw^F2NdB zKp^2*uyL%VJ+=O;eCe!qb&PtaSJcGeXBrwNJQ9luW`Ay29R@* z>Y8;LO7Y~0eTetCscJ9?q+5^J<1yo;z0qY4e2t5W&C3n}iW=G|xWhYu4+s=-*6*gT z<(8Yn0hLe72chS}nFn_)=;s)x0;3663%GlmQn%r#tDGzU4X09#+Txoo-sW;ez6S9p zX;o>023tlY=2&4WRYrQC=@4e@WW=von_2CSBFnxPzTc-aPP0gwlqw=sH~3<6c^Uj& z+_n!pR>;{qTkOhtnh|9b(-c*fQF9$dC$pj%E}h!91!G-Yym7A>8W%(&T*ZG_QRMio z??=?k=@#>_!^rDDXoxioBTFp}Fiw z4qs(k@wC5$gJR&P1O0em)l>QPObC4FC}Bx7S8s*&IwCTIL2yzyQdPp9XI3B7s6hF= z=;(?OTCAR`>n8MzlK`5u6tz^9y2!Z;zor2QjT%?clZLK!ek@*7E@M#&zCWK@TCOOi zM<&T87*0l;PK}yy7Gk7otc{IbuL;kD#*XRHo2*3$jHFkNk_2gwAN zFmWf!C+pUOKrBpYNYvWOX*TlCiu53OG$g`c6RXx}?S4R0${OH@DTk~3*3f5#;KL!o z>E^4b0^gYfb}WWIjY(ET)8oihF-wX*oF=$%7VWiFYZSl;_2uwT>QWXrh8F8=P@Q_fTjQWXQtS|zYhN#G9)xqGz3I60I?qgyv0GnrAEf)md2yu(Xx0&CF2%? zPs_zCrK#d9mk6*yU6PovKnT(NO?7s6WHZ{7sh|@;rM!deE$~|I8YJ7#cZFgHL8Bd&-aPK z$GghAx^=f=4dzqJkJ)Z;cV*~ugv8WDa`rM+7B)rS;i0F!dEjbODX>wkn=tA9CdZ}j z7X9fP#qK5TBofZYPxvE8@6o(%?987iv0iByO&>kPtfM|phWX7saybe*z1V5JI=FH8bjDxWKM+fS{&O?tC}>f8&XmvE8!9Q4>l z?e`qXCK6*Dqn60TAPCDu3Y*nR^%^2Ju1NTqo+8aR8qoCw)Htw78(e2$QsdbcdHtPV zx?sj$17-^VDY4FyuybTXP94XO^%sPe_Lg=0JHOD*o$?9AkZb2&RNI$`fAsNmi@-`6 zBuSI!?ly61AZm@o#A!P@M0w#7(7ii+V9E0LT3cg|XT>c3DdwBI;eDpY#L(Wn6_lbt zskAk?-k3j8-dV)Fo3mLu)({f7EJXt?3oRNdlT?JSF5H%l^KMlDiEecVhfW_~2#enu)OC6_3$eNnhf(&}hcXUi9#7hb-_pCRQiP{X?&G2xB82v6( zsBimZ%N(`$fl82yKU{xoxD%2FYW8%)o9pk{FDU1(JY;Vr+!emxMVLAvJCmH(82YLBLsG@ zby=Nt@b32w>24K0r6_*B4hh^t*E$m!tM501h$3gcy@`C~)rMd*I)$DP zqv032b}NP(yQL>RO(E|9mNd-#k{c<#u5i$N7v(Nox00cI+X#8Z&(B8{IiRubTJhCh z^6_(SVRh_BZmanDZvGYt!+;->K_Cq__qo*UXyg*^Mzh|N514DE7W`!Azb0ogn&o`3;wvEbh#YZQA>CcUKeT+J`bog1Uf{U-NLh3V`owuc71pl%S` zpA3Uocm0Wn%aRM<1e-_J0O*d;eE&Mca~`ve3U;je>$75XFN*?jQb%?Q2iT+;K2N&Wz;c%u*y-&(D3P5e=wen*czSU9Zq;F! zRYc!Y^?QrZOF07>8%x{f9XhP4naT0kO6{?YwzHB5MBk=+dST#r3;yCi)w>kkB#!9v zjAdf=Ob{&SuoD+oU-Ot^ti$ks-d{_H_M&!-Q5ojNK3cHpp z@_4#8Y)zwC%+IQe@iI89U~y4*j!kv?^E~%T1=$xC^1tG3Hs8}zeMTvoE081Nsbsh} z%5i-v#Cg)v?IR_ywTVr7gmBNica$%YryaEa3pyjs8AD<@B z(~XrKJ(`ue-F&g|Vz?F2S6Y_vCkM>Rp_DVYhZoTWMWJpFJ1QG>%{@8U(md~y$D~&i zH4!dOMp{#EM>x zQ{V6+6n@#7^r1PGK-WFnVPU_w*(^&#+fSsTm+7(}(!<{yx=c46(JW}>kHZU zD0PQ`4=V?+j*M6sb#tcYLZfw5m)i=%43O|;&vX}%V-bI*znP-GZR$+f?}@3Z$KDOt zV0CG1{ly}pg>qW*14&OC;tlnvyWNFahWwz`X=%aX{`m{1)Iv~oQlukivw$>N>W27|jn{GD&iTM)jd5R4ykg6Ut0eIwZHM_lmc(=U z`R05~7%(>id(G9PF%D>!=uEz}P2=++y9T5y{slR}if-J{K}DhSEWU$e=Ea}7FByPZ z^-v$dHJV&rg<^6oyc!e`;#?Ex+ppXqrCaLlo*ToZy#rhZ?21zGC0xirQP0Ed=N zf06diivZc2K3m?M;>-{Fr-%>kEf!B%IR+t`A^P-Q5Lx6gK}=cT>XIn`q;MIg!}x#N zSdzqBNI1Ecb{lV(vi%nGwTAbHbKid7;{ywM0(|}Vnr*ClCL=6qLhhucLgEF3dD$`Q zBo@1R#vUW%5wWk_OX&uku-}c1 z;73Jlg}(s!Z1CbPaiYJoIN!d+&A>qMdpA9a(&a162x3o=z> z&^PX}`(_ynU}g+@rKYCo5m0}qn`KlXM_@&+xZ+$I7GC+*j^zBqP_ONJ%T-|G>N;%` zAEUD)T%9s=^jb>5$AzPY^o{ptXlzD;n*VGyD|Sb$9RW zEzBTer6AeejeYGdBFKpThKnlV7c0%8j6cGc(m_W@TWszACo^BZkH5_8H5g~#rUe-O zru_?|*&c1a(^A04X%)&?<}RFalndUJNhVYyD@uy@CucMI_8u%)k3E&;#QvMCkLGgh zfl|3Oq7Ex}R!`>SfIM+h)e5$|9Gj}4LP2#(-|*4fJs>f68#O5cJyl$>HTpI^cW7s~ zf16KTGDlE8hTFn({(WlSqjr_OV@V?-JD{LTXzWwzRP?IxVnksFDE4!$9DR z-jYo=4g<=c$kLC{g29veRX025*dE&~4{yuWOA_dCR;{DZ4=d81?cs2%3>SV% zKcxY0p{=5g@0&7pgLZC0fJBnfiuojJ9W?ZWRuxYPkgMJ#4(G+a`8_t}L2hvUHe`_k zd)4l;6;Wm`c*J3y` zw=d0n?hw)KXn{k8fKzsQ~@;w&!2njIehT0^Icoo3qNwtOsb_$$&?MC zuKO}-xWJLXG*fzBwHUn+YByG~`mQ{(KBa+oTtk2-gDoaQn0suQ+QY-vnUQ-+hSgXt zqHje*<(71(%X6>9Ob!%2>?-cY-l603v0_&Klx6pNAQjdfTo}&I&>qZeJDwukcn?(Q zZADrTJXU8iTX0;t%@r+!@fz`ogA7@m#kR#Go+EDGO4F@k(p!<)E6HWCfEd$HpAjCb z-{RB`9i3JldCYKpJ40c%4}EaOKO!WfSpC5KE?jLvYx-n{du+R5%G@7tcGJjZ(R_RJ z_O@QIsr!Y<^!Yz7NvuPj?aYg%yw5tTgj?{RUKmB;ZGdOPN zFFe$l<-rQbOPteQ6fvt3Vk&4YQQ|dV!(T{ebZBxns|&^G{beKj$NEhs*??$bW+iSr zHIta;xNmg{Ab)q4b)MeFlZ~zCE^PzvJaRj-kjR21-OJKz=Y^TJ>8#8!!`Olv;vzS9 z9}@Am1;LvuZym*ZXLDZ(O;8h|`p#IGEXv~KUOQX8LT)y29sZDr`Nd^av?Tjh_1CX7 z_GaP)4&Z4FNkK|kf*adWvaD3a;f;v|e3((^eMsv8I*8N_N-*?u^j{LwhtLZ%qIl3q zUt!)Y=l&nOnLs%)er^foBFFvrO*0uI;!B@13-=xtgJW#Fib|Xqw7msl`8FHB;M@SjGhy9I}fiSvWvSCtj_N zuTQ8M`}uVB2>J`Nkn=>M#}*Zb@%C&dJ>)*D=3&dM0WR65InZ1L^=M`H1uIizAJoBXc;A;c2`i8qcCfv@JY zb&-R=2CK#2iX0O0Q16wLcQTy7?$!R_Zrt0};6055p?2$}F7+aF+^-1>W8Rd@zsO8) zJH8z-xNK|$n2a?b_>$$T#Fqg+Q)I;1jcYwT&~`LzYft^5;M>uHu0$4Oe46TaWm-wf z(3)X?AsN$+2E-YQAoBZC5X-K?WRgeHrg-l8tF;`#S52$a$sfo25}UdL76#QwlJ#5S z$W`K@!>tP~grqxvcq6}~`Ic6s&QH<8?uMrY!8_J6)Ywq;nDPS5bA=y_+*1Oyf#Pa% zis_)4{_f|7(;PSN#>=WAn$+tbQMh;SeR4ODxdZKJfbrf&+qLQkgOVjM~$FelEY=d zOwX}-yoRgs1@sDEnY2)9H~M81(CTnfd6pOxtS84qGR0N#iZVs?cOW_!7;s5NE2R( z-wY7b1K=n;3qCibKi*O{mJ3*><*}uHNPAawg0&Y6`X_O%F%dV~>%QHh2nP1{5$jX9aruLe^X0t0qm!lGWbo^)L}AUf z+b~_}r1PRKdtwdqw=s$QA6C-gBbSQ>$y}usm+EdGjwQQxzX$h5#YM__jzHAkw*)u# z4Xhd8#l63C-L6i|81U$Pt}=x;()HL;DC7|R5@7NrL1v*cz&XRjO_%SutSchkL}xe> zX|Dd6b#o4;IM3VT9|psehiY=Z*D6UrsfE7F*NO(N4^~HZ0P1hY#TM0F-t$1Ec!|VS1 zx*>One2vs7$=8?SN)ThUw3@qWIyd+v@cZ}aTy?9oAz|T?^Rg@r4%%0S&+~a?)`agn zD+jkIHm780CkH+>;SV?yd#hr95$`aT7J(VsYl6~)k2Mrj>% zy0~W!gXR97%+fEpAD^*A{({0P^0QzHa(pgU8^g7P)W(6`Bi!?3d?w}flSO=&Z6~$K zOc4gzKWJg|(76vf%H-LD>=ch~WH(>v0dEBJyvlXA zndvIZLw(kDuevC8Qq9Bq+pq*zpnb_)uCC0SVE0*djhi*e=W8SPO(H0QN?2`_E zrazpg+iy@9cOWPipWR-Vw07k9XG0wp;)H(zn~5VQS8~qN=WZ~2ON%yioO(`fYCH1}I#ECvA63w6|R^=vq4iZs2XwpC`-7nGws9VG8R-{SG@r_5Qm4l2mUH0DTPI%Q@k9dK4-}@81iE;Z%hG1rc);Cu56Wa<;gsZ#m{Kt`2hM z#eZIVJN1bS`mHOuzH^XE`9*L0c7iu2VXg{ut&>ft;V+1U>BTDYJL-kUQg3p4f!(Qf zV8(!dig`m|L5bBxB)sYL+rib+N?4&;mOc6L&(uiEEhg+qzs*Fyb9s-Z?^+WSZ@u=S zJcQ0fXTgR9sYwp(V6ZH0dzcI+bHZECdIhc~Xk|fwNW4+akv3RQQ+4eW5}=Lg%!;(} zSUQ2swwdtKq@cep%v2oXvq7ifnDOoXQ8u(1hsfaQ>$i+ndjvnU#BtudW%6atrq#69 zSBE%etf#DA_J2?tgpa0(v@AA{yt;*}uNNLHA$>K%^IyG~WZ@@Am>DnSrrq07=e=1| zu0KeJLtcOXT@{1T$$=pM)X8dm*V2u1XF$Gh&XFm#Up1|njX`b}@zIFDDY2e(whx6j zdx2Zg_>{qNaYs!dMo09f=9jJ)+s9Xk zMgB8^P|+M0cfRKpnO6i%@Eg^^jm|ZDP@1tG&p(xEds`Dj6?xu_Gz4wJh`UQrPy zCQP}c|5n;`DzIL5ER)!dI<65Y{y2#Ewdd*hIbUGFBgk{$pAVyey+1kqASSNLFO%YP6;d zcxkATj3;kg>EB65YpR=9N6$%)=9S;ca4nj^dzQC<)+t752Mj}~Ud^!Z&?N@O(%1I9 z;Cd5DA*_LrG2-}Us-Kr|TmMoDOQ_8^O@4o^@WX*v?=ci3+N zFMNl)ikd4+Z@;J{>{Z;fQZP7o%Ov=e5?O_i#l8)X((@6+^C~(Mec<%yO^vs3#k8Nf zmK)^AQ4a6iA`^`9uk}X6mNv-6lS>Jng`p|fO28?e^9$A5RTXvf8@^-e#G{aFW}c?2 za_Q`iuJPC;9Z46uM$oRWNe*aD!EuZ4OQt9~7fTg*qoG3VOeY=J65jEcYL(NfUZFNO zSSxONjy+n=mCN95B3MPd2j*;ET7^M)w=JGAMDh%!i?f+&0zP08%Hj0J;--BL_Hu~v zb6CdEv4&;0@;04%Xf4Wd%|dH711A1u1lWOmm9d)|bFI`>7ECxiN4&B$LeWLOM%L9v zZ2pIV4%e>$$Np>dSL(hYB;;nZZv?M99m`k$R=zzh@Z&}9Trg#Hr^cstFSG9|J@a; z(#}pYsGaEhD9Tz;NsTvghxk(dg1uLB<>DUJ67TkrpX_;^=bY8RW&gW;H6QlKn(38v zu-hZ>&>3Iq4-g?}`OW)tfk3QzsYvwrnfN+FS~;D_G_U)@-qGyCthb6keD>> zziF~Rl&egRGv{9>MxW?BcAumhk=pV6-p4N2G5h@v{=AIKs6i#0(XN2CIkcz_y|Kgd z!LyHa^WqAPbd_;d)NKD~4e$qsjB*xbL_J4s8SrV%`b8%8W5zV`D8h{@~8Ag=54EMC$yeZb$ zt9pk_1BI}{5MaM>25c1)k6AT8w{%FE;(RoGMw)MBr$w*6Cx!1Dz!(^YFK-}Wj(P?5 zW@1bz2>I^yb4e@G^d{av#jkcr!mLjJy-3wh)*4M!Iy|qyp@cT*XUD=;-Tm%(f^-0! zK{jlEl95lIIXd27?H&}`>A93M2Eo-L7^DqPrsjCe!i#7Pf$W>`JBKN zH(aod7Jj`D%DeR}3qyai;UETOLH2CKQril7(X(+@^NcWxR@vI0Sj^mu+EknV*(G6$ z92Gwxzd1~xX3S?hpktXCo@o4|HYTPHOkQiqdfLmrww@Uy+^I&;qb#y+FC>NqA7a1j ztv#o^JT|v5V)oItM&H6K!@ow%j}nt2IabTkQ%*$Co0jZPv^V217z&f*B=;Gd0{%HmVM z$qQOJ%~v)eB*w#$9^M#4h}dRmT4j0y{afP&tt3#OX3^f4!&o@P7hR7` z=Fl{5e4Gd_chv9J@ofHYvwWZp@fjWlPuJ(LK;U0!=f#Zt2n7W??S=~hAP<0k-7Q)2 zgEmfX^Qu9Vh&=1yyZYOH{Ij)jUiB*_|GW0~+Qc4f%d_zLW{#X&c?E)8QxaOcu3YQ0 zBTUlIXBBUzzY3dBvc4W^c+I>sb~yD$D7Oks(DIo$0xY`C@C=w}v}DCHh;r0W&2P@V z%6dB(R);m=%ja&%FNb=OB1E3@H+cmN)!yZmn_MFo#8%>?je&CFwHD*wtd0y(#&#PU zy1nFHB6jC5(I#!9WmAT6g#!pa!4bIw56!rIBnxuLgF2!<&@w^djoLSB-_W9CRr58O z{eHWST8jxvAiIq5#2#BW*BCTB5tc|Z=z9BJ+T_+m0spqd;sP*cvCUwx)|V%x*W^fZ zv$WJL(24sPPNJ61DiyYpOSt@GAEZd9RNXb3nxAQbSgLV&0TEx`D zTIOk?oz+le*XmFI7CoJV^8?P;#obJXM$|VnNt460rTP-6g;yMP4$`PuV-t=q}GKNlt zEt2}xR&{s_c|&`?d!X~Z%`jy%J( zoxa+B1KvdIM1u4YL^!htB4r%kH+YLiJ+QF3NA$E=-F9WLioT&#AneE;)vRP9Ufr6- zb8!-B(bJUw71x2~wp*5;M&sGO`L(WMw{c{!wqnL@A?*n2gY*e=t`AC=D@-FLyR#e0 z(M(Q6V3WTW&A7|%HSo?G$^Yd#$jXs#@)AwS!%G42)yJ_A192DF@yExHk(nQPj1-F( z(Fxeak|^Y3PVAZtg^tWn<~`#$TD5;gmV3Ym5;`*e4ZqCB^(zhTe^#RkYxt{>*hd1prL5{5L=^@1Q}|Qp zX}bEg3r^fcjpw7%oMLLuZlsJ^ ziE1b)A1f$0ME>TK`bc0L1=jBqS1noI6~@xs8Z!Yha%3E=StVsZb#sZmnmb{spPf!K z&R16>J@ka)9@xh{Dajrf-^(Z*P6P@HUxXA5*}-m=7^7t?Nm=6LD_M*nkmR@Kgfr&& z?MQEAx4UN^*ZgO$H}{sI$JihudS{7^%%(XD&L(l;r@Zo_)Va>x^0WO933E;(tBV#S z%nn!VZ#YZdR1HXr5oq$*(K+t>w0rngSnf5%k!BMXC#RL<>h#~)U!ju>oO3C!XfI+B zN~UaVV<+ur5n!4MnKBuEnXIj?t!q~r8~Z*rAY9U|kMvOjW_bF401iR%zMLNut@{eu zoPPzEBsJ|2P07dN-TV^_F+vMm+_t^>C(%h2Oq~;hnCVTl`dY2GpqdBa{U@Zy{m4ff zd{Nibi<&rUoZ@~8%P)GrAhN4nbz8?$oZ@~4%KLys?zPpvbmFb^AOW3XoZ|Ahe54hG z)tF}tC#wGd+qk3Li6Jsn9p+S12?fU#_BpPxMCgKH*4w6tgL`pASxHebkD+s})IyD| ze2i<2fi@*gg1ijSzWxPyI(ws^6KQXu9sdAY=~za*7S_Ke0bh{uYj6HwgS&#rviBeY zAR}}ui8N_(2dl1KmzNn2aB&cxjM8W)jD4_*HNmHzraf0*H+zSm1RQu0GSkyI_c|MElC9X zIfrzm&iA)yG##Ru9C3>=fxe~XGvn~f{Rw{LUIzqRPalG^Zbpu<)n@u4+nU>=0OE_g zv-Vcy!`-yVd7IQ0Tl{^h(&x zt!@)h?s;B)$t$|-K7sm8Od-HMkIdH#gWz{z745r#9# z8de@?(jG)3O($~bAM{s3{{W&_MDF6cH+L+Dxd=*hWvQ^b`X@(bhkc<>O=~^83bl=v zxpy_Bl-OQ*Ccwd|H%&Qv4Mna!rLqB|X0Y$5@7^f6c}w{GH|k6Gd=-h{vbDTQzjCs& z5%;3&c2Ow#;1aS1_K|;f;NE9Ii>=x7?Y~{l{L$g5TDbhQEDlm8e0+;<_91;#g6&Ue zd~R=eRI?cvTJGBooUWMU*DLR+fy5k!%1Ea4npKoeyE5}n1+F8Ss26S19nuRP(sN4E z)MO~LeMtWGI0v=*meR1rgI4r?f(6HSSpul)DOvIG<~`}gk?$;Euy+R^MH>v= z<8{c;tx6zsS*(d!mo=4Ycq>^oxr#hgP=L;n-o)k`7lGC1;IN6|f>|Yz*0VHNg=6by zFFZMJ+58>&-X|3(zE0Gx?qK4giYj=ch9-uwjSXu@AQ!D|#?k%^r}qUb{{WOs-AMEq z{{H|2G|H$YZy#7IYj7XIIGcJ52|<8yMc{T<6b1M#8{$zU{P;KV z`9br%S8L_;(-eb6VlR?s&76b?GY^P~OY?O5wmba4O^`Jv7^S7YOxSj8Vus z%msUijW=QTDe2~8iFo#>24y+Tf<0GgQ)Ci){{U)x5n*0Utm$z;bm~R-s)#uSwQ<5W zcDC2d7~gMLE(xQa_YlxLQ*1kJxJ?mA=-3WjbtOkjO2!*yH|BL!Sw!*%kVbA^Wv+(= z=UD^k1kg!Wd)gx$0+xHXqi<)QiB2T%*o+imm`iFTZ@<-b-QdqeZpE<}aGDA0X5kxj zA4u{Fy0)VbtD3H%*OEtE{{RnxW8WK^_Be+$+!4ryvv|vKtTH*z$1|xg{UOiWyC1{< z014rfLecMy8RPoH_^BNsHgVK&JlDHwWAv}Rz9{)4}GscPAGTbkQh%(78w!LR$4yR3r=PGBCA>JJricxbKPxB_?tA~w}z0T(g6 z5qUm%1fd^#E)C#~%C*)&-qJ4a;E$b`=WvgiZ95Y+!49m(futJYj#mSb(asGPnZg~$q$+Z(I{#24ii5`RME-$E@+E{M|zw(j>` zo7M{|98<~V=6O29eb0u?Uh&tcepdZPf4cQ7YZPu1hE-NOiy71%?m7h|kCpF{=I(Qg zoO{XmO_Ib;D}qBS-S4pb)Se~0{>|HMhWon&e61e&OnO%Oy(Gym$3mR|c+-k>PHyb( z=dZ0qM!bS_^0mXcGIl?}td~R(u23tA)m+&KnBeP+=!^{aJ^d@7zz_`P0&|(kfnULF z+nxUa4p5KLckdMwp>JpsR^Pp^o`T;-PrG%MkcrEpLMw7w4mu+<$j_SN$q$&LachHzBXtYJu`%PA z-1e+$p4}Y4eu-Fo+8F(z2iWSE^v7K_*-x)~#oC*GNLcXDuUZFoC58#FEJLr%cc(P8 zkXMt2hfRrGdJ+BV6C_}_K5@%86}9sRY}O;1H#Y2V?*t5IiCt27r!{C$vi|@IF&kd% zC>pNOA#foErnDzmCo%6j=$jZ~JkyJKB3B*J#_6PFY!`Ff8qwla1tTaZsi_&+iYBwy zZhO3=;#Ykp8pFfir2UQf3u)%;4khk_O1K(zmU2 zK=4Jb3oDh74e>{wrC-Bv-LkaLlK`MCE*)2X@n3^xCz&1V${iO&HXElFN%tX4^TDNi zmE6K~ZH#MW(nZXpJ9z|VB^Rvdm2QAa0o;6kJz+m(H8}Kt8TvRt1p>< zo(cF4KPEZ^K0*c2`3P7f`IzWWEk@@J6=WSfJdFJvJ!&@?rD^UcodHp~G01g-W3Juj zsV}WLs{EbytC6AmP%DbW@cxt;V;z2eE6KI`d`s#@_n^&I*q499xQMvyH}{6?1L%ZU z-=d>v%;99lbg#(H*K*3{okAx?$#i5q8=~8$jTUZA*TrQVPNfZar?rxu1)8R2R&{uJ zm$KNpFLvz}C((-XpYU+~Xx{eSk5_&Y#%ro`bzLb8`mN9-Yw73S{%svg=&InUXKE=L z@$4!Y88mUq!_|93f$%Gjsd+%8=rOT7Mw7zVe~G@en{Ei6XtQ^Q-KQgM^l&pU}4h@J^FzU!1m+Kt|oYuMBmXqp2im>peoBV|uD!?vjrXS!awahkf zBiH9_eOJ3Seu(pprZ<0p*e4!Ac}AwsK}S>hIg3BeKikf0+};Ir#P*e%?hB)7UgsZ4 z8e_<=jD^-5)@Oy%4q-<Yy&VBIMXCz3rSzH7y;Mr%vO|bco2a z6=Je00D`vrB0?v9{&=^*-CN;~@SJYAe1HT`m;%4Uu-K|edU|-@-Zk9ij)yWfOB<|4 zg)AeuD{G*VO`XIV%}z$?{vf1{>^WQrE244{oou}~4^lcSx4{PH7Xb){ zZnLfGT>u@H+ylivmfd3Yej#e3k2|~*edsyPJhW2EU%Ki0{GP$lU@nvLwGVgM)!nyv z^bz3>!<$gUGv#J>1J(^1>aCr@tOSyD9qNZ-_J+BFK}cec>CM#t0Ch>^;hbwL$^96c z_mxetTRTt2l*)VLc|Yu-!Z50TiJ+!$sy3h+(O>}FPKdRx79bTa5rtHIbp$#j3Qsg5ju=gPuT?B`NN)*$50=>;3IIqf}CsquQKbS}(0ilxS>rm&N!e^P{wSG2oHw6dC72%g3t#lyzmruBt+J0{X%l^^^lzV|8&BMidZYC2fs{VQGHY1MSE z$&)u%yLhs7xgo_qLgwYR7#r4GnM3Zhp4Y7656`DFQo%r{!{+@ zvDo@t7ZGRw0M(cGeCuxwHqlz+exsVxm{wu$3oD0Y-4}9m6PUxnIx-ejPHG!Y%#t_@ za#7+qjXAt^GPlL9A!3-$C0o$@rm9&o18m0jx^wetcOZ_4CCreQ7G8NW5O3SAuZw0EqfZRQ+D$0$5zjCvR$W?$v z?Fh=C4o%Rh?SCl*|JBoNGoBseHRUT2X z2Yf{o4Ep51{{Xc~w|heV%&{}-#yXOgmseGZ~a{mB0r=BlWo)s3*VAzd?HEc!4(I+du$1cF} zM6HAOt)i%B{{YvKzxjsh(VB{-z3{w#uQYB5Cay??Jkm&79v~tk@e843oFs3zMTKlt zU49WsQ4`tMVdvti_9q>uzr1E|=$ou!g06yAGNLCio7&@fD(RIovxAdsVt2;EJ#Eo9 z+%j;Kv(^xyFZj7w?`kv^jr?8Ff}y-(a>((;r8syLLdsBS?p+=EE`{8J5j}KAnOP$o zO3x5RbI2nj&o#%Iva+(Ww(O;Jee0^!T(9sKSMzQ2IrDC>y54>M2=20RzKe&*IC-b! zcysbrLVrr%YUYY?;tzVn;w-I{_n+En57$~_;kPeTl+PNTn4mMJNu%H>Tf z+mcwFX9Y!A+PJU5!?YC5&l7^I9dPAEP-*1{mox|><8aWekNDcZs?FO${SR05N9_o; z(CS^8zpN%b9aU8%bu7x^>OIP)j!I|ccMe4WH$3HX&V}4loB*;W=Te;WYgu0CB1TkB z>>C2F$0|B1R&_^iL)mJ;_G=EnqxYugp51Ak$JyLB&r<~~PL_FF8ym&WHVYK;gF^+` zhr(xf(|1*$?y()G#<4qzs@&Fn2>OlvTz(FNX)&xa!V0(K9)tSr{c8*Ig-tOG#9-); zR=Ar2JlAYBYz7EKsL(m*6elok=L=L_6DsPX?8^TDwKEs4Z>N7)LmHo)Y%B21_|vt17H|Im$7*gV2|Z}6z0}9{IyC7f zABzahC#e}8d7@8$pKk?`#R$8~=-Q4@v)LCDyV<_V%GiYc@MPr( ze3Hul03?OkBxAdr5Z1?Y>){nbmo4)f6XL2czjoBQ?09GmwTi97W~M!H?FpsK)GBJM z+B=G{Dp(sZ=;YzZ_bMoz6+@tZu`@Lc;!)W>uTp6bZNbp2Qj|n44;@oV!s>IgqC8u} z)xTqpzSezKZpZ%sqALEV{i<+GFUl@#qw2IdqeHEeH5;dalg3!!s_ETI=QZBKj_Y4Q zv|vTqoqgv{3q6(ZyJmJ)ryi4~edDBQ>p$suHBr%0;q>gLrHQe|-T-bS1&?jb79V&h zGStt_--~bYShZF)Tp_H)Ot~d4=(A|vuhAZCFile{Vum+8?m9K1^Wuq>!?}+z1`0oj+qj6SNI&K4TM~Wq`jiBD`H&4_Z+dKim zsJ^NF;;FdFo9UY^y1z?7^yoXvr{b8{p&qC5{y`cGujseWlt-jk*{wh6oN(v4tuscrMVdG5+K9u+i~BD;M91%Cq-8nAog54O+Ddb zSUpAwTIy-F99aHOZtlZv%U2SD-IBig3f$Aj-=5Te=Hq`q<(owGA?dlU9{uy6qTQ}8<0{wSwmwN2w0 zMwVv!B?#(A49GUK=%2A=sOK8XH{{k?eD|xr`0(sR9Q}odfk%Ujlny~j?iWB-f zvAWkY-g0Sc-E@WgcwIY*SyWY3H0P@FnTtgz%TowGBZ`7bXZ%#&$0%XFWO;?!c<#h1 zCZ(${jQRuGrmBXrm^wKcNVV*$878kzG{zQcrZH?qLIoIW!{T^>=L zvw}}MIBEM6Z*{i8wfiwx(mL?uk-yQ9~FkItc2Bh*> zj^1F@uQ9?Ts6Fh_ziCu{P%xR~44xUGzopt}`xQK}PZK7M?UFYOwaqsLJhZr$iw@aSp6`T1D00&WJiln@I~Oa#X2bEjnPQZ<~Ui+I)Vz?-I#s_ zRno!}x&^)m$;IR5<(Y@}Kx;WS# z<%l-fAI|nnH&Mt~;bjD(Xv4nMv7Z}R^%=V#{)nskqxSBQU4(clY4eL7Au-oPZXrGx zDw&o>0N~)uv(XAX@?%=VEVzU~nbwx|O7{S4VxNh=!RncP8(-Wl7f97Jn zf~bMDG}F_+vn+05^*C8e6X2Feq!x1=TJKoXbu=cXrWgkqBx?uwjg)aV7}*O2n#P8K z;1$;2Z49od7+fyuFatM>j{voKL}XhVj>(5&Or(3yx#}I1kD~Lz+oEQ7cn&LRDOp91 zVh}J{f?`@c9o8SWehEeRKMA6xBE^umH`Y3pW#ulfuZPi(qUl}x2JANxs=})ytEGNd zu=@z^J_`cI>2ca2RR)h5OCL5lFlOMZFxpH`ifT%>W{yE`Z+P(w8NsS>JVsh6{S$UO z>9>(mO2Q6T9Y7ZDK^EIyNCj}Ny2WK2^;s;kofED!SByoMF1F&G%XZ6;{R>6)Pwy1Q zopXJwZ{}o^(B;>v{{XpI^H$t9o(FaO*7ky;c3PGoM^!h8L(SByZg^64nz0CadUI|g zRU5f0*bs_5QAHF{R!4snboui8t>#|KiYN=GpF|<^SXe2&3;wZ9;1(iAXqxT?#D1k; zP40PIIdUnb1Qn5BM#G9Ua@#jX>k)g|WPE3;R9KZ%Iun?9qlnYhv@&2@;)OUr6DwW% zmRGR#nnIwTBl4aFY){8l{YHKJvHt+*iodEqZsMw3DeD|r?FuPFfFqg?xL4eIj=JKp z93~pJmp7{?cvI8OO(A4~0j{af&;iL2b+H>Z%o`5(RYZTvss-JDL7bcXQ<)jPfK_!6 zQbjF9uMCPdmpJ$Zv{>}`^>LNmSXrbj?jOYNZ8IM0bj`OVgVufw#whAZ(m952s&qf8 z6Lfg3x1jeHJu|@SQ~@aV@!f#fW0+n40RCh@y}xQ1BXRskKjq!_azN!ykdUdv%<*LH zXKlTAR@X%Hk-Zyzt8MN701?&wSnvKpyJj%|0K!<^6a;kx^S|n|UET|9D}CLx6^;k1 z8@eX{0E0vN_uv@KP6b}dsC91%Bjon?DhkSa>S$|eUwCB~2fNmX1-4r_+;a@PE&f*p z-52S1v2a;{S^O2eRfguZ&bb?#Wx+df)ym6d*iH{cXFIZgX-=ii_h)mQ1+?V0{f%U} zcx}59ecpNCe9L$5vCJ%?9{R#hG|aXH1RZG*^DTccth$ljpQl&7R!7iIZNTtNe^+*y zQAHGYUe_O(cinCL7LHcQa)zho+z|f&C76Df+@kC^)UktM5&bn^mReU|i+CV`O*@K^ zEHclbg8u-MYR0Hwjh!nSl=vv4eEK!FQqvgCp3>9efRZzIl@=#FdOT(xDV6k+aRr5W zKd5BJw1)#`Gw|DKF~I*v+!jjz2-CT#ClKe z_IZa@vbcxgEDn-FdhBmR{{XDT&28ox?%XHcOc#=r=KlZzO>*sKOx>gem`WvQTcV1Ap& z3wo1%?YYkr!3%43kJbwOX1bxK?R3W}{TApqc{sx|K~splYbU+O^+zApxGY||xMmww zSmHhIGF$bz549H;66YOGr0xhUgxW8`mA>GqfAU(FbMz$d_NF^2%fxb5TX(bm;KhY( zP5w<%a~|OSe`@zGd%2ctd0(1_mPxo+4;|J#R;n{QV{ia~R-oB7=YqYFbdM=#s*vAAK5f9=?{lbrExx6Wd{aKSQ@Zd! z)Q19rK%nFbkzmI7d>>U~V)V{iK7W?_+TgKTm|WQ+b9(u7xIvx9$hG|2b&;;{UQWH$ zPghu3e3@CB^{8a&S$=#9$NvDLD*mYbyYZNdpw2xKnt6pyH%h^NAvMc41PYf=T8>=E zYpSoIWmIQDn#!VCjy=E&$%$u_qIuQ0SLWC&&fL~X^6{o=C%@_YjFiShV%)syb{A?6L#WEdK6$y8as zr33S`)PeW{ouSn|wsv8|n#8F(HyN43nrXUnDDEikC#!FqZ}Lm4f~>nBlr=v$V5h55 z>Qu(@F!(HZ`^$c?QoG*W>Rblkg1Rm?SPQQ}&skXRlybqTys zPl>V(c=xX-72<}vwi)<6tRJ;IOHCHNDb?Tt8Yj?zof3S>1CZ4ZfUjq(Kq6X?G$E#nHb2y(^nNN zjWS)Ms*YD`wo*#Zz?5OIXOB^TYK*Y;4^`aSc&{j<-|*`XLQlJW#q{58Ji+D-*mLg) zRi{FzAE{~dh0}Yy-acUgTQIRsj7Zl;La2q*(>b=&lD*O0rHUx-JJ04ASJg;$vL82L zCehUWRYrZ~o~4hUdmgnR>Xdg4=YrY8dNMyuta$$bCs;%B;t~CIibz{5t?zE;xdpO# zNyNq>(Hl)0BP{)TEGrg#0MgY09@D|gYHrv;k#CcA+O2M+bK6u#!e`fGLOb^U{e>~^o* zm}KRyv)y@rpg#H-ota4S{hG1`vZ-16%NAYcT!WD zF9-K~cUt^-Ev>)~4PIXe{!1~uf1-J}a~}Af*T&-4G~75F5LOwt*qtLoD%jeVj(wN? zsvjsgE62V?QyKc8{=%K*3j%?J#@d&^!GM0!u@5M9vVzJAW<&aK>$~_WikgbKoi#+R zl1FiKjmou#yjIU((&LznbmHPM)|vgy{v|^cP)8h5v^F+0HLV-~3yRzPYom#nOfTz6 z)|=|`3fk^`u8;=u3API4pwfBS2DEW2VzFV0;56=e_t-t<{{Z0O`nP#O!`WMaI(7@m zO74(-UJgcEjvJYinhj%<^g12aDX}@i=!Z9wv38|v!FtxkGyecVK;q{$j$vhC%}!%qBYR z4)61h_a5M!(sjCXbhuTN?Wu_UBtxQcW4NoMxTCtl#UIRJ%&sTq{50Acqxvd<`_p|( zA3ab!Q@bS{!+#~SNBJ~^^wvM|Dw0l)vLpWhQNOigKh6);T+>PpUQEsB#Qvmzdac8# z>%KLb{UM=SQ-sx1V0GCXC66P1uA za&uW6FK})-C}has;E8OJwc%5{Gf-6#-ufzeGP%(^k!yGK& z#s-gO?W?b3m9fz^-xSR59xqM-z=AX@62JqmB{%t0u{t*p;jZFI-p9TtwnjLIG=kDO z5Vmg`W;cg6Z8~OkW^d_t`%ze)8&8VR%Sli=uNoe0J>g=QWlj@WB`rgBZv)BA`a^i$ z(QdI+)YTQ$G1I`p`DAsP(6)O4ivyr+4eLnlWs~VU{gZ%LmLpAr;nP;s@NZ?GP27U7 ztD3&Dda0e6@;tbm!GxgJh3Xr!Ido)kUQKa7bKEEXP%2Y`}Y06|-^9c6NciC%xIm z-Q8la`gXab#`As!-*WPY6Cs-pjhA;;PCmr=o*Ww^Y*A@%9oN>#BDLkeyAs8we7J~6()+MuhflnQQ}=| zPRzr1gPj<*!cRjaN%`sHpy=;VD?8d2Y_`Aq|!ZhEG&RvU3 zh_|xoEPk&5n&9?_4hME6gu`0W=71#c?^P7?K_i&)Vm#af8kYmFWkYq0^<`($YNn}z zt_bQNcyw=V+Tpusa0oHlT#o9@%k)xrA0Gu_ar(* zK9}jC9o9q{-mK7#yO0Dd&P2Uo70$!4S}M!@RLw5>y#D}F1leaHZRI$o+FG(3X3QHX z{{Rrz^`M&D9`$CEu8XSO6}z`tSp>=W0zIkGM)7u=e#No0qI?RAq&fz>tMt0az+&n_ zqXz)IqUIA&GC|5tex;ek&A$P`2Q(51(Gjh|(|O%tn(F0PbMrZFr6;7an7CP^+M#eI z-)g0oqo2cq9sYM8FsB70I@fB%D06${TzP~XMu&0SQ3(EG0IhO~%R7|NH4>xzxi!tBwy#Yc!mE|a#$=>_T62P*ls!u{z$hEJ9e^!^|u)a>DvqII!uu z>lDKoT^TLXLBqvQ1K^Oc;>3BoBecJQX7?pizmiDqKa+QVsWrfW=8!=e9oYWQQsdP5 zU(}tW-lgvFJ_{1VD=>|y47#l@Z zURf(h_C?^|9~IHCD=n+U1$0isppb~q$2q>2=%(QGEc{v&G)x~9)$UqL-=b>w6&>%< zH5@*O-`>J@3rg+{LZR4Z^iI)6#j?9KIn zu}9Rrs8ZUe4XU?lH^zU!Z}+P%3p=^4t+;BL0kCtpBMtin78dafUBPDLwn6^@`U-)+ zpzq!)IdgS%vhtP#XTuGLVo$kD=T2Pq#u_{nOfJoHpyH-wBU;D6_g{f)mYcJ8ElL}Q znagup_@r(6smC?V{i>U&`yOD0-c=0!EG-9$9|UyboD_}BdaAO7OLxcG=8k3(*Jymx z`G;gQPd0C@51c8#qonbArqVBB#m(LmyFPviCQL#T;<3->#U9$kUzxP${)g;zNhA)+{G@jasiAysXw7`W z>5K@(@vC)5FFq1W6OlqPj1~!zsk|O zY31l}m$&kdV}A9;VguaOF;utwNv{?Ja69q5E8R@#q8)L^#W|&>-fqb4Sfa^?*T3FB z7b)~S59p@YfI+xC(;H!!tWyc1#%YWdapw9i^qPCZ$96{%!5n1DW~Jpbd3}E6f#4Mw zoXlfOBg}hGHVXmSoIe1M-bQ)cI`bYU)#1M##{@ytl#lE1k>i(u+*@?^5q%cAD{;|j zaiSd1S59ni8nhKrgwoA>H!1H6Vpw6oA%L6oQb^y|oA2G_GMhdPNiQ~4+x*Qax4bTm z7}b+p=I|-9G~WGEe#I-Ku)hUxviB=1Es}m+pP&F+s&)SW5l~b%Hw%m1c}cXg;ba{$ zN%tV^hbz1F3P*e4G4)rkaBcM;2Fc*4YZ*|}ma1IE3 z6TDG99qqwOYu$>fiM1@uG;(`i6N_9G(Y3!ZK~}nX%@;MWtmPsU*(l;q+wUbi{bZn4n6RFkrO9j?IxU}PjPjq?ZQFMtZXZhD{{XXpv86?*&EBh_>FJ){V^FZU^TE-( zi!}Bo^-Z~N+8j5dce-M4RQ9|NowB=J{4lbPy6>gFj^`KqD5r+5HcJEA_=vk)-9b>& zx!Suxx+vPh%%FlSZm|uOf#AM|(L3^fkF&W!ielI+`WmR*=6x6c0Hw-@9mDGYZ%NU= z=)Xmb?FI8wQ9J9(Khx&9mqZq!#4UAND{)7VS6dwCdxCb5ZPQ$Ro4laHXW6-|3Ck>Q zSsn)iWIaoxeXnG9vM0&If;6CMd$}5Xk>_-cztQKvWl#Qq!}auC`^6F#-%S3S4*HUP z$jDrEK!c|>W4HrkTg)JS09{xh08VRrMMiHEPloJ1ZA@-Hqz#R{`>qJ5aO)>$euIa4 zY6gk36;HIJLpC*pF)WOtGJS8Hdtn{h@73*!`WZ za~*M!zq}DQa=@UBz0Z@Z`*nO1_$*L=Es+Gnq6HF~);}_YKMd6|)HMztIFbC#pQ4}DfclkL zysk&hD5_Rn-qlii`BQsqqja)$2)aB@=BNodV(ePP88 z*jQxU6pZmt;8lCGRmsPXG!qazxIJMNT9_T{3lPI%ou%xvU;9?c?VTnQO%4%=#ORwF zot9y!>pM4DLp(JQwpw{vByQm5n}fW7MT*2WR|3Rt`Dt>$>Be#t{f1;e&8wYnqa$zn z!kc9AW_CIk`knky!0~o=sUPgI{fXF4C;tHOr~d#UQ*7l`SS^uV69k{{YEo zq=#yZkY;?<))PeY3cIoh>)IB>w=ca}TqE zjwqpEWK3%sI5nccD#}V)=u9;+2e9|@Scc43;S+nW4SU|k>!-#y_^c;vukSdMY=0`z z`$C3Tr;JGx+U6bv8~K$ObroO7Q?cGQ*X>j6wjAwabg}zxKU$MyCLa}6!}XhQ;-9j( zqj(ry>UZ%+W^sSWxBTIM!#J~t#WVcjH)L@m+H@`d01^9wi(=pS+Q2@mZlKvbE-v3m zFRj)esX<9eNG${rv&0WFuDX(jJoOTW4nvvs1&r+^n2c|yiad}WH|-m0fm1-qTW1S} zh+)pBadUegu?)>TP%Uslpq({N*L9G%QAwDb`k&giY95PcXdca4TI|d`M_B3;x-ih@ z>Il%UD9$dM4f>LO+nVgCnB|X#oR85dr(CCxnC`BtnO9c6@S|M!G0w_t+uUlRlyt2f z_qr+FW_pz^L4n>J&~R`LY6njIuz5U4bo%4%D+y{zx^R^G88mc*IkEue}9JIf8g1w5BN z>7BIy0E|Ah&v07|?6TK=Mt$q10c|Yf@Ev1+Kz{IEm`r_~HBr1t0~|TEVx_$^hcJOn zC~lh2p0K;SlQ9~GI2HyAcq#FE*}xBeP(F@`%{0f~Uwt^0h2wax2`5QKQ08BYc(nS$ z!Mvr>&z3A!4u$9OvGtkuaSD#Qj}VKdrKpZpcd*~>_!%N(oKqugV$BvC<(4Q|Un|gqZM%O#&Hp-5+s*H!z%H|&q2iT~#up2hAD}I|l zf~Bpbt8L(&kM(i;J{GSR!>9r2ry0J3bt=1SYGu19*dlMzI=M?MleoTDxz9XHjn3{D zD(Xm^BS=_{0fE_@*(lM(#>QPC>xtUi<=KaQ%pB zs2cM2B0n)*u@OrOM{h?p17H+v%H2I@*9DPd^b?P7MK?0a` zP`EYOyOh#8SxM$fr>BAzhfk^QQyN(`j9EFQg+}d7(T~z$RMnGH$R7-^ib3f#mY>w9 zJfLj_4VyEWRL}Z@t@gEHeo-*0>oTGlAd~#TQ*AZ@Q`p3y2f61V{{YQp9I?vW(LJsm z?KksD4HqWLOj8Y~1j|s5PgiOr=mBZa@_ck5LZbv2Tg zRL=Qb`bO!!l);(ovI4_%T;`BkNIl_GZ3Zb#VbfgKOKuqLljBe&V6pOeS74 z7JX)w9wR|7Vy+YZFoT-z$p?F+jh(l(zO!WfQw)c4{|;x)QUQ0sjb^+)R5M(zW5iU)$4Cd}IvSxYq;yly!Ua(t3K*`DH0 zie&B{v~?t`rEuc2cLlQUd+;1Ru6edcX~1@hf(I9X4qN`!PGKP_qdim};f`H(8a81(V zc3a&8Z&6fLMW>;ck0o=-1G|p$jmGC3sQ0Up#W-l6)dZl+oX`6a9qG+hP6RGo6WGTa z5S;{Lg6J-~2QG!nqH^e*x+1bHt0K3~K}>GzE?+p|jL%lz4OJj6npb|B#ouPzapUI{ zI45vXYVe!d+;rWtPs;kws?WVTUwpd9_RYJk63sOVv;c^R@hDK}L#v(XURfu|ycx?ZmfCi~u;liJsd^Y20K zZ)<|l+Jd@6Ydh^xz3l5T#CU~CjpC7YTU}B+ts|5e{wQ1t#A-cR%62n~+xg_*=2Bvq zle<-w2mb(wJ8F&Ny>cIc&HV*;`AhcHPZBVQ>l4SQXQaHAbbNUhCa~ zq}j=}@*Wbq?Oy7VPsTX=R${N_mEgE~m#J~!hN9`EPYY#X7FENxFGu=9PR*s zi*37Q8b9I8KTkp5yjMnD&3Qp~X|Uk^NMxSTqM8uHaBkDVaNh&tb2{93JNTzFqJBDkEzVW+T_-^k}+DMBnsCB_vj&}W+#{iQCEk1 z+=o;RwLs?KyN2Spab75%^zQZIQ@hoP;*E+Unl}ii4qVY~v2;DAwmlq@RjtudxbAVX9eGIN>7Ptjdn*-}i zJwfVDenEc)jy@UaB7R|**n#UzcxIMG!Q2xcablrx57b-~>ND1SP8weALBYy7dKZm) zr0NuMf_sXhlwV#+rtKc3H&NcTO&|fyC&zKsR7H`;xS_x+jZ_b@YPz~5O_+|U=A>h9 z$CBu{6k)z#x+9^tf@@yeb$ApXkPD7!vVO$bZa`U;(_eLc#dt0nuMoKKT+<4wrw?hr zWbq23mC;Ypo@YX&pkpQIMD6K2j*B#ro{hs+uMOKG-}||T-{?E{iW+FVq8x4 zaX@oc=B!Srgj>luyp~={-DPc4^eXp260(J*x9Pa8H{a!V_&}vLkJg*>mKMyEbrKye zsTvzj&#JLU-c##N;C%7NbCd$%+ugeXb)8Y_YHXQG#iS0yrnhkYslA}4?Uff{INyB% zwQWE4fo%(H_Pf2RJsIs$JE%QMcj;v3ICDc#wPUP~tq^R1XybG;zk^mm;GDohbb8z&nYQGcC9>bVh0|HS)qK3uUAv0l zh+ZKM^g`fPjvA`kHcrCceu{lW(VVh0IJbsjs8UM#q%t`5iADMOYPgH7j%hnNhy6!> z@lMGYyWAR7on=&8QMa{eDTP9@Vl7$<#odCt6nA%bcP~)fiaQk7;2Nw*aCZp~!5xCr zoAjeZdop!GC{`>$Z-= z)aSy%i+gEi)*!2DnyNUB`oo7koe@jKRngLFW%o+Cg~ENi{Z0l_+{{aZ8OU)qwKn!R zyXq8DRelU2c+U-?l37Mt(QquP&Z+38Jlo9nrgoQ%f^7rMoQl$X`dR3Cr4br)~X-lO1L|Ju4DNxqyLWS!Bm*spG&h)xmVuKk-(zgF0bebP* zwz#U>HpuP1lKLu3h|R|iDW#?2saAk15#8+X>j&1MSHJSrnw&x)2N;aeJN00eno>Py+oeL$2~Vswd;HUc>=rKDr*t(ql>6Gx%%Ogsm{Q6=U5zK?tZ*(AuA0wSiEbh+;uzPFQ2F3>daIYgAqM> zVCOFE9YjWxYwGXLwsIDZ*Nyh6KAvj3>PryHZuCC{EuV2A+FX!Kes5qS(sv1a_$k(l z>_tohkt|VorBY#an5IXSL;e1^3F_#4?U|fMG5RxyhL}VT@3?h0Qsj<;)xoA`ladJ@ zd&l4X+}fkuZ9EA%RerZ|a#$TjW+qo1(3d2m7;(MEUUV6o`k5!381{wa4_T3>=sMSv zyMc;Sw{f0|VH;XmPfDky)hQ;B7w}fcD>Mmg{zhLvk)!1hZ53?kHvU%1uy7h{)~}Ld zJPIH>HW-AeG|s>{e~hsWOj+3LCvR_u@2J))0(IzDdvqZ9cKu}NgRbCYNBd|4W{7W! zfa#_nDq-2aL#QPqDH=CLMK$)Unxan~)j?K{CMjA~B$}wFEm+T{QP`Odve3NUL_FP@ zFoxf}9if1-Y!wB7DBsgR56t;YNbTbT*nghW<%e*R2+?mmS5)lL+8SC~nn;Z$nH^=_0whe8s9ZCnr?ZWd@#^mHemC|HVIL29Zk_~* znD~q9x^kck)e%^WA&&pErU)1x=*y5!{Lp!54w=g_QdS3@PN^z) zs#Zup9*LSeVujC!RHifIR5YD6h{qKvNC@3IXDM9#;7XVN2~u}LjoOxlQZd$dUwU=R zgQfGtjpgUrcNu$IWuH;D6mLWYnkrD*j@wz0J0(*oI@tiLWwBH)a~!w56b%Uc;mL$J z2{$Y1nC4;6hP(6~v$@Wu%g?=yVzeF5M5_{wv(k|x$qo4MTW@W*wL-}E{qW4ECfeE` zXFpVRbJC;~CTJ!Rz1|(cFy07Z-BK7nS}n#GX|^w~2h7Y~$>f6sNj$<d&UtBgl$z!w=v|x0gO@RRAyow8REsViMmV*=5o$~pOSQK%mbf0=!YtcGknYijdy@g@yW?AWO?t*YMGMVJfhn$+om(JaPX&8Kp_U^Qokf6UJ>3`^GD z;p%|!t78zz>rDoD*mJaC)sf`D(>s4Zofweh9}|^P99EcE`UVdO-;t{v5Z0ViEL3NK zNyZ$kRgt^!WD6C6YVDQnL7FPgpyJ=lZT6WEbvb1aS7$TEg?y4O4?rxV*w`z?VESDq zx6|d?ICcPCb$FqN2RdIe%MU3u@{jhXZG=%?_q42ExoKIAxZ9+ol6nv4XFA=I+RU;q z>GM4SOuziUPqQE5O*equobRh6bN1Zi9wpqw4w)kRLT2Sop`q$xUTYE&Jf&(Q2RZ%k zmcMx-W1s5dJ{L_MYbk&=Ow44}CW6UkPRW$CED(Qfo;2Zcn|9I)0DMMrQ#1X|BHP+{ zSK@6dm#PRGirltE?|i&l8UHNF$kCDJq!$vd%cV8?Yp9DBusg(#lRWj1vsFl5cLUif z03K&JMA-5CE^i&nF4i@Sf;=piJLIdg=vUD6K2UV346H|+tN`)) zwQS-vN<+Rd-IoWnnf1uRL-$?v7#_`xD^A3qJ9YUEn<`}X(x+GWS#b7a8H>c>K1j7oZV{E5fi?QzNElyX%@3x7xH$(adomLErJ=p#q&F>2S;6tR%`L9jEd zvS$X4+23XZ>12>lOtR%kP)eRq zt&M1X_4brTYLA#IcbdL07l@mdmgUzkA=&D8qD^sW$$7C(iE%#G^GWgnM7OAMkn>AX zDp)=CNTrxEcD z=R|YbcKeGCl@L3pUW=95wE69~c*d1b-(y%cF%ET30c;G1Ud!vCq45ag42>^bsL5^R z2s@=TC&5ebH@vYfP^Kv~Qw*kiMcvc8JFnBY)K`k0b{67n38Ifqc<6Uli2wfmyO6g% z+=3~fQL)S`z3Ukjh$)Z*oKS)#ai_Q`M0Nm3PN-lgKfHKaloSr)I|VliyckmkcFf(B zGvpM8seT6(epL1S>y`P>$9d;xSEcJr2BfS-=oF7uf5SGOaJe z66DTBvoHQ3m@5<44T%H4p4;%1Q650YD*LI7DTP0dQC67PU2c{Nm}CtuWd{c%t1Ap; zfx&7f9QT@6K3bXfE6&jJmq}fjl~hr}Z6421u20k?lqyHwcmjNXJhY|oy~x-s8R3J<2R?ME6ji2*f_TVQglw=S*Lf?0{?h0Do-&?q&F)3zCZm7m z@tOP{F?tRqGPgN@{M^cbI<2^w z?WOFtBlDxH*v_C6IbfJRr9qo-F~qCJjl=3fuJ8tsD=t_)X zhgDHuS4h|VcaFMChxIyzo) zHr`*9#Ow=oxXaVEDKa@Q=E!^a+CIwQ$e50Z)i1waYVThI)C2VBzc5-nMp+nf?zkD2 zbM4kR#U1P-wUHLZw5XlwUfogE-j$4Ku_fC1CapmJK#}KyTz#pAn_OB%nKHD$>=b+& zctz6ktg0BooY4@H*4~iTUONV^$388nu172Sv_$dvE_n5_wIm+Ou<;P!xw{8uZWNgV z_UTYSHm%x29r}I_Uy*4t9!F}@3QBxCaK?w}#A>otdColXFxj$}y&+zXd|M>uM2HP~ zPZ}M`2Gr^zjn^}r$@BJj96Qs2)`d5Ewdu{epDnL!S~wxw-Xj-VLKU2CZ-E(wZWDn6~3z7 z*$R)-?9jMGZGWn0Ot^f4E>zn=)Rj*Hx6T+>d3LxGxB#mljI=Bp#}XhbiX}#RSZ7{y z7ck>E-NrLu+lR37N?XuKI7|ySb-bl~j^f^+UnRD4&D}9th)%3!rf$O7%fk;8og8Xu z_P0zd$S>$K!czV_R7e7O@qfCE&F{T{i$#*hy?-3_uNi3YG^dv^XHt>5&c3`SQC#4w zZlsM*$Vtr58Gf&wnMKkRba%n8YI&ny3KXP)qS{zW^$_SrVPI^k!Z{f_6*PoP*Zqpo z3P;wUFK_n7rKrT5Au-{6r2b?}xo_H~IHso$eq4yR^Neu1R9a)hy#nIZ2_0BtddIOm zjCjvDuDZ(Rwv|*c(Ev~>)%wuNA~D{q7sHF_pfApSJm=%8`~1{bL)nsn-rlQYt?EP# zC-+`F2k0DK#u(nL)H+C#Oy2T^1s(!|l<{(Ob}ljbC_>PF`0XD8UWSEsf)I=0R&Ul% z6sL#LH_@D8yN~EEl^Az}iaa-YrFEu>D_jkoxH}8*$uUcJFgYom;D~@$cqGJcCjX>0 zpVTTs^sb%&G6(`7*MX~vQMl;BZ7fX1)eHFo(ocPT=ip+~mZH$D9t|fgJpoQR#&dEt zB@ulUZ3_3rD(;Hx_sv&6ov|kCxw)tb(1veIM9#} zj$&kcIje%zyAPBpXNgj66V7d}vBTF6)u?9imS!dMY|e?sr_d1dQlzot1{e)A&e$GL z0?VUHRpqyL)7rtXx&qUs$6Z-vAv_#XUHtP?OTCxFpDcDCfHN^R0V*xY^!hUvPbR-w z)%yvOk_*N5d?!zq{PvYMvx!(OIU{zA2fu*L;Yt;gvb3I<20cNfPzKL~3TNoFV!VUi5gn!Pb8SIzz){HZlLXfhpgJ9T+{Ly~UdhxY7EIRWG3Fla z;$<%iCwnMBx5hFPwR=2Y&S7tKpYF|>9i6s!7wjb}N-!#w>op1tJ*4~bNDEz!&s5q3 zxXNwfc7^zcpI(nx{?N8);+!4y$MunR;a}QMMTW-i6$fD9Xvr}6J}_n(xt93XSR9lG z;dDKx;vs8^g zl7D%3o4|B1p7n=|dZ92m)lK~8^nz-Rfn;QcOYj=soI~5USGqg-%@D8L+kV{>RYiv8 zL+)SGPLUJ}Kb+PoJn>SW0j}4?4Oo#2uTH+fRM7qLiCy_u7aNZn_*elRUyDIm5ph)? z7vp8PRO}Q-O}bg)DNT{>ox)msJyK60TVk^r!sAu$?i-)(c}d4=K^q$v8N2OCvWFLH zlHz6B@b?rGKNrb3J%>?u9j*R{?y|ruYTO6%K3=I@4OPKiv~rzc9cb%Y%x`rJ#^~~CrmDiFC((0UhvuilP6;no9^~ZLe3`2r{F%6DQA3DuCqFApEhNSLT0>~ z0UeQey+4RY@|!`BOXm}LXZ&g2;nGCD9Iy>~uU)M3aVUjL_n61d^*t972F>%%FOgY| zhPF=A6Q%V+oP^gHlTrM%!eWZ7MulkwpM~p(Gw*3}rrR$7>fcC)V|=SCAJR69372)I zef|1cOJJg?AvSfWyh1K=K341)i`HEQS%&g@kc;6Ou4_~_J9qWiF%&iDKD9;FiN6vz z7q@gdxfK|GZY@bO=r^4?2X7abqOtoKc*ArCv3g1Yo^&=cdb|RR0S@$71NZfqO*o8V z4cvJ;=(gO>nh!)Gt|2-fsNsxu*!%hhhNT3K%K*j8aCqc^@xA?fJcQu5_XF$x`wQNc zl~uaLeNI*l3vzD+(g$ih%NShkFBud$I9f_d`7?daZu7!HAY|PNpFwuf9Q+(kKvQ0i zh|01=Jf8B8#bD9$pk)_Q(&CQ-A2YK3Wj>rSYz6FTOVU+o{X;NTRT=#-N1r^lhNGLM zvZt(MAd5+nuQp%v^R9`_YuCae+5J1ws)V^2N()}b+M>b3CceQPuB2?P#3zhYSXDKC zyE{DqL6jCEO7ly4n!|!iF?M_#KuY$(Ex;A!fEYeTM_mujEGT*muQO&%KrG@nIIgnM z9a?%yj@)ODyhq6XLoBm}(;?q6+a)-4TjK7Z3~=#2YV?j<=lNQ6(u6DsKxXYHy(nXa zbjmbu+zhVcW_*t?irj+p5E91Bg(vJ#%_)uw<7@(BjJI)DKZwK%G`YXI_yKdj@@Kk@ z-fSY`ko1NB2hQ?sw?&#z_BTK&x43*nEf z`&rqOaC{-?sG|CR`GwzNd=$8}_LB*^Ik;un;zv7dU$IJZZ8G#ZW_Zs3Tb>?B=J1$e zVwKI@P5J$`18ssU1`B^{_a)}$G;nRH32P}tQFC_4*yVsN>L-~ za3>-lcqFpab5_2;G587VyE~A6yHZvGcX6sEqgx%_d+-+y$6WQ8Pm~*P;;4Ts#uz*& z14p)63&O&7qU<&gE>HDw^q5d7(x3<`xn=Ol2Zc(EJ_()x&GxDOU4E$FU+nBIZj!2eKkoh7{!(#y7Isp>Ps5HYoBocxt$FPI zlBE1{s6R593YZ1X`pPP-`q|P3(K%OsASDPf;qLDwAx2uc%{ypW&_J$j^&m^NSyh|d zxwMBdqxXje>;ECls&aL-qHz@q4^jopK5MNsT9d;ajol<3O8LVIY@RGKiQ zUq2>fav9?05mUM+n;y?cwbE7^jiW2b9#DwPlPKd7F?O(F7|DILnW@>PwTc#O<{H z+2=S=4ls4SK>qo#N$brlwNK23ky}r7)j3(Gm}?VmLpV-xTW%`w#>@-@7+m?OkD}1i zTHeV{e(D}WHd5aFDwMt9$NA6rD5@ju1qFb!plO1LIEV5zs=gUt9|=W>4wK^d&@EP_ z!P&XhqRSgKK3n}?vNkT15zevCLd(XVPX*@`EfvT;x!2u=G&?1TaqD&?|DexvT)?5K zrIGb;Jk@^@g@{1GPsgqYWUooj>Jl6`i6Fv=JfcB3QRP`cKtRH5JUEwHn&Lm4N26TD zcJ}QvxJQHK^*R~%=BBop&?}3Gs0WBobP25JW6>M)eUyg7UMB?C_hRi8Xk$Mun;mRE zM|1o7zN!X<^tbS*XO4}OM|9cf0yt1=KXFL|56;GSjw-H z^Yuia1+^ly8XiFrP@6l*atz7@URZ-WM`#N(T8!6v^e}cuLWqM-9ht1Mvd$RWG-j(f z0&>*?#OD0~u<=s%t-C66d-?AyPjV8<`$!y8(X~Md?S_jVGe@50 z0+6I_KgS{HZ&fQB!!N%jPvI%)e(Tl{+;8V7#QAU+!9N6qeq2jYKmNsU!45H~OtdRW zZ3%*!VXQ@oWme>U6mW!X=7veXvnu0KUx6tJRrO{AzAjVhX01M`CVv#q{mA&yF!sN= z#M);K($qMQ4j*~whL+BT2lX%GrAB=cGj#78{hpmUI5cx zL+w%+IsDm}6?JbZIk)^CX8bt-2U9`{Q9FQXd*5f`xJs6w?;^rSXLzOOAdU%M4jdhh zkFUiQmw3EyeUM-^A# zA<|ACwzVbed?0-H;{J9uq$W_u$a=fvC?;CD=YuOn7E66nt*RlBeC}#IlI@X1xKBa` zF}9TR^&oD5r=u0MM^cy5+g)14)^h7ZaOAcKwA}hy>Bfa?IjHrvy!`M2Dvx96O*Yum zIrPTlsl=ovEEj8{MJ){~_Nk~cMoMSgo1aeeh9e{7IxHN&>OO#O#^4>hZv406lCRk% zhhx58u|4muD@uvy&KiaNDV_vTn6Mw8d!uM8e~U>bZAL`_#x4fXTCddRF1DNrxI<_O zbNBQkcujO>3*wlihj_TN=qqeMQ9N0k{55eR@LYZRcdHln7!0FI|xJfS40r`ZyBAe#i& zoBX-q#<8K(sGUA^v)NaBX7WKZR%-qXg5IP$CnArwm#a}1yA4Oc)pgU5x8?Wh20osX zf$-$j<;^BC3p9r*TH6Np_bx_EJcCK_+>E$aM3!BGEhTq|Gr65rdzW>cN}|ct=1bCV z&(k{S4EL)2RpXUGEdF~s`*Ask;}lJ>@;`RKW9MBB%YGLb=Ivs5R~C!2mHLTTa9&oA zY%Wd8vtW?`(I2PhThD^9);sszrZdR{j2o{*2uOiHDpMo@Kl&i3!Kb2G7fWkdT;bt-ZuE%A6UYILKyXxLJ38XM71MUTiJLO?%CL}Cz|&j z`ncjQKYwo=)LC;{=trkHXM9Re3<2dqdHHtVx3;?ESba#Ptv+8BMilo8^*2xbPvN8} zdx*h(tl3h?X#&LHmQ(isrA^uieHzBoFW-mLCViqvn;`I1$?A7+F|q18e-!}XAZGDT zFPPx=dj=`An%dyjANM}_}u^iV;6=x zbltF{BRH=pXpdUg1A>A|~f9whCa%@;jv+r#lV z;JLOKco2z`UVl?fbzE^iNrPsM7&x}5_ia?$g2#U3)p$Ds>Bnp-klTg<+qV_l%vG1I zfmqjHr?{39qrvx~I)<-aH7iXqFC35TQ@Ax*HjPvp%OTZG(R3wBG^JzmV=B3jyozsT z?$m1?KMq9iIGI2a$Kb4t|9#M#aULg;(Wo|J>^N#?`zRJH2UJ($b&5^V7@Np8L1QUb zg0HzB4wg8%)N0v+8O~Z{Buft}Ru<(>fSZMJD{W*!a7ZOi&(&a7NywGr-mT*IkW) z0KrRID6YyRg-K+bFT~AOVpxmm@6dCDN%e@(*S_-G6gXT)0?im6OI6o_=($5A9cC0O zz@JgPhsNpribk{oQT#Ga>JWx_U*h^Wm^aqg#1y%uHb5#ck9pr)n$53O(aQZh8v??^ zo%mIDBtAYXMb-7_sxk=dJ!&=cM4~q@9-CfriEOcIZc&Wx%8ge_lz5m=t!1L z9y3l)L9Az231J7zOkfAW%GYTQ>aRzt6*}QX%Dnv!9c<|Y1vy-Vtn4DYpwkT|)9n8MBBM=X4-Q30>Ke{zBZ4m`(t1)tNW~^tXk8lh0+NdN0d8yQ_S6M9KB80FsP2g}0pBjQ3Guq%vsYRm4>@WIkUASAP@8a}yapOZ8as{!Qr(&%HJEiR#Bu1Ims^Ph6PweHF15xI_S-QBaP zE>uWwlZ5zaG4Ts+z4CkbH&mhvT;1&BecW^ikPX$=W6(w2+sJaxvX)#Ng|hy()#r5W z7DQ!}Hh?D7eyXbQs`gh6NVVyRPaU=A?j;{ z-l6tiiMP|zG@V&3fOS@b1?i|i={pc89#iz|^SJ0B`D1VRpT%&k0P*0uh%N_5aPm4b z+=qfehVR{%vush!ky+6bK%kZ5N<8V>lzRJ>0Z(lFCk=$YvcPWuU0DJ+T2XAoJPs)$ zAj+bQ+1#7$&a_wblFKL!NhahEOL<2cuM&E*v(P9*L8zuWOkld#*T3~iKagB?Tkx_94vSGLy8~!#kp`W z6CnDNyZjdDuahKXb|90H=ZvwN6Al#!%#I?%SK7K5+~sh++GA(*2(=AQz zEWTWHN6lB{{GIy&%y!_ci1F<&omRah-$+*2@i~ngEMHCFYZg0+Emm|$+%IkZ@K(;| zT78o<;#p~Hk>d&-k%8`VNrWi^g^4K+3@44>T1ijwe;C0O8p2oJR2+fSd)Y?dX$ zq2DRTGueqAF6G7*X(O(-juhWY(v_V!cClIw-i+SSlRyx8oiTwHUI9qU!hDcCLJ!x@rRl%UTdGW2v-mFAg zn_;K3l(SyfJFB>ge+ZoM(KJr?mar}f$FS1);uJWowd;k&YO~U8n8?`1s6kz?*+O~T zLiqWeDI>(2Cj5smR3A+6{*srLl=PGv&ST91Bas1FJhjcp@fkW~917{2kH5V!*gqsD zBy#bhv(a+$k>Z9!a+3+-y&@=p4>B8?7*b}ZX?q_0lRHSR0{D}qFxrGde5&rOtCX`q zR9%pizKx?ty!uPmO)zq^f}EAFM}pR5f;9Zy?6CZM5Vd&vzUb2*RmD?+atZCFKGq`|k)PUV8~yE4N5iRy|1gV@53d_I|@ zHglAL7?}w)HA2;IAZGNVhEr`FJ%&sdc2;r7hSR#T0%W>Oy!BcE!Kw-f+GR=){iX8M zCEMz|>TB3zs=kf#h;m<+l>-Kor7b;AE#m){qFhy1)lHwH^KL??^XkiTr)Lx3*V=H^ zH+9EtBMFc|qHtVLk(yy=7L-}|Ud+0zP=@OVmztL^D8wDcBOs_h1u0}oAuvRGYX}?- zc5*w3(>qW&gZR$P6eq;$!!bi0@t6n55)&?zFkNQ}9v!!i)Dm{@dP`GgrOUBOm<9 z>RA6Cu7|~qsM`s}!1EOBP1dh zuIGOUY(aJJXx8ceFzgQtyAaW4TIA#MQ{$9422#L^K_mvsqI_Yxa$E#M}#7TH4~LrRJ^Wagsq}=k@c}ka!@j z%!gPx0&%FI1Ch9GMdqZi&CX_B#k4#^u)qiWTO=IU*4Dy@G%0}OlV?TP?(ZMmJgmzr zmE_P|X})SD_oy(7kaFc#QsiJd54fR{CDR+%P5DR00uqkd6Sq|%uWsgcD?`sG9LW~i zJ~eC}FN&S6L1;S=wdL<17(<918#*hIb0?5`q|x*Vx;LzF7MmgJh{QfBQ8VbIow5)$ zRA3h=4ErrCncEP!B2HBK1}v}$@icc(VywX<-)pCcr=-o*Y}4x;-|ieE z5S}x!IS7WodJh&Q;g$)ymQh5wkrn7DkW=Jq$ez&Ee}MA>Wkoq9oryrnNH66p_VEZl zlG=BFKCT*`M6uBf4&!7$S;Yu&hCc;jQ8pf>W&J7cz|3md=tH22nN+ zS4m0F>XfqKP345aM5aKO_WAYdvprWxwQtJ&LRM83@Tz0&=eYscBy`@~$M9)AcO>pX^8-H&UjZLtXegbrms3f7TR_cq_Vh_Vp68KC$N;p3LtQuvl&bn*Dyfve8M=pc)f6gDJ4Wk67~DWS2ge+hv} zCO6l|^2W9Ie&fbqX7=$_8vVPvV!!Gr^_;cmiHfmSk?56@I!8dV0-EyR-bW9qa8KG7 z*!vD=n^OOIRStzC(9nTrV;KoJ&jb}V@6PO(ug3aI4-GzvRdbba-~|%@tQXL^4CEX(OWz-QPnK~v)zT2~(=81j2ZbfI(z-w-&kPn^e zVSl;ewMzeo;6+`vuvBu|HJFR}4`CPAW5;wA-cLLS*T%@cAN=Ysfu;jxeISx^-+a#wxGrXJ_^cQ*+fxi#Dz~6N}9tje&#rrx89cr05F`l!>iMmO3*Bj=-%3C23GI*^3(;P=YKE(9 zUq1m)%;LkQ+8%8KNSpT9T0Ul`MxpG*-iq3-srpqlx7;(}Sky88!O3KbUw45{9DBgC z*U@nM=$xXKX)5Y(sz&`vcB+{7x4hIHST#muxUcyy&#d66%nwUdh;KsHxWlFRV+s5k z{B-?8)gHu_L1ex^Tt&$0g`+1eW96Nwe&hX&S|1$B;;v9$(JS=U|81XH+{+anv^V8pYgB;vV?M-T@Q<& zIqw$Qk04Sg^F&?gmweEExq02ufTzl8Un-t8tP!9+#1ejfEE`eX10?XmbuG@!1IAkqX32n(3x21gmpRm<5R*H-j~#d>`^f zpR6KBYrg#)a|i(QA-MMvWqRUo1dnZjE$~6qYFJZi>^1#*p7g0g@(hJ==Ex}%g~YJ4 z=t}ji8?#FUFD*{r)^{IL z?o@>+Tz=u!jBwn)^>h-Z(Ke^QZ9f0rJ=K)AfqBMhpLUyv`?`9zuB`R`@6P94`!fG> z>v-g0@P>K?=65ejySvAcmi8N$#(>^R{#2!1!R?xiYPh=?aqv}_!PK&jAJv$|Okc$( zu|dN!`G|M{fa_gd$6Mycc8r$eZ9^E#( zu8F|@6!xC8>X3qHfh)_d=5sV_7P9-mPq+d?ie4+v%WNKPI(tUz3eGYxE}KInO3P^q zjyy}s4Js`h?Zt8+)8jbVq57-r^6zZH&gHIOeeq3|SbawqpwgN7er*bG&--|!P@|<( zP9ZU|H{K+rK8&|1V(`EBXK}HkJjcqT5f1%B-(CAqO6AhWfrV_<4Wsc>hslSss@jl0}x|8V7e zuZms_e5Oq*i4md}@ePFPGQufc7k#$K?Yseb)cJ|a%-g;TQ+AkJ9V&ie7inTMW=C#3 z?Tn*0!Gj%nukU(ooH2X-nI)3Sr}nxm-@i~mPXtLJQdWnE;AM2E=RJ2o(;oXLcdjq; z;kCyq&y$aJ5AARCtYC>bX-8_DmwYF!FVQ18MDqA#TJNherUSDL&o+Wi1{mYJ9~d+u zJW2v$P}7F((Fg^WBjoM^XWrzsuw@Mgpy-~vyT=HAuYO8p-`&UWOFt}Ir-P69*@j`? zP$;oY06Vz~aMy#eIvfK0>(SsBMyXip9oM6T-PxdtuQ0F03ZdhuP~JT8oPq3x2Kz=O z-TL`7qa#FP-e~R~pY=N)f4nau;ijSOZKrOnKsvbY+D8kBC19ObhQV zu(t04pZcp5ZR#KzqKB1crrr|RLp>)-+vzFnA^E^E{b+8lurc>#x1eVu zA(ls9xxf9$i`MqIG{D=Y#J3%CCDY5*PSpF|+FTYIovo5RPH`sDssSe=bN7)%lYfy>ROB)9?}ta9QVq=iHVfzKL@Fa z&g-m4@?~8Zl6;Iwqa$W%b(t~AB)y48y_|QV%7X5iGl0vu#N+O!vx+-cDKhl71`{q5XA85M63FPh#=dTEw-z`r1TF4xI>Cq z!IClmDkxHO?7nuHL|%>?`!XJOC52Vv+K|~c+J2n{%wEk_{a7BwyAnS6Lq3;m!Bxsv zW;L6N*bpJH_~#ev&Y2@#|$oYD%Xii|tU!>jDPc{8tqI~PCt*<`2 z+{80AU3&tX2kLwTimHOMu*##jB2ET=K!fH`3yB8kw%qWzW7X=~qsb_;(VMRQQbo}% zr~%WV@Bud~TzS676KJL|kU&x7je3<)g)Jv(Ox3ObsC~1Y(dXYRn7d5` zZievW&hfsz?cVs#3Fv(_#HP5}g`a;xa0{uQI;L9C3d6fW*&200Lzup;49-0mILa+7 z;u0v5%r{3UG}7dd%;&ZRzZp2rbXK63m~?+N&_VqgA55&g9Y0pQ_RfQ()LEH*hE5a8 zMO5rR9{v}nJuCt(`7a^PTKf5w{tLVf{=e&Ee;IjVMFFALlgrQoU}48RUTv`6mkKuH zE~nItBG}M4e)oqdM9F^qDJ|()`o>ob7jOqkO^5J%HF+x+SOt(6aA!}o!(vxiF7d~V zYdqi21p7x5^h3dQs8P%DI#LkKf^~TqV{XamJh#(PlPRacL4|+3ItXbOrYX|cX#Wgf zrn-j%+q~5xJMrS4p;iZ(|7@2&m6D-%L^`7p^>~TWPV@Z1c0EQUODn=|6P1W zo<2JFj)40v3H}>zasuxhZ<%SAVhVH^SV8R6@S;nUT?q5e;!!bQhHzQty(p&%scD813=-z)oYVipO6=Dax~t&Ia^r7T z<vto=KR8k9lX9FEIFbRn#CE-=G0Z4Cu!^_6vb zjz_M}HNzVMup^{HP25tdc5w3t!5xE*%rt6t)yo-{4kgHJ8f0^Uj#&!NiB0M)bu>!t ziB*D6-g5IbYh@)9gDj!v4+5a$cC^gKaZ z)9)*FVIf#Dygs45j#>#p;nS|)%umeuEjKa$`3&>QQz~pxRfDa|H?O5Lflg0JA_=FaUP(q31`y_t*ey+|>oZyBiF&}u*T$`GZ8*zFGeroR zZV9Uo7++UN|I)xpR>~26yKI`6QI{-?#&k=WL@C7 zyOpp}$-Lg2WkJ58=;}7F7$KJ1^M?p(2-AfJC9>u{2L&$MBR>xWULnC3>}0a*FaHpR z@X>4C(n-G~`MKA3PD79IqusMoywyy7h$&x_HxAb#0#)|mn_!_AA@*UEk4G;PLQ{_zmQ{_}22L5lABZIpk z)dGtTxi^%?8uJX3Z&;dOKng~7Y@mYFoC5z5JNM{Cf?LZn zF!m6dNAlAn?=G2=Zw)x%N%^MTBbv#rAzVp-*^=17+ZGSFdO+P-sBChB=x^H_P2-rV z-r_zVF(NWdiF6lYf|nGfz_C&|tj-Xo4!<&N|GhH$$j>AHdu7zfu4(^sWv2d8EbEEB zJ>LD})EyxC@Hg^MKt#1YxZHjvn_)W#+MqNiku+dYjXY*Hi9S3mLLn=eS&^{lnae@X zHkQQ~7OvF5Irfga6ju??O)qj@pOijx{~D=98{ZGn9mQ{uh`D?-cZZ^in>osc>amoM z21EtV`h1Ht4WOec?JjhPdbLbEKhT*x55FU9o_w5)3beobmaWm-%5HPfLN>zfUq$XM zQwA)`@7pad%OneQ)t~NYNeh1Fj13&87rM;tM*knezA-qm@O!gkCmlN-+nkx$wr$(C zjfpc8PHbahPHfxG#7<`WxBIU}?N;rr{?=99_rCYsbI&>N15?p|0PCf|ef|K#;hrP+ zyyt1dh@0A%pO&8-ixN~(>WxL^Rb^=z-I{-nk={IwKnrJzS8$I^dBvwTa#IA7ZyGb| zZfV58Zyb+h;W_U0prs!?=Ymg&x25m%$e&A!tG`G0`&Od@C9EaSJBIO0hHmhtaTdn} zMnD|KlP&*ah$X!qxWq6zaC=E1IaiwGe?F#eK_)IhaOP89V7p3n%hzzA+x~x_-SRFN zOlt{#6yhG9T?zgHvi@@sYRk(Surb-?#VEUTd>z{k7ox9ef}dc>?&(XeY|nqM0|U#w z6+d%JKKG`#1G5?h7YRUDkrOL_&joNlS~oh&!0a6`Q3dQMFTHti{5lSL#9*P%n_{iK zIDBjMX8p{o_}uvi*fzN>`d|_NnE#NRSjju<#(yAwI}S|A9cDQX{g4Io;;YrS%6go< zw`40s*?h)apVw>?udxqhQX8=fDh{c*6>W+C0S=FL+x(uZ-TAR#3Z?baiZ}RMv>u1| zba|!5D%wx=md9s<34h362G|p})V^qp?lC(S%Yh!eN< z6N{wrKFqGx6VGaB9`N7xedT|kIqT}Z^e}G}1_pjhWZIU7ICimAco43}bMu(eW(wny)CzO{^C5l1KEXe@gH zKStcsE6zt9@0l#0=HKFVEdu`v(1CAHw4MgO+J!)ON`0u71(*Ud=Fn0TWV+5p)9!Q3 zU_;tkY#*q#u^-3-_$*ca2XGRsS7|cN)bR9(bp>fFI_H|~Ph718o+}VOgj${HI~0qTIJPF9-*Hpo*tHO5>XzL-c9!!x zz7E+Pk+N1fgwzjD*tK|n;JPbb8YtDZ?tK9%0)L+x&IN|f-QA#_sr>`If%8Y{kvH>a zYVGHRpf@=1@=88;{{Mk@{@(+y_J0K)%V%Es{|r2~{|dY!uvc!~blKl+-uE;@4%~;n z&tmWmU8?x2X4uAenF|&hhT7*;?~bKB|B$pgY{iq@?S$^mt7RnlAnqcY@XvMpF2}lH za%+N0EMr0F?|IT$ZX*1^`Nfs^MxMQA>58z$poG!WoNhm9`{ip%&I)_LkfAs?p{}yz zm!@Psu(%wEV{{nn6&biF!}!{I3{vvYSTLc$9yveDbQoaP@CTbfUTn647WEXtI>#ef zL5irec0U-spT|@cS*no3HRcqBHTfwo$0-$=oF4qJDQ;!Y)kPV1x1~jd?bCGK1zZ)% zODm#P&RC8{kP@xG^{(@grJyCcIyJOC#{rI<)UkOqMrpXDWkzg!Y z*l>p=Y<=+*A7G(6edSwBbe3l9plNVNl>S;^Z5vZVai3Lv7FmBn=)UeyT%fBmgGKWy zJ=&F_%i6a}t|T;Vr9@eefqwv)x<_wt}0j@U^IVdWl{4#7|XByNT_1%Nrz{ zN5qO0YX;WLxH?`{Bwyv%-~Fz7bE3h}&K)uMmR<>X8&A0?R_$OVPX3?(k7ZNk)$VX` zOQ*9&>y*i66Lp=@zOZxiE%pwDf4%;HkA0^9E%uB5EA~N)1T6nay4(Nnq&xXvN!RIr zB;Efb_WxJXZTYXHJJ!Npi&0EdO)2La*jM_Rk=jXLNTl8tWz0HBDC{}w>b5+68`vD@ zC)Zc`t8w*x^XfiwMpw6veVnr`0(HhUr#o+?E5^tB)Qn9LY-PN+99Q~q)z>0QFr2%U z@N-sl%34C>jd#h8!q|zrdFre+=>90FN}{_6vpTmSe@ogx&yNo-XtQozZUeP>;X$#P zO48~B?$%|UIMG26)3yxBe`zT&!s7MqU0okrA9|Re41^Ur>(v1GZaidH(yw9S%3g^P zeLaWb@>_EJ9df<8I1)DBgIMb5t-`{rF%TR7_?_o+3Z&Z`dh;3YSHU?Jw^{yBrnD}N2qtBQ0haWfAVRbWl$a$3Dsea+U zJGB<4XMt#GdzbKQ7<&pmNwI|zvi)w7kKzZ**HVUHb$&0r&qD=~N*g}q{iFU%^u2^? z+M7G_oMc6ZP7T>o9&Z7wtpU?EzfqS)?98h3Z&NtNM?gd-3SXrcFI=%G-M!5IyR6Y$ zYonSUZ`RlH0jh4i3!==$Qg?0MzwukV45p0d=hX*Po&<`%oMg{2rJ|ZK6%G~*)mN;Y z5Zn;WSy%Mp^Kr1ZWQY0k1GnJD{~$Il2ne*aj0cdSplBsu54&W8NE zLX@y>XAUJ@yxb4Z9x$_*@FG@IYnne$#92?m(sdx%_ob+Cb+ck%Z?!@Tge_sUb28d! z?|i8hE?iOJ`|c)WZbjJQiV+GwT+VQ8yGbOz8p^0&DRr%D*{tO-D=bM&q}nw2gzFY> zwSPtvFnJk4*4TXy$j;2k)U$W2@TT?4w`(@jwu#;Qz&Du?ygib%e`JrXm0$AwphZ&r zG+JyDP_nJp+Ljj?kcVn=?$O_09<}S)3-c@qxva=*TS*obW^_ z`%V^0b2-)$>d3}u!ZWYhy0I`K0`Yr`PvNz?yYG1Qly#|3bFL$@pj+c`GG$wKVr8uf)3GKDC#2yoZKjDpLeJJ6v7XS<+q89!3a?TeXRQ=QVD zP~v7J0l~uzmqk2o_ZX6BRe5?whtEOqhn*w|;OKixGqJka-FF>FkP_iVKatG2O`)9; z|MEKkKWbr@Yi4PlP#XWZH^Jz25jWNTXJwdg>#yLYbFK*%$4}iVTU!tDXL)}v!oCz6 z&-!*!riDZx-#gM6H&rBCoo3^vGb(D=FVK)=>l>9$-&NtGmn^E|=0wlFd{oJ~Fa^si z))d^6$e>@@JX?(gzSz_6)5%4Ukyb8Klqt5rNzKnW!3uVG5&11iOXo}i-;3U?{nN0oz$Yf0lcHnRvib!ic*JYc6%R*un z+xH6Zqw=gfMT$I%$&F*e6wgagDl#E%pDnFXNPUCM41vLtZgYUborkA`vmQ)!&{|?;eE>6!Kw(PTF+EOyt7-xvJ4H56qSv7wVW;iHxy7j3!= z01xRF+yT9mk^0xyQJpYVvJYm6Rj;(G+Cii6FZapMJ{Y?{?HibAIc~?y5WmnOv3)%($8@Q-wcVxnVPVg=yQfhri}okgri(>bVrVsZS+o?Xj|CwX%9`6fBQRO<0NTQr{d z%ZCy@fh~G{tzUbIKY+9^%fh@9AWpC`vc4WeVrWCdvR~Yh0G@4ur2M{^BqyAYTgz;v zrURAXPLO=vUbP#ke)j6vsk|O)r*$K9Dhty@+ zW}v|Oph|eT0+;*_9_qB(xHs`9elHSa0pT4E1&ne=3yH(R)jT>#5dWnxe%(MkZ zhQ5RW@GD%BN;-Ax3riyX{0w;}YtmPbVqa|vcLOIoKnaiZd5Q8DbkFaAjR5}+@G+s` z(ji8P#H&7%`P?4=^bAo*{Z_jb+bcd;$plHW8!~F_68(%5ZUz_QDuvsD>dA^@;g2P& zNVWJ^10-$EziZ~{GNvA5-SAMzrOOX8@SZMQ_pijvDXmQ2qU+$fbI4q?sFl1Up zmb7(#hI$DPA$S`EVSsf1>cqzVx*zb z;MzI<*M3yP#}>2|ON9M4BY+tD!i{=-=Rel%&EJ!goFg^ASNVmt3}sQOe#7rN@koDh z@#(BaeQ|7gqWkTgRtJ@~XUDE86j-MTpy_`5dj#PQTr@|RLLn#})2oRn8OQc)cY59d z3Blm~?%{z0R=7uptL7qB*|vS!ZtpNw-y&7M!z0m;7q-T*w8Hp5(y#Y+n1QJ>u*Khe zJyd__4;gw8qsi++1!D> z#E$)rsn^gTF@hI&TjHcqK(@ukDPh$O#Y4_nck zg!a?ydte&a+z^cUPD@Ky186HdoHH<1tY3t5EShV}Ad3gXBI)C#5;`?tQE1p|IQ?{K zU~JKGpz#21CFEM8ahP*-Nay-#vP>@7iWTft_`=^v?l31F?R)>-S>Uqy9z+=8JZ}C| z58J2;Cuaoi-?)j*8q8xgp#F=<2?oQuoR>q!goqcgS8J!sidr+%L_2 zv7TSc6QH7Y)Z(J2`O(b3CODR}#brjEaB34x`H&xbRt>=+z{81Ka&<38@fH=u=M_RG zCLY~`CN|ILndfr$=D|S6DEr^Cem%3A8&}dle|;_;m3nmc#x#sIfEO}UBqW!w)u~+r zO1Mp6K*Yccqej)KuY8OEIUw=Cn)lDuzDpIi7ooxV|UV6~MpBJ0GGi>d!L>s`Yz zXw@e1&L`)ks7qXOO~nKAx#*han6JaT50tU(%?;%w8Doic#hcy!5-&N`5W;ucJB%uCf`RzY0RTV%(8<_EiX6vwG-e3h;(yPJKb{e4nw_K_IWbI#>qHIVUG^ z5(d)&0R>n#1FEm4b`(jL?*#xv7-;U)bHsLSz-mz z{{H|ZbI0}2n-5f=g)(P7wc3AxV3@M8YavEsVR%;c2bRc1HJ|OJHQ|Zk?O&loGWS}t zyNlMsz~4_wqTtK5&<+}L~7KAe95quMI=!)x=K@*XMiG8@KMx^T8Mq5>ev zjSTP}B%#Xi%0_m7*2lsYOu}%=o`C!le5g zflcwKU2?UM-=Sw5Us+A6Nu;Mm$?At^uIj*FgEM8p7a|skV zZ1Xbi9njXYl7h{|(ZrGT!#=C6*8W&X8CFCG)c=Hp>eJrUEaxZK@x*<^5L9^Sk&mmD zI4a&?Gd*cDMT3M?C2%zqg=EjBrYk7Aw+?Q-lA$?!8)`kTOdT}9ps-M*PQJ2*T!~*Z z@3FGjmCg3HA)_aO)`4B464JTm35_AVqPDc2c^7**{M62vwawlumYmgFP}~SQmm#IM z>hT?v%{1u4*&E?d4M^R$>TN5lE&TkIxV+GI%i;V5F{7`x7#+%LV7xfdG;|4*5`H-z zG|oM8MnqH(VKd>)^z^HKE#s7=xs`ylWR^v(Ocar9G-kQUfcPc!3QtXnG^Slk$T{73 zbTy{w#k&1L_iMRfr-O1so)h2j?=%MuUNPm^K#5{aSn>~^$DI2O_)a20k(XOJwqrff z?@J=S40M?tOp(i9i&_mQXIX~X)9w>>QDK?1&x|CfYI05W;@&a$EMO^MGQ%5366k{( zqI30}PW(pgQI2OY+R6=n?NUi*?|zJGs3$#1HJ=Ob#65Myo%f$F2Kd84?tLV^o}O%z z;TWeuYX>S*LgASdViNd7l|`-z778Ytp4MugiorSO!)=#`km8hP+*2gykfTj7>l08b z$yltGP}ks0JCOlZCPh;>j}ZfGBr5kat?ouVGqo`V(_?grvZQ=cB|Y`?Dcq`o|1IsTVmBJJarGk^WBfqtLVF?V zFjm?A6%DuVh~?^GDpB|hl<(xk1#OCqD1uJtYVXU~W64@d=e@%L5ywiT6X%&9&`vDK#Fh^ygRyuD`ntJ%!{DvOXY(=(U zv>mqj5bMctJ<%Ny0Mk(nSN8AJZklF6=a%6jS&Q;EK8XoB*kL z0(0ASKTx7-$msDw@cM_caN2f{6`>|y7@j0TDxMQM^v^D7OVR3%@=oM#>o$iSyi5)x zt=D!T7*FnM*cPlqJzwHbSgNC~Fz2t-!@=45&*)yDVVVEZprWa_NpfH32Fh8nVj4WqvW3*`BWzQaNsUvv7F+ zZC>ruuZdi)(q2QaiB?t?hXVWuSP7yq@u?ONA?k!=XMa>A2^`LRqV@kF1h+~Q1(YWT zi2HYry?#j>UyO4>=3<^f0tH&q|bG_fFA9B}=_w3|pL~V)Cnv_U zd5VeLLV)99SdcjwUeD;GJ<#F86)<3kdBr{{*HbG?K5C;#{cZyV7SIk~K)zn{J+I8& zMH@iwoB?gT>qlpb1_k3`L=)55snr&%6pMvEBrYnxPdt)Qod>f$^dZrbm(Ys${6gPX zex=mH1+630O5K$r<=h*VynGc163<-V&lF67j#epl27i>kiXke=%1Rwkd(^8(LY$PV z6btPY``E@LVh;)Jr+j+}DK-sAGOptwgdc9QlDpPeLYu$_5imRk31CaG$`>%E!w-Al z#g!Y%Q((?~5I#8GceEJfD}mMrJFq03V;$$SO*+b6`8R@7U1C{yij;= zowaIM1WYP8SxM9e9uE?uA-QuG1}8SrC9yFCBD2#l!xEPQrIS%dx9cH+wCG}6yYLmZ zjGFhM@x5)&v8Vp9U&>ym*WgZZPkUz3onIgrKQdWK9iUB$WpM7227dPbJaSe!8iQs&P+r?Pzzj$YB%lx=A*;_YR6(CcWy4)mDT415 z5J*p4WjJ{NiGP^R`X%qLvjJbw<+`b8Lue4w7($_gH9(bh9r5K7Zb>2ZYNn`&L~7(e zseWh{m^-UhJIU-7%LK!dO*BaKhOh4!t}nofw8FTInquIddp;fEi(!XjDy7lG;liX8 zOQxWNvQi6V%S89fe2AW5SXnjz8TyB#Ah?+!@GLlG1>$>yc?Kdo141ERmW%K68n$OB z?*Gmv{*qKqLxcY9VVsIFXbDY~F$d{MRZif*J4uYO;Y4068#C5TfJzV3yMsK6_`qH) zw_(tRD&^e4jwhn9I^&IBx@%dOAHZ#acL=3nvfu7Xpm=VK*V8w4FnvuW6LiZ~(%tkPi?ubrQ9^4+E7e+SV0!Z^FpbT7AeM$fb2h6a%t<^h0}B;Q${^;R z+3<7KD^s0E7@h~vs8j!tD-H130IT#bBR<2S)R8%WGaDt$8SO-Ce1Xzr6ErU{;(t|^UtkM&! zd!}OXK}GAVa^YYxRva`XbVTcH##jiDx}Yo*(c#?@+i7O`Wg@XZiDGPETc@&vGtYUcT)dIX(>rv~ zBFp^gq4Q7iZim^(ojy|q(EJg^mr7)d%hWyDDMz-b#0(1!h{de3!f~$nGe0Cxdz!aP zKAUA;S$;X7`bZlF$1cEG^Tv5-vPqO2Eg9WVSP-F#2JHl4)WG+7H4-?*m8%n^ zLAJL!VY&b|x{0Yi+Rca8g}H!)^KTqP`jJqFMxFJ48z%b9oi9{#CJySZHL%(PVUS9aYdBG9m{HEk!Cbw)q){4NJOm^NCdd^qF;6CtnWHM2 zYwR8+c*h}T8{@Q%jL3+dcVeWlUn_TmF`8eV zrLmbu6ZT#Uz7m;hWcP>@O9AfwV%5tJ0qnWx_IW@B2(BllbZLQtla`Y+vA8ps>Xz0p zDcxWeiw0wF9##>pNJFC-uU`_B4OW~hW1`q}^jK0z+vOc+UTyTIigdW>0lIDHcDk>~ z2!1`nyM2bO$(Cn&J;JA7DgI3GIar1IrlCj}1jWb)gcCMFIsBF-hVCf*X21 zYeJ5_4)z$f`Kjf3jw=TT{VW~Do$Q>{1|^6qR>Hu*74ng|Bne8XA2cUySlst=Zl2X2 zC1Td@y{uSeKaVIHtB&4}G^6+yygOU$QmfG+>=@EwYG+a1L0WoB`-W}Zl^G}go~Pa4 zGET=K6J)cYJ?{y(AQ&-?N(Z&u3I-b&49%LJlIzR1;2GG5@XpA!ml{xi(~*U&@go>UfJ9`AYk3dh=3u~w zy6hgiGsXfugnE8RBQd`DMJZ46(UU66SMCRM{%A@4aLwCzi6M23#Wc+5D>z(gi7zy- z!*T)%cbd{yyYZis6G$cft0^j&O z_>+dqOH@QbN0y38=btmq#SQg0%j^Q&q!Mo&A2r;TO_<=?nFQvbAf|{+BxTe$ejWJ- zr2>znD$9t{wh;mcFSqBQ)bwu1H$0{DBG@-BE*2uN0@#+yo*G7e;+KbxAgVU11Jjl1 zHIMK8xFuF`@+Cl49$@n;luG1k)FvyRkfT*ifm_hhW{(_vZ~4{8%qv%~_(M-qZu5su zWV!3T0qstg;tDaqhB_C~Ss4V+DxB7%{fbcS{MnCOvCx?9tJodAfuV#3 zft`-KCvE7@FJv%+UI`-iqmZge53vkL6?H#?|Km4JANSSA{;ifNf^#Yxy+A&rN+(iA zn9@(!+-R%Rb?lZJLz5T_CUa-(k$K>2QE>!Q3{zUV{s*g!1V$@J-AMvw=wtLl6}I@h zBgIKCdU2YbW|XPYvy=HY77gwig%mz1=B;oa77HB!Js~WZJ?HI#Ny(v*Z53I(t?}1k zwqU0jw3pG_VC%C{>$8ck%J8VSRZ4AnAbO>qQ;a7gUCnVSe?6_OGY(*4-LBY89Jfiz zP%oxvusIa+0q-d!+?J4>Ymo#jbth4+8U`_coKDh}7#J90elnMz`_bD@^&!VaB4jrb zf-x%I7ld;C9LJeb=ya&vx_mo|XpwPvViLgMIyE$zi^XV26Tuj~#_G%H!LRsx!xTZ8 z;snLVO(xNtFtg1!a2|G;j|*ywN-6fMjjDhJL&j;chq}1dV7m;jar{8P0VFR^&o82M zmXdY^CuSDOD@uRO2ekY%HRDWWo~bq@5hEvWxdx|Dn21c(pY+l6Q@Oy;9x{09JonvS^|!f_l?(g;k2p=V(ji{0dNBv-7m zaVSODLS*@%83i}#jKt|yWnJMCc5ur~r5f%dDwV{m^jHPb#SkDC_{Mfr(}JkQhp24kr&Cyo(837f^sEGymt-TtUJegOry zZA6I~_IVQN&Gh>54U<>d0RR@rzOdKp#ae30Kw!En)J)5gwbyx0*ngvS7&YJnhIKG9 zuh?TWS9XsEKQ@Ftrb^B0;iJNg1W~}mm<1Wt8={W*^C!D)QyP;y*F-J!n9X_PIei;p zdD%hA7wU}0S>STh5}p#a?tf=}?*Jpye3aPd_h_L7SWPKLC(HnRqE~oXWZ_yNBn_r* zAMCSRBboaay=zPC*P?+rbL-9qdn8F|_@PHEGw_|ZPaMkkhNoKlK?ge)&;2+?DQ&et z3e6@W-j9`JY2wZRYJl+!X47`(dgL9G1!B>aihXH6ht78(wCP2OF?NoX=%jZ4OTNIc z)*?$8TTuEX;$sV0gx{pZ407wyu7#mPF>RV#5_qfI_^Jh@sno%lFH zl0b~kG?tqMs^*<0i4rjsn)_b7v~$IY_Fq?5&*8sn>6PXf!r1oxm55dI4)S;zGBP8A*Vl_;&&5o z(&|fU5CVE6c;PTS_E}20i-lT!!2BIDX(GZzbzIwRtqB&`|1;|8TZ2^?_5iIHxddx~ zMM=`I%Kk;D)3#%TWlWd*23Y4xyi4x;IPpdWZIGHGB!nS{@j6QgA;(;uqSIL*|Gh8z zdX6kHFiH<$QIvRFy6J@d9f*5QygU+(SzNhbu$VrYiLG_e0AdtMzoK2uw8cCsX@@-_ zm1~spxM7YwELWG^Wkm%KVeq^giLgaoRVPolfl(BgdN*#pJ$(SiX$S3JXoHR4H^^EF zDcshjZYdYOp-ugi;h1y-L5w|mee2b!hVCK~Ae>FWxNcESeu)lq#+?m_Kf}725q&U*pug4wgW*tewHIHALipZ3X zhEAh%2eW})HLO;JccvXG#!B?;%l1QCr(8Bcdr2l7nCHhuzh_(c*>9s^AxWbq!X)wz zVx#KbIa-tFVIgA*unvw>;=&yz2yd0E4%M7TAcZML9IJ$e`1(oHE<J7B=Y# zTXztwDOSiE2Z>dQ@;*i`Y&Uir_m z#X=2V?6$~Obj(Rqip|mw4dz1;B3FgsAAqhS;0sFXHq>Dl;+fFN+keCj;3M$bSyA%E z;B=q|ZW!}2tjBJgE4moz^Q|<{tlJqTk%@)*%hwfCX#=FYd1c-x z)6TXE8;0~eL1FZ9Ug#5$+sV-<$Kx*R$^jwQu|zuBNk)u~cO-VUR`UxwFwV*~g|AVc z%K?!O#%V)76(TTg*D}gcP;5J*Aw(!nBET@xz8Gj^d}Ye6A7d&^z?h7wzxZcsS#<**0GA*PRP4no;JioLypt zIalgaEp>*Q*D{JL9nW3$MFe7`*O(N!+A#<<>|Ao%SIR8{8!R{%VIYSc)IZ^?n3hq$ zsYG(j)t!p_nPk)4N*$6Z6$|#;pByg zPyuhB9QyDmZ{0?1+1SzWR7^Z?ff9U1I>K|3L(IRB2K-N``3$|L1k=4d2SvmEmtfk8 z!^o;LXTud{V@%35m97~*+YoUsPjX4YHXCgOVaNxLoM78>^Ry=k1$SDc+TavO?PJGb z6iM*?^AbnhZ%}!Nl+$+g2|p@8iSNYNmu8r-2oqd}KP;oQ-!8b14p~=)4Z&ugR+974 zk{eWzVpSeew$qJ0TQHP;lLm=@CPmVxCrxv~I+*F9?xuYl-68hwL{gHW!iy${GVg_F zNPgy7#m6uzU}Ra5wWDQ@YdxCB)V8PK;wn?9BvQj@gVkD`Gw$HYa`BSw;u{LfJ$6r5_enl-PlG7YU4;QA)H0g8Q5-MDtoq0^ zk#gc$BHl!laIOw1AqiwF-P%61e8$YnNYn}#YIf#s{=3Xb9pjq_HOcNYqu6PldYFz9 zKQs1ru9ACMRvs{+iSdn~)a7bWgsmM>qvN@7l1ab}=O!h}( zDou*f?sMn-fw(EkyPmNcuOT*qt;qo?U7DvoLct6LM@q0^n5>;iJCV>|$lyg&&;SJu zI$Ki+&ep-s@aWLL(sTF7tA1i|4KJ&FKCCk`bE4!bh3}@`$}KI<<15pN9J&)rmZ#MY zNfbj6(&|y{4VTP{I?oVQJn@P30Jx|#_BQIHi?A1RsBEZW<}6G4zSa zfwjkFJ?e3Eq4_T0fi=US3|=BgNI03QMkYL$B8qH%r46{cjL&jD6ig2sA&dfhR1Ty> ztu^!a)N+}_hKW#gZft*I+;S+kt6BoRkuu=5-adAsC@kSF{({C$zd5uGeMXwy&>Z=i zm2fIc(=u(?dTA8|TO5jMRN}l34iH?&>7bKIYg6;V?;mi8VTdWV6Y6n^q0TDS!!|tj z!OmFX+OSt2+JOmxj2{;uucpDZWX3$^0(@Ty1bO8|NE5lKM;exbofDb2*_A%p5ZT!y zBe*U57M7YNviz7#cZuXCelji`8-d72Hw9Fpo+5!Le}V`jt4{Q>li=G2U>F&^lW1si z7vKmdh=jY=2^@55WW@YEKCtF-lnkE3xUnIlo1>X<^!jkoSQqRP9jmB; zY*Alq{(>g7s1Uw-JVllB{`8eEmM>8=wNv@=mDeo)z4oh{M0;V|_s@L=!o5{#_{CV>=zfpKSrsUr} z=*|=r4bu{|8@hR!G@4HfbyI?npzt2(^&STtJ~pZ$bJmu8IQj&xS(pusBKE{<_+0mU zdTgdd)i~}mHzL(TEFVZ4R(in}Z4@$MSHl8+N`Hc!gYS-FJ#`SHeW>u@9R8JT6kdc< zY(zOVPO$qSMschHZOt~6$(4^M4IGAOjGr*6T$Y5aj1dO4N z^fpqQ1=1OCVtXAslKxqX|Mh&#{ z>#Ll*Su6^FKNSkzz?ZP5qRezN_{6c1e#dl>amV;1pfY%~(klf*JyIKGXH`;nr^opl zY(@cKj)Dxn?8kz~n;;*FhcpEfq1N1pUMx=PO_ihj!wl#R%N#gnqBMfJUrg!hhAM!{ z;G8=+)IvQYj=nLzN6{lIR|3ayR=cKH=(yl7kIoW0!FJVwT zMYL_}OJk4*0SXhNDW$s-i<4t2RZ{Sj#9l1OLY+RG*{t*uf3ZwtbxX=BQ709AE(*EP zAyqv`)`$z+x%;Y!ir&PSe!>}B{KHw&`763~E$GPe!7*XYvMuyM9jU!RSmHqP=5NuE zL&$HUsNgvndmKem?T>jRZ1f48XMyf*6*nkHQOGzRu4U3&+Hzn$W@&_MrYyzw#Qvx7 zx1KedEVM2dy2mKTpp?J-_gY`xQ)0h!muDllE#lEuq11K=yFlsfdY{Qb~;CEtNrO|e4=|bR*7pfMhj$+)Yw5vkH*l>i3 zRmP+XK^p(XUGSDeXI2V@h@1m80~chY8BR3e${xDwka1A3e5T+dR%R)`GbMM|3iH?lA;MkKkWwHVh7lo z=I4wy9MT7+{3Dgq^k1=1_`zpN7J^mW&i_*Dv;m$@`Dk})dW|k1GUT*?w=g<2p znHHWzp9<}pn2~Ua-r<`BWC<$sUJzCh9A>5{p-N+>lkq&Ko zwe=l%-~AyzBHBi2ySp?lB$J5R=&%#jSPMoQJ5c#es;f#j_>R^Fh+Z7j&!A@L#VA&Z zjz{c{7X1|=o%txVXFXQAjoRi0_m}T9a3Br>su_^!Ths(XCjn&k**zATuu(}cYJ_NJ zuu4#lBhfPBqg{xJVej^5=`oJO5K!-kqlh0Jj@|Qq{F3*=qlgelyD=y_)y(%eqXd;>m-gt6L(LOQ@1D0G6HEQcpVTlH<`o|!OkIQi zn7$wIEhWZ{0{c3y2YK*9-Y^SYva`s|4XgXJ17o3~alFTVy6Hib z=1%XG+E7>?InOSgv7M%@ih}MHcwl4rjh+MI$OI+OE8+(qCQR=MjXLob%|C!I5snvi z>86RdDQ=wj7wB22)f@C*uTX z!*0fr$3DxfxXx=bISiumg4I39AU^)GMhCT|i~&({y_%`z`Ws>$0qKM{{ycO9Fb#4{ zO+^^nLYs;yuJK2os#B1r?=;`lncx$7}Tc%;**YP7p9wg?K zuh=Y!ABXjA1ma6?9t=E@R?Vh;AZ0u={m`Fi9SBLc1>J=(npfWf%qu}V?`-LRqM0F- zW>e9#f9kRaCFJjX{3$Z$aHHgM=42g2;ejaly^v2O^FO=}Z^Dp{M^^Rqdvt*@-?6zyR zI*S@owCT4DDNx7{5{rok)xjPQmdiKx;YMa3Pz>D}HrjKFPyhNQdWx1Wmn#BU2btvY zTERj?(TAY^pGqr*hzOZ_5#X1sQD=Y>V3Gt?w602FvN+J36GVovK%|QidtrBnDCjs3 zo7A_X92^GuY9=LSjlb+}HQ%nm%Nzq2%fn2XCvwUgt_*CUB>5AX@)6yP%vX2nHVYMW zhDDKiAdAP^IW_>E+zul=g(yGy)v2RsXc?X2n+RLniglCP>mV3@m8i0Q%N!o0dLcZj z(Gr8`b{|l_mmD1=U6F@Oap*q&Q}!0s!Jw;S%97~ZM!g*ETaq6nD~soD!h_&Rc1a7- zeN)o7suKR2FMfhT&JQnrS}A<<_qRA_lD%(3B_vjeMXnTaxXaRXZ)1>&|VTzs#QK{zeK*Jx3XMx zYNN&4Q>>;KAW9tei76CcvK7&qVq6SNMJh3h1b`6C-iYzjX{6MyN3TO-RKZ;DA(l!b zkQo#H1ouAxG+-tzX+zg!5(#HQ7QS~FYYw6fWsm`i&Ww{MUx;w4a+YA~=U|NK-x24r z=&~1bYnS1p(h#Oz&Gk93W;q%3aV{BZj>Q`b&ud z47u{bqixX50;RsC8UBPjuGAWFdC9m|@G#3A>PR-so3QV)X4ysUPAs^~Ma7c@4WplV zsvR=tfbp=#z*KhWHOWjr^!GqEtfPGX9X`3&SZmog;VYU{c&sv6acdgRa{y9m5HB!b9OkwTW4`9Z$ z8Cma3l-)8M8&JXFk99wT4Mme9Hy7PpST+8=xYUq2+gik_(hdPJ1O z0jpmEt+c=0#^iIc9qFUd196(rCXAxg9;+&Wx)%BQky~&NOzH?Wb|!D?sn_Dcf9cDO z+G-ACh|DW0Z{{e321O&-VM%8^0<=wxOcF%&-`LuE^T&H~IK?15h9q&HA|JbpPgx+0 zxD%rmlMnHY9D|k3O_cKKm88)+wr?YyG9gICxrlcX-l zJW&Lx_14aQWFS-$VvcD8d@7Wr$E=wc2}@WqbWx~1&De?P@!mT6cdAXN=b+Ii*SC|id9f?gB?-0`Qx6CtI5YX!9p z^iB>bWk;4$Y6`jt;C6!P2;;Qr@nSL;AyAD$cVWG~{y_t=*udImD z$@Ga~$;fWs{~G|eKuEt^jGksNfG{b~P!&mrH53nGHg0)ZBkK}t#Hr)Lhdq3po|VZC6W5D+V9Gx3;zJdX)w$1fc{ev{{Z|b##>@g z5g!Q8{#_Nu;bY(674l*khaiVxVPiyoF+7jT3z7I4(OjV)rXM0c_Y?pHJ@W|4{n!1+ z@P$ZMCnQ4!hwTS+ekq4>(q3h63;@KbsbYwIVgde>9)HGm!ScWwF-Y?mj=8Z|q7b*t zA89g-5Rm=UGNHqe=7|J}?7r@lSB}3?w zM7cz!JWa6o>e1^tMWRrCM}UC?&>S9v&_WT$eb7E5NOOEja8jq^F*d|WdQ;()v>$df zEBoA0)s`kiWxNTa`@&pi89hEB;viE6H|uB<{qHXn5fj^xdp;OCFl87@0&V!z2PTH} zM^)WS&d*KQ zcL1w9Q~RL?l}NMma-FNUAey?11EIMmJj8Q>YBP`{+xO?Ht#{NZ?wRox#V zP6L6@1`vS&Eh~_Q(pE?%g;7?YwkMhVkj@`uK#MDkLMV>5Vp;P7C+RQo5-63s5UtqU z8%dJxKZ*=nP)5*q2jBEcwc;^5US9%ijA(9Oe8)5WC617zAO8RZTdddGC3gj(17i`= zSkyN&zLQUuGgytc6!pnf=wOS&%ehRWij;AJJStSFseiHP@Iep~hBB4Saxg|G(I%3n zNFphlnEHo`^^0QCjlvuX%PfQ;>LCw72%ukW{voy-FKE{Ar~HUc5`Gv1)U3eT!{0D( zWacF*WkM;3Qp|&%J3LB1c-ktk!9g@1`w3x+ZxwO(7sco4AylSP7wxid?5nvg%v{SX739Ho{-rE`QTC zI~tkt`F+^i98TwUe1EyZDV|VRvK9VnVfW$DByIac{Y}k3yZ-=k`S9VB2fhCQ+GXH> z%oMc!dibf0dx>N};YS&@ugo|ck$Hp=0uV%O%5y#2pD~rdyN%SQQdOw=fsF5z1^t}( zG9anu88H-vRuZ2OwT|J6xxMSdl3-cqA}##&EfshpD1C%xy8Wm99D>ybJWmME0M6Qi z?Uor~-w|jn#}eZ|Xu^(f=^NQZ&xF5%5`ug|bU|(qIIPNLd1VI`Do{j5EoDl9!v_^B z_BZJf0K|+ZsEYal9(XM-Q$z}Vatr7K6~Z2ak`VM95NE$1@Im~EqXi9M;U6WH;xX(~ zDU7j^g%+^wWMXPx<7n*lq|**k8uv``v(EKr+GAQueo1}v!S;mG#Ig+7>7VC$cmgT8 z#;CWrn@!o%5F-H(NtjAGXwpGtfk@&KRb+z?69!Bj68avcdmdRpF(sn_oLIzFWy}z{ zu72;I1R#sh+4&}EmEFvNsB-(U&2`WJ0K8ds$5G)j`=;Nd!+y)`hOuwH?fbiu{S0rB zFkrv-s4ZzFrk+-(#asTZf$_eOoC=SWo{_DEL76@xOl9@9NlO?i8cGqiBZN$rV(7qyvz zL4f}NsEgrpw0`gy4>b62%%%?^Jb%J&9A{|6ltkc`=|ibgBq-c^nc^p8#5_vz6w2Xg z?}`zI-b$6tN+u#D{RJ!aV*O%*F|=VTnw11i`iN>SWlRrjJ^gCnp0?ttOY4&Y%BvGOv)lG*hr zWt-r?e*+}D*#75_@_b>AMu(JzXrxfG4UjK#`9giqaupc-glZJUS1@dbdg6$dH4@c` zc1s&l+Yw6MEgf&Qo0#K2{Y$lbSimhvaWpK;@=GUPMddHs&iJlspXV9u5ijh}ef=VW z?~+Y>Pb&WK&OAgH;yp@p+2t5dbCxKC8Y1=InHXG2n(wk@zx}(yj8yhBHsQA2d5;lm zxD!P4Gip;zt4@jI!3LF=GO1t82QeU;iAPon({W{X@JeNf>V`sqnRorr2{c63W}*g< zFppC(MhLddU!;iAjA{j<;Cs&p*d{t4bw(Wv_{8l5zbKLOu{}yrtzCSOzESY-&?C2T z9*kgki`$;m!*YjF$5Hl1^77s#qDay{5#Y48W@prv3~}Oqw%LhZi{wNRh+|%Bg2*E( zy#&CGpvt-;W3Q}BmH3AiWtTKU5cQt2920VT(7(iPn1_qUo1|nWUwdwtCt0o*)paNr zZ+w``(AGo*s=$i;%8V1x6YipoVi2}vK>6gCrCqQsmc=4LN}7Hb24KJ9-apatj0K2V z{{S(SnqsiuBnT1=rSGv!r`CQvlHuf86RYyUS!(erJt9lb2_3U3z>}%zN+V7r&@=>E zH`6VIeW$=}ItbZ)i(%krcLOO)WUL1k0-Jj|JO$y|3nf^w1qjr3&0C4B16K+!M|vyb)28)fh%JV$2x3&GuZn8xgw>~nuD2QS zAFx!kmP+(r7hEDSi*a0*!z+;}g$M!r!$tmt;9Tf6$qzxn^iEc1vB3N_#vzm|Wv-$t zsnG?eR{-Q`ff|Qb1bv@DVUO=0c*Rwxh6^EyQME*MNZOqhkx1S#1g2K2g?}lD9sdA} zFCXOi#$tHEgAhJReqd9pzxhE{qR6H>R&y0>IFCk9BQ}dN=4d{ZV~A>BiTQK!xE=V8rhvYJ%8NIsZVSu02x(a`1>>L#Akh>^EZPJ z!~yJ*_hz7Hg>KQmw+WZ+5i7Th>=t=41ZD;Pe$JOFzX+M|{>VvAUcdX7OauplMN_g& z5fRwSyW799SZ)Gneb41GE({bPxo2^V8-f5ClnS0qEee3Z^TlRg=hQxBUE;pq_JdIb z9*Tgz0h(XAJ|G)mqdr;O4gAdBAOrCQPcl>7VneXE4^o3O`OuI<6L&8gQUa580|(OffilAqc}5PHGgDc{tGxw+fF&p?5!| z791Y4>JjFA9RC0?)Y2;>_L&x-Vl@SbZDjI6rSs38UYHw$)QS$KJd0UdFkWa1SLb4LqQi= zjM)3X7uX!(KF9X3fLqX?DaMjN>LWI$r90dI z0HAtf%Oj_qn;rzoGCK7Ubi_tyB?O9Hr_0&{OAwu~vn^c`_cj0&D*7eINNrGaqJH_B zK4L^YTMY9&{SWAMt1o0@niwjM>36lq*Z5n1h$(OqR8JGKW!~CBfSn3*nX#W7%E@UBan3gf6mIy6{ zld?Hs7N0N?l|n|M2Q~l@ayi}q0C)jViD)CBCBstd7UQ z7N_~|59V;#)c%Z8H(Hc^wU3rz)smG^4%aIC6$XH1glnC@EW;Z<`+*ct`1VKVP#~8B zf1Be!$;oebW|y@oxxIy?@J9@5YjL#~%jl#2DgGIQ4B^b06ZQKJ$%fVS+N+rRMBx4zpD*X-mhM2ed>Xk2w=B3Bf z93E1JF;zD+78JpR`#{+Kz4QHdFt=zz)Kwe9$N9Ic1fsOznsjESVwgGX z%G&BT!`=->G~UIQ8^X#9a;qqsUp7zhaf5gJ?X%__Up+%`Z5_OQfIcmpfI+72vWkJB1rzeD*t6vg#I>ozBr{8%Hbsse z{?V9asI1?Qn0f*4cYnmOf`~-2Iu^~e8s{>^5_(d2N81n z>0Cj9ubaQzm{^8}0~*7!Z#oG62p~s;V1J2`L4Y7M0$K2McqK`Xjh%c$kG<_0+w5(a zHS_fhuXW2SB;EV_Mj$Q%FlJkVBISo|&o*8a1aJk6)_as86@A_bT|%H0gkUb9-PcmA zP#ij!^ra_N@exZ}w!mWT+zQbSELMt(JIFdCYdD8ZjhNj;C~N-nm_ujY7P8}i5{`7Q zMpaL6azPMOMxHFJKXp_FtI?)7Wz!ZF)CXW?kY$gDnM5+sl(NcR7^d;p0U3Cvn3AKz z%lkpq)Pp({{R!+!zzJ@grXbZ;aJsdtN1_WJ0&;B zeqiJn6a*c_0Ircu0Oj&AR$vA%@njFf{%FDgi^?>H$`GtMVk`u07-gSgHj9{`)C&E| z1Me`yEkcyAeShms#V(s$WL_o8y#x7Uv7+c0nv>|iE6;YxqU=ueFS^9GE$-BYc7N6s z!NyYm08f$9O{l76^XHbD;;|~ihQYsfB?`vPzp>-X3vu9Wsaj?(Z1~g}d>~t<`VQ6> zD6;OXeX?6I8nS7dud0>3VUV6;f`+6p#|$kWtL2>-m*?ZW|#5SSA^Ua}tZIi7{I93AO^kn`Pp_FM>|t@z_~+ ze4l6~=tI!m;$X_G$IPuWN=X4l2n|3CGaDh99B-L|y61!XC-jx|Di6{)qE{peE-)9U zY<(5_v*@0qgE#znY*et> zDBlcUSxQqgEK7=Dm{tW=uMunqN|`7H%36Klyq(JcQO?r<^RveX**NVRN@T^wAC-ex zg^DO}LLJ#10=pd&sy;8kk&E?N^>KEFU_&=6eIM!(av$W2^Ua*Aud)9C0yMS)KFa!E!G(*OQdyB5ClQk4y`f(Z#&!xJNEpf`ARI1E0>t zk($z%6*@|40hLw(IbBiqqvXJyOm~4RY6iJD#8T=pQSB};J>6KiqL`=MLYe>|la|*lbI9L_)SBB=61T7_??mHqxz% zI=(!`c79neVE!H`^S(#*{5V#g9!qd^AI>6`J2yz%?P(cYFkWp zq@q%L!AO;FP)fU$)>5`L>_8eKDJ@0r7@d;il>%H9HJ738Aq&-r(-COkFZ0AoaVd(i z!@zmrpA%$I$8xzv{=~3znp8BEVF`9biywXu57cAz!|JcDrOYzDC(t7V#rFqqq59_s zsQP#GvGxz{Vn^LfbA0)W7G#Vhe)x``+FeoV;N4x!rvsk%_DCX876i3h4Yfr5n3jNP z6cek%eXS-f%&Siw#H`5_{{ZF*q!Qa`d7J!VKQK-S z@q9dD1X6kgBAIhdoQ%K_HY3?FJ`EmNbH-4|l)|0HC)y745z;GB-k1;C@#T5NX?_0x z00-AFN(LpcC4&^NaUd^ZDy+fg%}T)!l3b6jIO>u1SmCMYeX+Rcl}nUEtwc+YZl-j6 z4Wp}7}~0~P(>hpoiMEhdvI_{ zl}Q?a1TsO8r%WJX;M?Z$?m~t@nj@Iv>59%0J1vT{2OF2vaUW>=ZXw?1Zjpl-%7fjR z_mtccQrQ_+Gy{Fb%wxm)pyr|Um(nv2qNaYGOq>9rFKbgke%?xlkNb`a&%9T`gwihu z4b+Ing_$BST+u%a{!|DdWZ!rFvN#OI^Jr8Idy4(RoW3ejK z{?a4{pa-!fA^h85;D7?eb|X8sk89!s$gm_11KIEJ>4p>W{SW;|lo5kUYNgP-fpSa8 zn}v`~5EO<7A$1Lt%s>EX@_0IP9rIGj!rj2*B0Gh6?rj!Y&3_Kv5{$YpScrB^0l0EP z$&U?^gw`za)3|>C71Npa7hcd6Rv7n+DwUdn5)BbW+26dg&@s(?%9zwFF<&reY+$2N zikV}zy{Bfv+G5QbC8HbnY+#s{w>=0VBztkT{_2bk*OJc_Zat zON##Ji+)sM{E5bNXbIv52GnDH*Bjw>Sq-pv_{#R zsfgg){7;45Y&&^dA95JMb^ib(6#=>#Z{S$RKz|X*1*5VkFT65AjIc-nYX`~G1#%X{ zDQ_zLjHY_Bd!n$4;ROC;IoXAbBmFD8^K6udz9a3Zi1NiD3zc2Up`PWX+-Bu811lN) zpO41jgf55iHxC<701p6g2pxY-5ps`V`6e1H4Q?LH3Wt)@+cFkgr2hc122v$QoS$MW z_=wz{yUbIf6wJCNaOw9_j;RFYPR+afLE7EFG9Fm3a*H3H4gloXIv#6-5$EaCWz{MrZ6upmzC|(H?y*KuoBCL{- zcgN2Q;ygIZ97gVmmdWmcVa)2{{X2Rr9SWPFa zarhMetRVg2KcN8a_>Wn9$1n2f{gW5tpZO^&o8RawED6#v{O$BSr&}BN%&-YjwpM}h zHk*lthDKziJ~I>$g6k5%Dr}AR27*@vf;5^)7MA(c80CO3Z4g{sGiCfsQ7r!XA}c=U znTw#;lfm&kpnoTlY+!aq3sXyz$GjIcRw`wpI4xri2)y~5OkoV`d`UvvK)99P8ij8z zfgGz66KTB1GgypQafO<`5L*r<6-9e7g9?mdA1nqeMHN3}!bsmt7K=iD;7j^lz)JKy ziGtvu<>BXiw=b3YH!#ZO3Y98Ut_BP_xqNP|3yDSZ7xoeKpGmy%Aj8m0&zN?-=I0Lt zUMDG@SmjKG`IsT7a-kk)%73;8Q;R&sV&)>!d&+478L=DbsbtogWPA9To@9TxP4KQaP2BjA!iesz>)`V(;w`VdlE~OwZIKm5hM0J; z_COmh3rL6^@!YEZ92`_3tAN=d-7rLZI+pVCb5f;p5Fu!ayHIWY8T1d)9*=Ouu)raO!nGJ4jR)d3 zQpouuZOC&SU+Utz+Xxec!K9tj&6s5=s$9k(%R>+_X>}dSqFU0IS$$*S;gybfXOSKz zuKqLkifg@B_DkDMSv(f6{W?Ls9tY&~*AVix4g!m8zZYQX{=naP1a9m3#=N z&6iB5P=E&j)HWp~p)2+O0Bk~l4?ow29X|yAOCZ{4JA4{%WkY-LzpbA`mM zjhc@7r%Y<$EC<|yn5OF*BERa0w1Y7hSz>??D)8y{V=Ph8a1uHxhcE)u*#OGJ0)%3B z64CHP0}%mE@@J;uIB^Xf+dP zh{wY^neNY@7Yl$T7tLZ+OiILRqi9D{ey4zo7wc5I+*_By0tBgvh!UkRVYDU{qZ2^- zr_?=1p!&?7f}#w8MMVkls|VizU-nt29NP^`3ixq{{XEQ6WPA3 zKGD#jcGxhBO5pNh>m{L&We?_Hcp%cAVdY+&j1MK*F6`RwUKr>lcrVT*u>Syu{{Xny z%wwhX&2J| znUzb5V9K~#x)8g) z=HID4k@Sy6pW?BDbx2FJTr%zS7{elJbR!XTL$?}-3Adl`U?VwMv0(m$(Jl{pklt;G zMbR|NmBT>4EF92dt4zzg*d^)hmYOyvYN0bi609Ati&!PkR;5_12*ZI3k?8RW&f{dT zI?A9!F2mp?!Zr()T(7>o$OZ@}!CEb>Q4TBirR zVOBN~19?>xqq-byWywTi@WpBa*e+2L^n?k>iOhMh3C zAg}u#OO)|7GNBh|WK?WGQ%^9~H4VJf zc~?l5Q#N1XEiE@8x*}7E!Gu~X8=y4+qO~_Q6J-!dU^cJ<*MVB7jYN!wri5Bd)*&!;@auIXh9ih=@pE?M@_^y z$s8Q36@JVzEt*0KjGF0ukWhjxaS^;lB4+$7V5J+>8zLrrL~-3SN)HnWd0`Tlu@LGW z1i`-q3Q!*^2wbNg~J}fsM#!L%7|@E#V^Vk5VB=yJC4>@9cm|;NFE|m zS7u_Mwdk%?Bl=4SL+DhfHN?0b38VT$=$@17kfQ)T>y-FM;Gcan7w*G^%y?$c0^Q8Z z*l=8VBP$+%=>$bv-AjDY{U%>I75wlJ$8d~eMDhKy`rR{U%w}G1F^f6*i?@m3L1V&L zRLnjh&^iv`PCo!bWvL-_X`duEj55?Mt?3NDk7<06;ia)i*vj%pz_h)k%1l65FuH}E zDN!f)9*a8_sJW`hgLmSTEMD*^OkkA>jMWl?w!NXBce0{QDDWwOlm-kmv1uwGAuLLB zC@U{2W-0PwJG5rVJ+Z#r#FQF>%4$`9V_PaK2~twpYAxE2c2N>bno37<=s`Rpbi@*r z%i>-#c_Qszt)Y@Q6?;$ipK1Q-16W9f$x&M9!($~dU5~W!t`w^32DL~$I}-`O0;?S? zaZ-k=Bn8gJRJz3_>Z2hW^+adz@F#x;T<%y}R`7lj;Pz~IO-c~yEVs%qM>6!hAYD#W z5-Q2|3dZ2eA_XMgAXS{j1EWMQ1eI%8nK{TsHkyfh#-%xL;$?J>LNLGBfUvb8V@t4w zh^KA96>+FEiWPF8E*wjbNt#g;iSH>A#PZApw}5(;&4guF^fyw+mL8Dwkb<+~C0{IA z{R#9>(VmAdU>|sq7+4+zx5K#txjT;|i;QzB4q4!f<^f>*FwUbrhyMVq0s^I2nYjG5 zAwK>h=Od?M?l9u<7E$4QQhzav%mmqK6D&^%v_Yl|46Dkxc#4zL^Kef^&WIg|dn3M| zGMG@A5sr5SANq~~E(zGceQPI%6GDQR_%6N(AQS8%0Dx$6!G)u1{;V=V=17T`y9g)& z?G_%~L$P0iSCH(Lc9A;Fdc*CB{9IB%v#2PzD#~;p;E!1Qh#zb0z%q}B8L#n~ya%x% ztpnMQ?koI9iBy=4+#I_sAOJ6^ZEplU&R;mp151L};WYyX{jAQA*aJ1> z(+{kwz0uBgh#~o}z=Y{z{{XbAbQEz+q?m4K(tOGu!UvAX!Dg6C%#DB{ZBf3VO0zJ( z1hftu*Th`cb#lFQ;#~6}cO=>E2uj29m-tLIJ&=wl0bZGgnCXXh$hUpq2$+CjAO$&2 zj^zs!u)I<*Qeq$hj;Ty<(%>LWP-Vcl3%FFw0tlL0h(o!Qj~1dA5Z&gc+m0FRBN>{( zE6Xd2iV=lBrL>lCaD6K$oI{n4{{V0&fr~yeg-=8GJ{09$vp|cunfx_MyCCZv*t7?*eT%{Q{qBX zuo;Ma%fFd?IK`F_<{n`{_dF?{#?=z%yUEefwJN4L>moz*%}!9A}pX{Qv&={z^4bY`x64as-Fu;@Q$LXjV9aa zhZpIofP*ww8T_r_-q&mz#p0$^@%?a-B#4?w-?_gmK5|05j+hBgbuTJhIQW(EmI{x9 zjr`ET(&dbA6%vYM+}$XrbkE1JZp068teq^AC%n z-o|^cHlH+O7-}($PNuBY75#_|t7t$J2))>1873u_rV_0X>NRW-TV;%=K-nz%wPP*m z(~8$@ic_squZ@HeD{SG0NTM2F%n5iVAKK$jc|skQEWa>eWYL*mdot_xN`WrmwnCxe zGLQzSfL6T+mAzJcOmEJ>od8;w2P(!>UtysM%7W zd?3J%;TMpIEQA`w6JixMrdvzo)-Q^d5p7Sd!n` zUp;9YVL@`OWYM=rq6kcb$WzWqBbVRR>d*X z^6<|CD{`o&(UgrL?Hp`T&WkrHd$kN$eWr!KFlHCpDcrYgw~O?NFMvCWY!P0o;spqC z(D-|M65hvSh4^_A&#{2_Sl|OxG+%pjbW(eM4+LRt-$eLffU(K5{!F(f>$ZL6g=5ai zNx5;mLQ{=jbjTwa#6E7}xkU)nQOd7$y^uBqA>j_1O~I-a!EmS>fRG#kW`0O0M(I-HYUf#{_H;?0im==R`+&R+uT9ebEG`F;~emhOER2 ztt0Izx-1f!{o%4>_KGbh#0_QqK&qR)N^HEWxZnvb1{?6qfl|aOT-7m}racjzP>N=l zj5-LV+%?;AvjGO&Nw{9fJ#fnQ{;r_0eFrB5A;J#;hG*U@e_%gCR1%xnhsIHg^b_Cb z9ac42j}HvfFEQH%+r-JdTL{d8gky|jN}nd!{0St9`-87$*U#cU!KZ>DQLVIZ%)j|s_zvn63s z550zFT&Vv5)lA~Ul%-5xlo_R$!i^$IX&57jI;3X?Bi15DlHRf5n^QAR<uwo0W31m9>!Rp`z%Acz>KEWVi9o`X5-5Rdq;rK z)A6)@_|6Et80uMn0>YJ8$3cN&2&-t545cvssH(|Z)j&9iGQqXzM0$Ras1gSPQ zG$Mmhq9VDUE9`0wKM?@O^Azvt);T=`!3q%Q11kq}Xo5eAdQJh#^0vL+Q6*#iJJIgO zTU=^!7)O)y6&pxIiSN`wSUG>F_bE|Xvk7PdETa&v-T>5-OW%_c+fMca$r6~#Z-MO_ z28t`BVD?D=0EPf`bb4+mqz37XK;c^o&9SEb95808t@Ryb2BFIPiq2Mf#B2c+5aVg0 zI=?QXP3+kU4QgPd%4qlup|6vNfkmhkyfOKfAZ{sO60;iKCJ5C*wCrU{FSa7^INOvy zH6M68)S8mG3Y`KWI;-kWbA}#4+4Mmg){pJ12{yhdUixB=19Ide$+sp%dj&~c> z+Q9oHco>6I;iwFsq;>3_F&b{}Fnj(PaaES+9Qm1L#_=xXVsQ)a!lXTa*vtZ-6690F zOFBk3S7XK%wY7D5#WJ(ML~)wu`od&vcq2lM9?@5C)vr~)=4x; z5Tr#rWhM)18;X_ z>N7#gjxE#s#P|pdO+=k=YB#o60L!zlXb7WO zAsy(w7rKS8)$5{KI{;t2w^|gw@{=A9j7JbFqx*_aPMB3AnDnlgk&FznAjJ=oQh(=g zvLUay_aC*pvMgoU3l8kYq%W?<6)lq4Z<%ml+gKnfjTqz5gr`UA>yjLvk`pwT9|Uj@ z*^UpQOQ5Tt(Ge|{eq74Ez^DOy6KciE$ACDu_d!K5{{Rl$Ibj&^phg4S98!{!VroJ- zi^s*V@8s?kgB^{cG$$FXktpFgml1g~)W&O>g2dKDoH3R(U2F5BWb(L`03=(%w89;@Q^0-u?tqW~Hl#gbTX4XWss_u3kN zetK$!;P})%iDbEQ;LDdT74A_Bvq=Iuc|lhBPRK;9-?Q7yw^m}WfyOIaJrKvY;w7N_ zpgDrs*U1nXABa)Jv2;vE>Am>lBa#PWrtt(L6dwtga4Q|Q95owORO)8~2q<@UtwJ$c%@D1 zJl#Q`po2{ZkZGvN{wP!Q_Hk}8!BfQSKKokhI0F>R(jYhrX} zfM%b?%6Z5MiDjd}^j{3z45E^v9nMjTwU-EPE*2a4BTMAXS5V$E`U;5dZ3TQ#)?xA@ z21Io*WOht^(9K3X_HBE*VKGtV!=VdYghG)b{4QOn@EhhbhPQ@Tq`6c#x04k5 zSJPY`wa`EotI!h5?I73vM}!|`{I*nFyrouHVhb=0fSRl7A4u_Oz=Z%&G{;24gml7E zQ?k?V8jdzQAtx`x2F1vR7Ab*Y1496}2pZqyDw;J`v}uMxjx z0!9od1+M_+46vgMEFn>HU+iI+qxhDHzv7r8%oa$%4X~wH!|c-aBcZ!ME6L)j>xbVigsUUnxLSY^k1Ol^<21xnM!C6JEt9Tv!9NPu^CCQD2RL>SSs3_jBFI< zA@aVfm&MDME)N&Q%Y_GZvT*h!A1#6a;;va|jQT90XW^K^z}(5*b=pP()#V(xPOE z3=-&+96>R?n){JNSLD>HC!r_K2Gs_HKz5wH{9MOj1+Pa#CAfoVMW>iUW!03CB z#uOgM=TIOTg%XPDcmFX(Ke`{Sa{0Inm;}eaFiGK@p55gD0)WvN z^Z-);4%i01W&Gx5rpon$(ctF!zfH^Kgj~6D;RFn|q2GyTkiKu2)Rb-2Oi{vqUJ$sT zBTKXaAPx9ppdawY?LHt%M0jec8;#+XSu6uqZf(~!Q&43R_JF0TiaL%1<^TnGept0- zRxXj1HtscER82lrP6-S)!7fZjKrN4PhjH{~+`qHreFxAvJ5131HGY)zUlvJc?XrLY zmejR5LRjOuqYwkD2b_6=vG_$7(8hET!(bqJWlxNNgI7nv%flGNmyEt8qWsTt7to@_ z-cX-ugGSiC3sE;1k<~ygVPR|#)BgadbIGOlyM!t92zz~G&-WYd#k9W2J1&UsX^lIw zf8qZCs7eP(2dbgzzAiYqrV<&Mc7IleqNw8g6TuhXAq@=7IG5k4v*6)V@9)tWd|#~f z48AU07uKd->S=LNDu3mi8TY5_md)+c92#yZhZ1U4HOf;~Awa_7Wjba6GD?E(pcq4-8{F zn;#P_$~+HAa+ddGH+SbTTJpR%=b$EO?Yj8PmRl@PcelGOq_il-CXtViQqSRXlrz9P z)9MtVHy~Lz2tJ1OAt z5t~elH=c6H-yTS_?FXS_7)^idNknYN&OLfbz-Qy+d!V$27b~#kNS-MP=u5b zz@(P!o42V1RL;7;B?b7@`~xn)*50e+ulYM@zMpR&{=&@0inrpONcLm-@8~XhRbu}D z(<4?}GM+}HM_s@c@5I(4tM3?xdmrUI-kk|0`Y+Rbo`>rUM<+7|j~9N-9+c2$a_TEJ zj>tC8<{{yZgf8w5kd6|bkgaLagtQuVMzap0+%3FITf<bc%NkH-uV#})bktPX`n8yq`BWn3tIGW4mBOV_UU_!MNtaWJj z>J@R>9+C}+Rp@1>Fa#&0P>SEg?lM;|4*d*&bkR1wrg5}CLzplnKcTpDev(}HU$H0- z@L?2h6ueoJWJH(Z1{Yn*O0o6=6)-?UxGIdeV1k%hGCLWP_X1@v*^OIf68OF@EEp~p z3^jYgFAssnsJp2~o`L0y8Dt$i5fe3kGQWREh_%hL&lh`m&HK$W-KrnWHFd8rFo5LF zTd#xK{{X1Au_$Zr@BScxJ*n)Sj4c`!2idL?16vv48D2Ut&tQ+Gy!;D*)~G@GcCwrq z_GX^pbE$PccSqfcP{y}k&-CXv^jJQNmj(4)%)bmW88AQGDv)RxVjS#xCc3$<4%LtT4n@8(d{o*j=5+ zXIE1z_JdhBsMvu5D&yjqKOl1O>(c(}(R)tl@AQPUimW+L((8nY(%wh{Fe&$AWo=8* zxUS$B9wMC>zMv2cL4<5?+I1r0SOXP;%`>B?+FUpyCdj-m1XWa{fGTai&6@!wOziQdbQ2^ z4^8@Si{kmeLu~O$Yz(ED=>azqntZFw9~1%y_J|{&CjyZILaAsn_%N}< zbV9)Zg?9e{+GE7qG<~8g<2UHXTxZee5JqdzZ?nK~J(3a9h0FeFAYX5Z>`2hxdV}3j z({*3(w6lVs7nbYgm6@R&Kblb6?z%h;4`cK<#riMl+_`6@OHwgjQ?Hcj<@gfK+(y&p zQc^d(?0^q&7)L61Y8W`)@|$b-F|R{p1pt;9i|7k$Qz>9&v4UkirD_>OQovbdrXm<1 z+Y38mt7L70)vpscE{+(XvoKJO+KPj6)fw&q9{pIYcvCxxgI|$>y&NG}B1!-QLHxe- zzp*FOwMCj~!1|8|sE?^QudHx@HnOw2S%u9nu@**FC}28yj+bT)Pj?`~%2g5fB`yoR zSE-9i5F(Gc#46A{+^}wd)Tcl;KT9u0|cX6}*6t#hL7z*r6lY&%-7Z-(?;1Zgq zo7{_qHHeF<<&Ao8h4f!6L51q!B-+=$$EVzfi=vRT5dfm)(z?Vq_|$%?{)PP`V@{In z%6-u<%Lve8l3pUNFwcsb%w!Y%9}up#rWm6SFNQj~dVG73O`i!v4pBekm6moUHWkS( zz_^TC0i0tqJ{eee?bCQ!~f$>C5^|dSB9yp|{)r0K@`0KoGRF&E-okaS=%sTWDb!{&8kIo7cZ=dSTAM~xO6JZU8FA*nYr(1f>A&iebK}->dTLa7SJXHeHCp)Sg}b5= zer*Al0_Dit1wiYIi|B0e4U!U5=AyAXSi>EyeTk?-kOm4dF?VN%YslqW#B5sZHcs}F z_Ubi*46J5brH5$19kfTvzSE|;X}7TOb}SE!8}G=)D%EQ!MK3&c!wvD*J|~|;zg66` zeBTGtuhQSFX%llMV50f+EiT!4h0Inaw=XNRa^RWp>RJnVz~;Low9X-&Sky;|VWuG%Lc64?q*Eb=o-M+q zLNS<_T4NQW6s0HZ1b&zECjJgjSq;Hd_B|({+j2r5A?8Q4eKntW}^Da9f+2S(d z4t#cW%^Q3&lOhq{mM|CIrNzKF9%k>nNKy;l;kg*sKgN98ub}pEY1)88B#jj zAY9Z$s^aL2FEO}a!pk+V;EG_`<;TQk43P#EP_{HkH*Z7$8p@DoU{q_lU|D~Nw(-}~ zS%0)&(Z5ac{&vXj`OGSPqog$e=VqY79?yCqI6kt4?O~qiWMlPsij*QPh-+hPp^DC# zb@amBFFxe76Gs z%WDn`4aLDgwBQL1JW4&`~4di{2G7($F;$s?M#32wZdx*4+ z_Ir>(Av)3)V=?I2(*?-Dt-`BD3@!lGlcb`EZGm#;3Y$vLyuV^S)8{*b5#m;O)YNv0 zD+UE>;yk~%?VGqho{9Y4PeC0p`o(%H&#lahJu*m&MFHUl-^eX`6>rq7MCtVyCIgZwersF`vL=3}FikxXC6Adquuq z*=t@Ht&gYID_4WmdQ~?WR@eubZmJ8!QRvZ!)UHbPC(9SHEIj~w5YJ<{6FsmGorlD$ zDboa$hA-rZFpPRh7)&WQ5KvKc5zlRa{iU`e8`y$J4kZk*+`vkK-b^@S6rrMk_1YU9 zv>snGI@`Fsz9w#_*}qQxJ#l`V{;lHW`Y(}}E?)=GXBitK6Q%nZd8#=f*%bn7gw6Xt zCBGT#xcMxDFFncbTl}FCFh+pSa2Su0I*InK9wuP9i-~JpN(PEaiFGnL&+LlZ83;P; z%*S&QD2Q3^l^!ZTvZ3xo@t+Y!{+>Xc{yw?DrxM>#_u!T=hphEHD&50UtnaE&zw%4NU8SEy+$fpIg4~9?P4Ta3qPSz@()gCoelyQge_dnx6qV?{FPoQMiwTR>nFEERV!EDV11jUi@=t%~{{W{MIoAChaLjsd>es6H>*-&q^jFllGWb9ZBXCe^ zvJkO_apF>{2b0vxP8wQWM;@1yf#rY1s0C*bOQ5bG3elIUwp7V+(Z;|Q%(SRe!_xx( z#p+!xL{Zf!Bjt)*p!Rt)BvQfjD0QVwnMirUJ+i6caJ~rmqGbq=5tU2Ie^#1uPWqn_ zTOPGSV*X>exAe|VFZ2foG(ek!0Lv+sL@evlfgt>5H%wpm&kU`1< zRAj*j_iZ--BgTkP$C;`a7ZwhP69E{h1WrsHN9|X;i-op1XTKNU`}mhV{{XN4Pl#e` zk8bAYzYoyfI5@whewXV$n}pXF=)Xn!Z@G;wsD|T6$pHPch6_6wqP;N)gK&Vlg;vXy^MlzX* z5jiPOm$oadR@Ng&UwPm8pYJ?AWwDB_zeg_W!u}xle?s}cM{}>Cd|x>aKy8WXYiJ*U zJYS~$cj~*q@BSf+4z`4TNQi+Eq-F5%uQ%zwE??01$p$gx@<0T&H3f5A0a)vO)exRKq$mGAYQAy~@zn@oT;KZUH$MfG2x9rbBv^#gab zYhk;36b8sU_E{+kl~HOiCD~#GvI%%F@+wP1zYJ3WHSSHb4x;$o1*@N7h@?jeLkxvG zNui>V63YC*3`UD$;W*L{aOlMYGSf^;q$MaI%o>b}jZ{QN23y?U3YCeOaRxD8s2ZIH z$-chPN=o5KIvKu1clA%8aC*)UKxpxN5Cy+09)mOw@pA7I<_RzV01MB*qULyG z=&k;t*ZZ;SpL)mu7OT)p>?HL?{j$5N1Ayz!`4eJ5gRvndZShEjVl)OP@O}7~`YZHj z?l6hmiet4BmNJ}?-wag|E%zX$fAokaj-s?-iZds(E}<*In4S{D-%*?xTd`tGYa?BI zLS)*O7ERk<;rf>Y{0YS6JPZfDfo|%H3-MpZfz*}v9CsjTnCmc&Fu9u zN^1xDvj_FsBJ%ek51qZu`~Ltl+y4Nts4NxmJifwty~pufJb&A4Ne$!J`w5;V^#0f1 z!@w&+n07;UPwf@2IUoDP^~7-e*B&nmx5_LhJ$&CjevC*nSHav0`SSiV8qoNv%f1trP8`djmUHx+v*M4)sx_vLi1EV z(h8EG{&D6G=YI>Vr^tEt#~A#K*Itk93};V_92{3Sm<~G_@9!F(tTy>LC#z?DCpNhJ z!-4zO7H3b}zb-C1=cn^x8oZzSVsFH&r}_Sx0NC8g7leRL+%lao zz;Omz6yu_xsE+K2h{37f>na~W2zZ^kO$Ga-&grkYHg^aDE>}A zwDvI#QQ)OQF#GWz+)7mrP7=KlcK8(Pb_&zSQ8`uFeiCpmT` z@lC_3+ZZE}m9xgZDrbquB%|!YX+Ia=!^J*5>5Hj`DRENGo)W6>w(j{`gBEXnp>s9% ziIveCmdiU6r)L2DD)_*-UF`XM{#;hg*XIa=U#1yUvs$(~{{VI}{10V@i92}CuEyRx zgegMzU*}vKeRtybLLMj2ssMa|!=IFYD;roERd>(gvLV*JcjVJLuc8fY4MA~QF0~BD z8UFx7&ok5$j;-I22Lx0YnEvy;vD*uO$zTS{FP^bS`?cJBJP8uYKY$q6m97TT7hQKG z?F6qu=12<{)7;Osy3hWfsn4M6gGkasL&oV7hiYcX-{nfnA%-?Rsb4o?{{RxQ9%$M9 zJ&!sHgE|m>P1ctA)S}mQHhZB007e(Cccin!ABr&*gF=)P3>Pt*BY%}RFi7L3WJB|! zy|oby+;oTX?M1cBelm;sgi05(y&cR*z~kY9?^zS3Wud-I8e`J*#oXL6K?i}R6uD@W z0%Dlahzf~iO3f7!Tf>lPC0ASXF%cD50LJsFLmP_>$#;k$6rfDCac!0~h~bEfEx5)} z1BxOe0MC>ySGscLm_Ne7`fFQ9@_ zX@DAr;0g^!tZ1DX=0#I5J^>{GUSg4eL;@fPh8qcn35CSR)wVGvXHX+iLFg;jT1Zhns_9oc$I&pF}+VS{{S*8Kc4>ZV6y?*#L%Ta`%ysyxFcF1@K zrT+j<%}GaZ=L6|1p&L*>@LVTETE#~;nx6RIQo$*0dQqmy?+YXSlKqTHweT^7x5cJx zKvE@y7{)seQH{VrtgYED45B=&)qG5fnbsFh@oUoBBPNG+@B$)~I>IVcN52AW)>pOz=^h|rKR;X&DvgGFC&CbpOXo@rw zCGF}3D%KWla>JY_pTU1v_%!PM{IiZ)-QCM)Px-#_%zd-}0JJ~Xe{=me{e?ma6CKlK z&+|OuUx#PUQO@yyDV`hej`{xpn;s85$KG4vAIP}>0C<}J08|LC5B!eccPIY<5Jsb? zCunznyXHUZzq$Uq{?GB>_8`1cI(I{O!{zhtWAXn0uO7Pf54rru_nC5b{{XS(Si%kb zf3MsAcaL8BVPA4%MJeAmKi8ph;@1={U7)oJTP@tR@p!m91^7p#1J~aoecL}Tr5U#K zLmgS;Fyw>&i~FDGKkWYiMgIV2`Y-zx+V=|tayYqh;2k!;%(f8T zNN~g$fYwGXH+KS-ph`);E?)ySA{AlTGmt`IxGGjGT8kH%wAx&4j$T$=UMdA}`VUHa zFVlQ#3t8W!p&waMsNwjqcM(v4&y;Et>b z?8LF$zliQ=UuFGx@};m0o@k_g6>qpS_f`U^p>x%lci%)|`plJkpZ+I7w+Nct`XGqN0vV^`A)k#1RhBHpOJ>nuwmpmFc;0 zLZEGkP}tnO6*o~TI3PZQTy%BuKdf8hsE10$+;+f!DTps}TW3r&!ZBt=2}jr?Z9^UL z_cu#cH0&B2pwkcbQp@iItXG^xZvOz^l4lk1InA^m?BW2V+xE`pjY@!|`Hkmkw|f>?03u$? z?=&@Vlzrf4C-Yv9_+B$VpfmpfkpBE_o(ukmo;gInkLSNc(u{lz)c||$V^8ZBlT)$N z^E#K;Q{fik*8Y_rk||mvQa#|FZJA#N*BK}u(j8k~0Kvn(aCPd5Z-TORL!sdt_u^N{ z(@HYJSo{Y{uCQ9BILR_cE8VtF{P=TeQUYZMDo^5`O-F(kCJ;W{H96M6ptpY)F| zuq#_{ly$qrAvHem@RMxnyUuGvA=54URf1F~*XIxwR&stKFk^1Dd=q#KvkHGcp-Io&I% zmtKnr)3m-!_a-W8C&!R1v4Il7XT(eJ=u2Q(J_!RHA$3N$$d@FC|(3LORwA7V83(c_**UWdw=&9_PBNN4h!b> zW35zNc#8wj0UC)1#LW30_59;#Yx`VMzpY64Hbh(3%XY6CV81-0Lp6ONK9K7Gg~ z-e+}n{MIe+M8KbNie2}@?R|cDKdh`1k(obfVFsU0GWQ%tFsbozikPzy>$$SE!o&-0 znCdGl)NF_`ds2<8V9K6lo@PACmCeD#970|t;t!#ZsQS0S;-ETM7jY8C80e}cMNE9h z&(J+5p!AT32PlD-D=Tc^Aj^EP%w4{P0H z09PLTZN1Dd8{y|{Gxc9azhPxuZ2G%?-|hpiGLW_TnfeTLoF#GJ>2`w3Vvc0#a6)zK zxpAk|E+Qz(7>^f-yjT+8%ZKW(GRP!DsH}&EIESqCj2=jGLL3|;qjJsM4y|zEB|vY! zh?K9bVdaZ(&F4npm12=@SaEXV2*UZ7YMGWB-#ta}OYa7L*tPBa zPk;CEGMBk;&+{+e{r7`X>GAwc9ys^odH(?H+}rNr!L-RL>HaY<(oaLKX4b%PM8!REfzKlH(hLmo1OE1m)C!?-{Q!vgm?#unPY{RK91spE(-KC&jJmWBqDG@ znZ*&)E)v1$KDEpG&nk7qj+n#QiFGUMT79B=uIbGeGQ~mwU|bJL1`y)Uh-YN8G1y?% z<=nRp$|5JC<;&na&vaX2DH6eavg{Dz<;Q@qTX3pGjLdC2udtyi7U3HGYHWBNpjMOneuuAwpiP#O5zQq1M{-n z99+3_;>Ir6Z3&>vk4U$kE+mXCW%wXdDWU}1)BF)caA588_ny}-rn5JhzklX#du#aq z=D*c6-1q+ge-X^zzwmQ$})vdLZ;39=bwkslf`S`l2_xp89`aq2PxHOngE+!}(#cv*f3 zFt#!h1_5+F3{kCi1q1&8ylc4Q{akO~`Qual;(x!@H_P-l==tXOe?tDre!qlaG&+M3 zPHX8St_;W1rZ$U%&1shy{=vUe{Al!J(EWvZaX;KQ&m zHaOXc*mo;DFWk9d%Iajz{{XIBy8i%I8-LFlnmm6TZeROx^zSose0x0VTxO1X`aOLo z^l#K<{Tk-^nT{ru*}>>E#~HVwgTUmEBQ9l+6)W{;(4%o}mlj;KJdlCxgo$65piole z7i6%+tBK%nQw*;jk(717%(6la1rb#U5j$C9u=1Btm6r%}a`__KCfS3NfPw`Gwi97S zk&65$&6Nsx%R3-lK~14SfNiKLY8nn!G?{lQ@h*f4dPhF;h*k^)r^TEHn%nOMD07*< zHJyJGt~?V`{@=$Nv$;Xt7Eo%K~6f4q>7@$o*o!N41EL= zJRB$Nuc%zNf%gN4nLA_?xIU5;@wm~};bx$Fo0Thr5pRU6Au9+<)kd3#FSZn8hS`3b zm(HOHZ4k)*+;{7409>;cww0cKDriuJAnHb}M6V${0zC=$j>vp0_Lzk!i-C+fF$4n! zUwB(JLB4FXydGU_w^r3`wjgypzjc# zTuUEv3S4dK9KrR@82Sc0jJrQ)K8s?iTp4oMc9xF=AY8Jppn5eR*<@7ULW&Z$NxpmI?)XzHOTyAaV z{RAdoM*je0ew*}NCxqY`=DBw_^>jmV*QMZN9$g-yFYO1UQHyQI&_~kpC^s%#xooy( zS}cw6Tw5=jga{d7zy{_zk1O;92Y>+q4fRqMIlf|@yj&7c?&XBFj;NcA)rEH~P!r1* zwR|mQf>?$4Gai0JFVU_H{{XIB{{VR4{{VOC{$k7ZEO;eO{(~>l{^al1QIqEl=jan& zx6uC9`pAc-lhpkqzHVXS;E7|hSr`T34@k09#J7w}mG3LwAa^@rRH;im&<(^u=wq-m z+MhFn;GZmItXiu?(e>N{i+sl|EX<{XiFty*Nd4SJ&$`4kJ$s(hUZXy`KA&H{D>n;S zZXHa+?a8l6-vNm4_am3pZ=jSl9#5o0QS^&1kd+0(AqzMV2q3zGd!G5=L^6nhz={RN zYM{X-%s5(#VdsjS!EJa)gB2m;mavh*S&z39Jw@zH4MEPJEG^L=WG}EI`4=<^ey#DZ zZa2ob-x}h*KK1o4oAuwRe?fd-(!W*u9&PX(6szsUucD&{3=mDYeNXMLtQh(pNc|E# zU!tIKaCxmqa;q#UT7r+6hY2?;QeY77OC=hGBEcnNxT$Lak9(VRq!C0Yid#fqkR>Xa zg`pn_;%Xx|aF~MD>K2z`CF3XuLlc`7OJ5KH9n&s>0q1l1v2*FqH#P0e^$$j)#qrnb zAJ}h;m-O;qE)2fj1?jkNhm-q(%j+1gOUWOkUTRdbU!u4ogMnFN%s4n69>gOkR!$_i zLQ9X9K4`1CpE9D4jtFA|whHk&Q4ox&TF8~8M`Wn!prRHImQieZJ?;t?Wcemf)1j>|2= zMBdmw<>C1ytd8GR@Mr34>%OL6>M!+<#ta!2KND=%i}aEG&<5AT=s@Nnaq4mCBl0RsXA0|x>H1OfsB0RR9200I#MArKNV zK?ERC6CyA&Kw)tOffO@BP>~~2a z5GaY!Y2Z^4FFPV+&x1$$bYD}hh}`nAsVW^TKL5D{{yQqSLa*mDtuO?v zDpbqJoi{=Nwj+>>g#8`zp+bydMkm%%XlU&mJ`perj-^jI^aGlF0jfNK-BAFo5HqhR zRK&Qr=+K;V5_yI^GbQJ-?JyFxZ1rULRLb?J-9ZIfy`E~=r;i-o^G102EOz$Vdp<4M)h;tNl zKPn{_#xd)!FqqEFN9rT^RUMFJ8t#ekPpa1tkA-5c7OIWv{0UDo=O2Fn0!RT=id4lh zuIpT3LWK%B1&X0lGhp&OkR&eFH3(K?tPRt(cgg_~D7HLP=;^Z{4{*N0IoSlAN+)ys zIOd*dQMgoKG4A&8Xt%Rzs_(LUi;2W@#sO1yQkd+*aetQqEUW?QoIA(dV-lRgJVWTI zQvhdp_z0NqyHxiV87ThGpz(A}y$2N^_)TM;UOP^D8C1Axm2abZ zl}d#=UQvFkCsbPr^xFMGa~#iv*FizR;6fw8r$oNy!88CNwh7M+5EVz})e}6xFD|;G zXZA7Zp*jt*DoD_(bsaEntWmc3LX2mUF*pGx>9U1PjU@*XONx+Ih>}$bnS>oeAywjO z(zdSX6TS>}be~kiN8M84Sx)Zn9#Y-HDfEH3u~1v8Yp^4otu8PEWfr#Xx#*8q zCso)dzXQkuw#st;%5?B!kn*=&VH_!o5~R$6n(*s6Wi2uh8HBf0O_`f6Mwhzm%Iw|I zWOqdcMlhjeQFH_+Uecop!-C;Bt|p)qDVJUe*nE}sAH{IB-^0`yV{y3Zn%6wTK_yb) zx2gOlv@}2vIm{7@qQVD8HBhHCaiRMZh-P z!}bDs#sidJkZ6V``zFz-?HE&NJT@VA6-LOh!%&*%H(P`Nb=rIqoEqpebVtG|qDrVC zs0;{H>ACKPf=b|ij>200Dbhc%ANExmjp3Ra&;>9w0eyFl;5f!Lfu;2a>J|-DTTQI) z3wTfpEQJVwQDGWrgU}(!w4p?sxWG+lXOw-_f^|c(bH>P$c|%E72ne}F#cQuQ^~1mj zAch+)d0>%bB{hzA)crD${gmDroHGMyTXOPaVYZK!*|;{i8(W%8sUF zDs@`@4f-HK!aM@dQEG%~XH`XNTU)42t5J^3991qYj$01Y_;1vaL|Sfbwmq7Vq#KE~*-7l7h8 zvNAWj)UFrloIei`3#xTG{g>1$GKE@n>ItMLF*=f}eNM5aXi+&Bbvu>4&?HPCa+yUk zX34d54nXuPFjgqQ5=a38906&Nh~+R)9@tN^tHUwUTirkOxNoOauih>JfZhl${uLjk z{W_-(Y14c2e{}bO_XlOheK&~VEwOet{nc!%Vs*(-$lB?@46&&^QXqx9K1 z^r%%XaTM~iU4r61uUt9eomV6&OX1zrhQS`Y1otqunR;s+6iy zc3O=fp-k#mbZDTtSR#2tcPrzV3<>G#!aDjJHsoJmq?09YfWTl7~n<7zH*AlcIa|gvRG600onU6Hr>)!l}cx<257l6D;9;r5dhdLCPKO5aJoQ zU9(Pw8i%ScJyv8$eVm-a(yUaVR}07STpQ+%LI&evsC{~Y;osZSc7B6GxGp8XR;$Gd z%xf4K6zsT~RE2l8i>8=1C{t@woq@&)Sz|>76VV-Ug`$qUfM9(#t3ao~9EX$3^1aTu z34JU=!k^i$3P9&Wloa^Y9ZnqHx`&e6e1-;Hd6!;iJefvpppWr z?6TC>m%0=+umY=7^o$zf!yu=%wT5Qd{W_rMH$th?Wmd>AcL?zB8W4>-gJqTd3Wuz& zJ0|A`EJ#nFMZyDFK{k;)Ax%`NQN*(|vVveul`>mRp%RV+s6xsKRsj^p-vAqKI#DQHE@g8Fm#hZpyvdM-AZqr}vw5fN{)l?6f{ z0S1zT0M$9&5yvL(vYmp<8lp}y&nWsZopOPN@r>X+4KKBq`Qly;%$WEncV6qdcu+bGOR01QCJvgxB*TFdJe*j=H9Dy{* z3GI+Vreu6z4Mdlv)9@5@tov04U6hU|1&-ilW&B%ByC_ zUV2vwuV@KFv5}4BAoV9ee9a{yFv~DI@iaG{D=LOGBdWxeKuOMvxz=b|i_AjW>-#q+!YTX}~7PS&7!@ zHK$gt&ASDcp;GptTz;mk^v;iumBM{yp90EFaj6k-=Klcvo{Q>r>G3>mYV^#p`C$Ek zO}RmmRzkZo?z=x_4H}TMQy%EnIsl8_R5OX;HuFO=H*Wh&$#*r~-o=gHJ^r zNC=jWrDy07E-DZUDN+&Sm30a|8OBs1FbHxR3YLDjA{{SG3iq$!=i;$D>ob1Swi^2f7z)Yqat;%zn z(Ilrsd$l z1r7e@pulu+MWJ^_ff-pzSqjRa1$C7|kflnJaF0k7f}uwwBLu=dIYzvr0Ed7O&?>JD zFbPJxi>}Z@WmZn5zbjA?hTS}a1SXZm{{Y8-*-`x3f94UxT~nJ0&e$THqebu#TuS1u#wouu`NA zKtH4om)Td!Ydb8(qHu(C?*m7prFm&Z&jL@I;5qT$}xFoUQ@Eea!a zTqAWl1m|FC63{aKfG7A)yNscXViw496hyBnf{H1~;D2EVm;|BVjzND4o`F>`UGacZ zWDL+B;;0a5ACj1<)8XGyzle5PX3jr02kck=Cc7(C4k9TG?S9W_x@|^3LaJDV(t;=z z#8!81?4}~hk7(d)WEQliWOyzn9V!XIugoN;kJOs-)tnvBdm5bb{{V$Wl5;0GG$S&h zvt#!_PKvV#0m_TAymU52(Vu9IKNtd~GW;t%v* zxPCi|rqM&RCY!C5*8+NUPNBO^)^j1XE)!hWXi!91J=q%L&@ERE+?1Nlz*V?l`?pl! zT1YrC*-F_txllk7Q(2hC!&KUo^wJaMwrP33|N{$^0rZX~DR|Ei?ghOIR0#^}8y?}e4e zqIM{w;2v;25DKQvxVJdc0QFR!8Bwkxz^ZlWRS?%-qG|0w{A294dxfadHF-Kn=>KVWvmw!-8&pBAb3yS~Y3WszkwKr{WbCK_ynZ z6HWo`rf#bmb%a4qpy8#~iIwX!N0i<=u+%i8$GqysuF;Nsxv)Kxo-}BHfirDYHa@#z zvpyZ6Rd^bVyF0`F(FLXmAf{HT&nw77jp7F2{g>nGbZR_hbGRR5>a`!{c`2=Pj5HEe zE(z|vLj$Jil-o_YSRp-u;be132(ZQtBp^$i1d?Rw)1gKuyP|8N$Q%g7VS8W?fK%u( z_t8Fs#+L|!H5~a#Sr$>?6V6X0D8TnxP9p)z5b>HN!4qOr@p2{_6Eyqb^oHm9jRy56 z{T0VjXyEISwHN;Yq{9R5p6R54Om&qJnqGC3_LFix;SPuSPHRn`0ry9SqHt&-z1vZuJlF znZeL*#Qc;R)`(~Tayhw?%)+d2?V_Gh4YX*Q99RpCoz4w|ZP3IhLy#W?gyzaL+IB{h zJ=RA1qQEv;M@0%0DCZ}@frwFy^=o-YF!8C~>Y;`d^KpsQIN30F8jg$U4kPL`2s^v| z6~xsCf#QR%O(#e2zd`v~yO=WcUZ@)Z?5b4^1VqSLvVqYx`g<(1Bw01aBx%txz^TB9 z!coRBo@cW#M76qG2RdDsQ#u7sLjWLy(GeIll<$;hSjJ4mVZH<=yRU*mgj%6FwpkA9 z5$7)O1|tE80I8DWfJYhdhl5CYiml~@?=~Y;M6DVEIxnVAU9nE6zhQm7@2afMayruPUfzLAvu)gJXWlt3UX zp*K|9kZgz#;Z728h{!O23#CB_?73ANbdFqJ9DZOCva>YDF(C{-FcNar{da5xwUyljal#TbkNspSKy5h7Y)VKp>`hW)r3n=$U}-{cDk(^&!JhZ?upFOdI{21 zcT_wfXBETt?*9PLYwy${M~Y)2r2|hhfM`0L+X1S1BBQ|s9)%55-7%9*t_MP#f20=5 z?hvW81D7;{4F!3lek)q(h8cC&fznOGv5-LLxrvi2<{N*c0w=A;Wro=LD9S} z)Qv=;S%-M}Q_!g^F*_+e5h%}03xM4hwHkw>FC|R)#ST!Pcl-`nQ_eg_32+FQVJe|T z6i@^-CVA1>Zk>fOVQ!@`-BROtsvtIs zyhAD2T&k5{Q_OzKohQ^b7V4>R?5A^ch$>MX6O4J=Zn~=MVQ76gjn@lS4ny`s1LpZa zE%_#Hr5#a3NJc`KQX<9*?VdN4+qjs3jZ!vUx3cX?PH|a^nMcr!Mh(>)mmWjlj}nm7 zs+MR9p}Zn+I8hTNJIru$D<)Qhn5Kvbtj=Ktq=F5ITnE)nI`hN=;TnYQB86@df}KXW zuc$6MlcMAPhr@AgH5vUnx40HRA#offeh#5+M(McGKSqQu+a^#0y;Jx`uj!@`ZRD9e zm!4ZKeco$x4=x%A@+g|{K}w8fe}>YKt&ZyT&gu6MZ%*D7LZ5CTrbyI#5^~Mif)L_`bwx7_=dtl*dwI1Az`Gmp<;(qWDs$0m?{dl2uM1n=|=PNxjOaLT>9O zwXF~bf*sX-r_u$%(QvHx<#K-u7hzAR;4;Bfc!JA1;Q|~a85l+dL?;U(7U;AeDNb9X zjHyS`1UU)rA4WNvB!HT$J^{e#i?#|fv-lVs8RiRD1a3|aR78~!)K?2zh+}|bjzDCC zqJ2Ncadj&3b=uZ}HX92-3bzs;6zDT0CYcf?#Pun)Cthj7LVa*7+ou;e$tkp)?}WxR znguFwoF|w&2R00Z*dkR*OsFm|)l{VFX8JB3sHRAEg-qOX>Jx$?6zRa=c@LzLgREs| zF%q-8s*+px=N>5Kq%KL>5aE=AZPYQqhk<*hP+^|*g!&{2Y0MD>YMXS?nxIu(a4{*V zopAwb9hKs8p}9`50WzL*?40=SQwRV8EC)rDgwjF|o>K4=oLqwolW8@&_%f%Ju8SN9 zEiw>zb%v<45eDh>8S(Cbt3dT?Ynt}UZ;85;<9jDI$;k2?W7GvQ8}rC99tiMhh-*jP zRqp~8cL@NhWP<73Kv4lmaEAnGGixT&a}0{EehkeKk~LL(=_EWTz~roQmu1srmT zDc0RcAw8lkiRC_=A)|T6AjFIZD8+*_fIz?{R4~*6D7nKzn^;Z4ZdVHwL0M-|jS0B2 zbsCM@Q9!9uiCUFOguqXHoQ(UGU?6i?qRxr{L@cC~1bEZT$4HIUh)xBCQ>qP$Bck$u zkNQLc6uF+^10q!5V|AdEO{EK)qR>gYEHir|Ao{Ssx8sx`IY)~~Se?z2wE!IzS4o5n zB4rYq+e*qy+9ekjlq$EOLG3BUI0tGFV`d;DLAw;-SYY8vh3^0-oPbkir&IuFkJtf( z9V0jLp4`GwmYXVdBtgT-V{Mb32f0o)DV)Fp4xuu$9j%cQhRO^0BBJ7MVD?H*;1i(~U2`^7FPCcXBp-rw`ViXr$cYzDEoE7V64I%@LU05@(59S^A^!kkoKxxMyLD9SI+@Dt z6^s#dG)587r&~m1qA-kfx}Quo_~bYR`hNVQ0f`DQjA=Jle+k<)RV;DJN;HPW7PumP zkZEy2wk-u7>jOds-O6#7V&Z_3njm7<8jk~Q)kg=oFnL@1D^5}h-bRI1 zVAS+PMuZhf@K7=(R%{%@L#k_`0GtC)u|de9qEQLnloAz+j!~m|MI-3;pvT4~$Z+J=~6O5oUA|S#HbF>Swe?{0mb}mMXmuBUVs6H?xD-=N(x6AkC z9)Jljfwcbs3C_yWO4Q0Y5<;0bRGU5GoO2vpQ$(3e5qxPjNpa9D#-oeR+Yt7+kPe#q ziSZijt_2GiRoez|YoRoUv`7i`TOhRMPnJ3j(<(K>4U`E@I~`GxcBa2Qw_S^!?7J2> zQoDmjqUUVY-Z!!#-BBLpLF!ZPXVpl`9#U~3Zt_e z1`aY&XnX{Cb^tEvO{UXP%`*XZ4C+*65D~g#R)R3ORZdp6lXW_+00SC6dcQs?qzyp% z<*4G;7X;HS1nx1ZQy}*`Axkyt5=t~1`bh;-d!(`2j}eZR1JPD)b|+y;@L=wz`A!Ep z%upjpK)IS5bV8sg)g;7OHs{?_tY6)6zxz-T!$6K@qIC6Ajrogt%Rxn?a)%b6SbdHI zoN^9PgKct&%00?^@`xIgF(m~_u`Vc?GMV|$!lQsqYu$N-9a5ARU8otqR4YDQH8W(| zb%-zRVihWmbI5(&)e-=d9agNCoULEgW#=$d47#D}Q{EWaC<@QHc2%0CA~Tt9TpL!S zrK0YX#?%H}`A(`^RE?s+WvuI$f$R`_>9`7pLETMCmubq>?%JPwHej0I=7JG>yI|S0 zDujkOgx9^`2J5*+m4(CQQ+{^N>Zqxe(%~Hw1z2g$BG$zBtAKVGmP^Cs5*i688kH^% zP>8C_he~6^#D!Jmg^+E!{;Tc|P^T+p$3TIpQsCtXnvA^W2oOLaN}}%ZY27y9g;B6b z!$O+iKvjm0!cn-}z#XOcOg7J`T)kS?fl1I{^slD+mC zCp%J6IgH{;xlL|jo^}{B^PZexJj;Zp)uuO3t4Ylwbqax+VNs0L4)UuUtZgx=)i_0V zM@3x4G{Kdv9@tT{>inv%9jgPh3%94$KCL$}iBwF}YM?y-061&JeX!y@t@%O%f05q> zQ2elu{D%B0zn1TXL`62?N6k~^R{Xanc^mLCAD-`yO4rKXr2!Z>yH&$tt6kLoaI9?6 zu`9Kw)f%iKB~kL9oB@Igm~{1AK!*d3BeHOokb)|nN{|Jts&^bh%ueW`WZojz5}NkD zVI9#RB0{n^<|;V|#&LvcjgbUmGzY;Lj!?JCKIzW>6Wy5OD^(YOrz2x@Flqj?)7>bH zuVFeYt&J2npu>c0)Ok7sDs#QhPyF_LD&lGU6w@sS+X_Bt!gL`+dMF(R6f}R% zAN-HD97mPEI6uhmg+ubfKc3%(SIF;$OY+-=FFj9{Tk_nQ)j;NI?RH^Xe4C}P^LZCtIQaY;?S1PCT^qGUb3%gs$V zMB>Alp;ZJl)=p>-`by*vL7h$~8y)Gi=Hk-%z!PO-opkOJtSQ2>O~2!pSK zJ`D0?9TrBe$aP(b*rQ47BgVyFk`P)ibP&227tMx?J6bKObElLdGJxWXvtuh%APFeO zs!?(-OlftMSY;5=xkA7X+DYUF)X%cwMuj_ET2uAIbt%}DK8rHUv#jViDIBf=!Vv|g zF&K*+OPUdJcB*-T0e}+ZUz*M)6}C8CLKX-TY;crJrnb^mCo;jhm0aem2EcmB^h`g| zZ?l&Xc7l!|!hRs9VnPUyN))Wc2Og`nC{-v{9T9yuh$RpP@_|w@n=IiM5~Kw@z!hML zo<@l%H998CH^R;Atad?@u~}e2D~D*$JuZRF=ew!(GviM^Gc=q!6;kxVJerPha0k+i zP5~SwtjHNsrAm^eNmI%ZcEgYa7_8LlnY71L%12b{tr{ZYZc3zb(Ww6bvSpiupnW9x zvZuh1s2OcQjp-x%5R6U(mAu3v5Cs5K6_la~P_mo=LIUqBgrdS0QiBP(Lo0L=oaeh; z7GHD#l~=pNvAs_y^b(~XQNy1&{{YoD^4yL)ppUM$2MxJKoU(A(E~t452)Ke)ZmTd^ z!q0scbk~8XOoxd9WIYjpV4`{yMc|^$zTp<1vM`wF8UoeCp8Ffne$+~va&Z1W(85f$%2-fz=HW&?c?0+mk_*X@EgILy;tc zN|Oi~)df$^)NfJe-1x)Pp96>UpYbZ6k;&WgpHu(D05cH)0s;X80|NsD0s#XB00000 z0Rj;a10gXIAVDxdQ3Ntj6Cz=8LV=MpG_e#TVse82+5iXv0|5a)0KZ1@$;YED>3UCh z+|`NJ&$*!@haZ7*dGxQ|-MjEbUhm)6tH+@o?fOfe_Zt&yWz(EJau2c* zIZ%^)oEL`6^%)#2ErvX=NqWmNmLi4udndXREtXkgR&kpjs>Hu&`((WbB$no^cjp8o(Dv3kx}NK3z;$;uVF>{0A*7FlJN=(DRRkrsF#YE~$Gl8S1W z^wMvAiRfPOC#K@<8h=$Ny3|&_NX|1?7i$;lT@F`>#ZZ^$I#JTBq)^i=eYKk}zJ(G( z7h^+IEGYbmP~?!?d6ZPSHFO%^kvZwRjGAVb)4%v}dOGMxM5=i`nh)%^#}M)!OP`57BbMh?jNZ ztY4OSyvq$ycZcZNWu7p*bv0q8u1wOLHDae6H}T$vtl()$Q%0}SC%ix4o}8fRs%Dqf z>SZSe~3U6-imG^pcM7+)7btr%clN*B!N2 z{{W;yahkO47G5YwuGY=oc>KlTw2$2QMSQ$<7QC-PVUM?wEbDz-H2B^8_$N8)$x2c* zbf>MoQu#Erbo97Z{zNs4EWYK36O2zn8k^St03<$(MZ27v{s`%5!Bk?BB?h>{Oi3>5 z$36G?Pn#k2idqqn^d+ENbiN=`G&>00ytr&r{Cd2wGFsl1evbm(s!sId0M8 zC}o>5ri)^cdEXsKZFWX&a%=rubXt!@n|^!|`l&fNT6^(Eo~(60hAz&dlEvMBm3!=j zt9QHZeXC|wmD>AfsSP;Xnnt(P%TZRJ?O!75o1~qZKSwTgCH{!UFiAyH#db;MDNvQV zp(+|oX|D*jTkTP7T5HQXQY8qRl;NW(L+-7H^?I&txlyO|vz<5lVrl6^NmLj6?yNp= zklhIz*346DX%f2P#T58sgF0D7B91r{PqKPY)Y?ea`gf&IN;mx)lX@C)U#a`0yYohj z+_a-9E!l0A+hzCj`2KtR`YUv7$wq|e{;>1)QmqNN?Xjl`Ip|JJZ!Nxd%6PukBsxNA z@+>uIR~?@PC*X2%XX+!WCB(E^9dbr_>MX52HssV3=lN!wy5giI*?oz2UH4uWy03h= zA@P=|PL7f8v#SL?DaHI66t!b~^{&Jv*w~PU?U7dRx=X1FE$}$Y;Nz8*xv=ER&b+0m zsVLVHXVR+BhA%fo*p?PA;Qq__68zKcLfS;OY2%bKFTmA}e3UpXPungTtMt!LTTQ61 zxo*7TyEiWtz8=V{67OoaLXu>o)kj0JtDM^|FTrJ+x{LK@x4x!*E4>JRyjmMK670NQ z7v|q{IgS$~tXn+NHIq=03F^Wz>u#K?xx!`P_AE=n60Y_k#WJNHH$s;Cn=!-Saci~x zFVm^YbJylg(xXZdP)ZHB_-yEBNm;V-)N&+IXtMtR3x3h}cp&$ot+G^y$Y+F=?i+VU}5C zo0TSXl#Poo6eaIbUe)$2KIAXC9!DkQ+ralCLHNS^mKqwYyeuW#MG0rXviq!4I^lo5 zAMK%ZJ>yh0V}tNhULJ22i!9utk}R^t?$&6(e22;&cflVZ_j=KbXt+`Ss_eeXRuv7!&axgQLUDu$>_30^6;xxayl;k4d35yF z#mJr>;&@E!+~l;dv;7GT_(C4%`C*m&cJD{jwfeH(%h>u#{{Yj%zn=BZA2`_-#7TCn zFL?Xcuzmjk;yzHug~{dO(K*LTE^g@xN>J)f(?TB1PYn%WEv#L%A?$^C+}W%&Cohsp zo@k*gYwxY7{{ZEk*T4jH`LWM?$No)LS^nQYZB(j%7pJb(OJ6O>hdp^ zITwXx#q#rcx*nX|o~)yBT8@(Cnl<`SsMqoF=qIq5CB72ucvxSIe!NB5FSwr=)YSbb zIn~h*sD`91c_YRz3|q+$fsGNBJgInOnP&3-j+9DEgeI9tq;#XDjy*{aZHq(2jM3Nq zAE}+nqUP*vH@6;=-3f2n`xoF@A7=ZaM<|yh{A24s?dSfDE~x53c z?k*^5h`hF#k{o=eOp~2vE&0u2YTglf*8c#jss8}opGp4!Z>NjzLUj9{E`-zk6DpN^ zmE@0%qx_pBxBmcG`o!PU%`f25gx0Q|@z#gneg}jtw-lBs4k?`_HJz6Nn)8N}@N`7z zMP~5TkF!}R&rztT)2+{?wV?~=e@-&--teT*riJg;@6vvg?*9N5ETM8soPAc>6EtK0 z0J#Of+=h$cVu@|G7@P2Qv1)$;m5x>5Q*3SJUx7J%okf;5%YBVsW~04%IV(zR({ONI z3Zq*uNn$ea{VO_{q|T;wGV^}kJU7_;pQl}jd?9u&Npi$%uB|B|5OLLm=X7~1d{Hh) zt18&gw;31UV^m$D#T?Qoviq96y$QWZ$tqn>c4;KE;*(GLUaV!_`)p5g{ms$gA!hrZ zYwmrBmx4TJz?~enBUk>Mh^O~_GCGix=X&5<-dKb*Y?$$OEzq`jOx)k+G|G6s&ALvV zte5Q{(|)bD{{Wk!F#R~FUklbvqVLpR;fr>UIC!sO`w+#ju*%NdGo{6Bts1XE-Oa`S z07jm?R*RdHYWB6c3C3#T?PH($DzM-F4PfaDf zq2?l#wC0=}N$^8oPHLjtX>RI-l_BB1*uLY=D#j!GFZ~vaevEYAkt!V0I;^jQu1k?> zBBLScuLW;iioFGjv!(h8KZ2RkQuWaLSh)2ct>lQXi`AlXU1zYmqP9^LrbXU!X7$%O3i&IJsy~!P^A`=RQ5)P)Xps@ zyhzSEk+`vWJeF`@q?2M%k0(+dr^k9{B9(0;#iOM?;>v5EO?%hf=doe?7mouH+p%&_ zoTI?G77;H5;T=t+tkn8v7&Q&ZThNk^DEe=ymapxr+kfHz04I|DCeq|nL)3m5QkUGy zOVLdJ&DO=xr}9QVNkaJaujF3&mhN^o@(Uzh~(B|JH6PEQtie92;{syQ>O~sLtWaE5#3k$J(x+EH} zXBtm*Msb?ilhlTqxiWuIIqa1pFmin>b5H5K&Hn(QB$XxXzlWEL@b~l+?O0yt=hnYY z_H0VtQcQ3itXQTgN9PYK4}U^YjMX%1XwEywbahEPPjcIna&5M0eLSSDN|6Xg3ZlKQ zBHXISINA3jjtehgd(WQN>^$X<;EKXR8}M4DC1qtci+?9u@+O`n@ptIzXv0(Zen-1OpMfex;USMGd1-*X%waX;l*sk+J>o=x?2|Q4J7w2KLnLBE;hCsj{9YOnZ2tUBlfYX61z57*&8#0$B4YOL?4V$ zu+{8V#Nk`6)$Fg+S!3>fsR?$rC5QH`C9u?m875EeCB$*e84gFg#JGtR`du9o6l9tu z_cDGl{uqZiT%vz)zQ#wCOYV4=5h2L=WfXsk?2nwi;)wagN@q(Y&tx++Et+|iqC29J z%hMcM#d}-yU!vc1S9dQj?LtM`WxUJ4#r85Bk7tQ-5pNb=XX78?h;xO?C-)2NWO+on zc$X0)$ky*Gc;>dUK63VoBj*wFZ`%1I$guZ9Bf7-uh~oPkYZO6I75qN2RsB-5;ajbb#L!sQa( zi|wIuJ>DHs7R+gLc}|U)OXP0EY|2Z>-I3wpY;4M3Bh0*EWvXNz8ciFbF3R_`y?5Ca z+~0&HSx9>!yC|%1j~<^V!gskaZ&9N%VSmdmvNPi!J zuTA0^cwYY5)O91N7QTD=?yB9DVqL7|(3N7c_Z8xb8=^mptiL1$mM?Wh6j4QXr$Tq! z@{~a)a5~1>t`TTVGLi>gJdn~g1HZ2xe)NaJ0 zi!F Date: Sun, 30 Jun 2019 11:46:31 +0400 Subject: [PATCH 252/259] docs: remove confusing statement from contributing.md (#3764) --- CONTRIBUTING.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ef992fee6..832156bda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -41,8 +41,6 @@ To pull in updates from the origin repo, run * `git fetch upstream` * `git rebase upstream/master` (or whatever branch you want) -Please don't make Pull Requests to `master`. - ## Dependencies We use [go modules](https://github.com/golang/go/wiki/Modules) to manage dependencies. From d4cf2040877aea2e863265a56fe1d91e93bf021a Mon Sep 17 00:00:00 2001 From: Marko Date: Sun, 30 Jun 2019 09:56:24 +0200 Subject: [PATCH 253/259] libs: remove commented and unneeded code (#3757) - libs/errors: commented out errors.go - libs/common: unused colors.go - libs/db: unused debugDB Signed-off-by: Marko Baricevic --- CHANGELOG_PENDING.md | 2 + libs/common/colors.go | 95 ---------------- libs/db/db_test.go | 3 +- libs/db/debug_db.go | 257 ------------------------------------------ libs/errors/errors.go | 21 ---- 5 files changed, 3 insertions(+), 375 deletions(-) delete mode 100644 libs/common/colors.go delete mode 100644 libs/db/debug_db.go delete mode 100644 libs/errors/errors.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 45d1c7899..d57446a10 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -14,6 +14,8 @@ program](https://hackerone.com/tendermint). * Apps * Go API + - [libs] Remove unused `db/debugDB` and `common/colors.go` & `errors/errors.go` files (@marbar3778) + * Blockchain Protocol diff --git a/libs/common/colors.go b/libs/common/colors.go deleted file mode 100644 index 89dda2c97..000000000 --- a/libs/common/colors.go +++ /dev/null @@ -1,95 +0,0 @@ -package common - -import ( - "fmt" - "strings" -) - -const ( - ANSIReset = "\x1b[0m" - ANSIBright = "\x1b[1m" - ANSIDim = "\x1b[2m" - ANSIUnderscore = "\x1b[4m" - ANSIBlink = "\x1b[5m" - ANSIReverse = "\x1b[7m" - ANSIHidden = "\x1b[8m" - - ANSIFgBlack = "\x1b[30m" - ANSIFgRed = "\x1b[31m" - ANSIFgGreen = "\x1b[32m" - ANSIFgYellow = "\x1b[33m" - ANSIFgBlue = "\x1b[34m" - ANSIFgMagenta = "\x1b[35m" - ANSIFgCyan = "\x1b[36m" - ANSIFgWhite = "\x1b[37m" - - ANSIBgBlack = "\x1b[40m" - ANSIBgRed = "\x1b[41m" - ANSIBgGreen = "\x1b[42m" - ANSIBgYellow = "\x1b[43m" - ANSIBgBlue = "\x1b[44m" - ANSIBgMagenta = "\x1b[45m" - ANSIBgCyan = "\x1b[46m" - ANSIBgWhite = "\x1b[47m" -) - -// color the string s with color 'color' -// unless s is already colored -func treat(s string, color string) string { - if len(s) > 2 && s[:2] == "\x1b[" { - return s - } - return color + s + ANSIReset -} - -func treatAll(color string, args ...interface{}) string { - parts := make([]string, 0, len(args)) - for _, arg := range args { - parts = append(parts, treat(fmt.Sprintf("%v", arg), color)) - } - return strings.Join(parts, "") -} - -func Black(args ...interface{}) string { - return treatAll(ANSIFgBlack, args...) -} - -func Red(args ...interface{}) string { - return treatAll(ANSIFgRed, args...) -} - -func Green(args ...interface{}) string { - return treatAll(ANSIFgGreen, args...) -} - -func Yellow(args ...interface{}) string { - return treatAll(ANSIFgYellow, args...) -} - -func Blue(args ...interface{}) string { - return treatAll(ANSIFgBlue, args...) -} - -func Magenta(args ...interface{}) string { - return treatAll(ANSIFgMagenta, args...) -} - -func Cyan(args ...interface{}) string { - return treatAll(ANSIFgCyan, args...) -} - -func White(args ...interface{}) string { - return treatAll(ANSIFgWhite, args...) -} - -func ColoredBytes(data []byte, textColor, bytesColor func(...interface{}) string) string { - s := "" - for _, b := range data { - if 0x21 <= b && b < 0x7F { - s += textColor(string(b)) - } else { - s += bytesColor(fmt.Sprintf("%02X", b)) - } - } - return s -} diff --git a/libs/db/db_test.go b/libs/db/db_test.go index 7cb721b26..22b781f95 100644 --- a/libs/db/db_test.go +++ b/libs/db/db_test.go @@ -182,8 +182,7 @@ func TestDBBatchWrite(t *testing.T) { for i, tc := range testCases { mdb := newMockDB() - ddb := NewDebugDB(t.Name(), mdb) - batch := ddb.NewBatch() + batch := mdb.NewBatch() tc.modify(batch) diff --git a/libs/db/debug_db.go b/libs/db/debug_db.go deleted file mode 100644 index 658cd0555..000000000 --- a/libs/db/debug_db.go +++ /dev/null @@ -1,257 +0,0 @@ -package db - -import ( - "fmt" - "sync" - - cmn "github.com/tendermint/tendermint/libs/common" -) - -//---------------------------------------- -// debugDB - -type debugDB struct { - label string - db DB -} - -// For printing all operationgs to the console for debugging. -func NewDebugDB(label string, db DB) debugDB { - return debugDB{ - label: label, - db: db, - } -} - -// Implements atomicSetDeleter. -func (ddb debugDB) Mutex() *sync.Mutex { return nil } - -// Implements DB. -func (ddb debugDB) Get(key []byte) (value []byte) { - defer func() { - fmt.Printf("%v.Get(%v) %v\n", ddb.label, - cmn.ColoredBytes(key, cmn.Cyan, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - }() - value = ddb.db.Get(key) - return -} - -// Implements DB. -func (ddb debugDB) Has(key []byte) (has bool) { - defer func() { - fmt.Printf("%v.Has(%v) %v\n", ddb.label, - cmn.ColoredBytes(key, cmn.Cyan, cmn.Blue), has) - }() - return ddb.db.Has(key) -} - -// Implements DB. -func (ddb debugDB) Set(key []byte, value []byte) { - fmt.Printf("%v.Set(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.Set(key, value) -} - -// Implements DB. -func (ddb debugDB) SetSync(key []byte, value []byte) { - fmt.Printf("%v.SetSync(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.SetSync(key, value) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) SetNoLock(key []byte, value []byte) { - fmt.Printf("%v.SetNoLock(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.(atomicSetDeleter).SetNoLock(key, value) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) SetNoLockSync(key []byte, value []byte) { - fmt.Printf("%v.SetNoLockSync(%v, %v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - ddb.db.(atomicSetDeleter).SetNoLockSync(key, value) -} - -// Implements DB. -func (ddb debugDB) Delete(key []byte) { - fmt.Printf("%v.Delete(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.Delete(key) -} - -// Implements DB. -func (ddb debugDB) DeleteSync(key []byte) { - fmt.Printf("%v.DeleteSync(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.DeleteSync(key) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) DeleteNoLock(key []byte) { - fmt.Printf("%v.DeleteNoLock(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.(atomicSetDeleter).DeleteNoLock(key) -} - -// Implements atomicSetDeleter. -func (ddb debugDB) DeleteNoLockSync(key []byte) { - fmt.Printf("%v.DeleteNoLockSync(%v)\n", ddb.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - ddb.db.(atomicSetDeleter).DeleteNoLockSync(key) -} - -// Implements DB. -func (ddb debugDB) Iterator(start, end []byte) Iterator { - fmt.Printf("%v.Iterator(%v, %v)\n", ddb.label, - cmn.ColoredBytes(start, cmn.Cyan, cmn.Blue), - cmn.ColoredBytes(end, cmn.Cyan, cmn.Blue)) - return NewDebugIterator(ddb.label, ddb.db.Iterator(start, end)) -} - -// Implements DB. -func (ddb debugDB) ReverseIterator(start, end []byte) Iterator { - fmt.Printf("%v.ReverseIterator(%v, %v)\n", ddb.label, - cmn.ColoredBytes(start, cmn.Cyan, cmn.Blue), - cmn.ColoredBytes(end, cmn.Cyan, cmn.Blue)) - return NewDebugIterator(ddb.label, ddb.db.ReverseIterator(start, end)) -} - -// Implements DB. -// Panics if the underlying db is not an -// atomicSetDeleter. -func (ddb debugDB) NewBatch() Batch { - fmt.Printf("%v.NewBatch()\n", ddb.label) - return NewDebugBatch(ddb.label, ddb.db.NewBatch()) -} - -// Implements DB. -func (ddb debugDB) Close() { - fmt.Printf("%v.Close()\n", ddb.label) - ddb.db.Close() -} - -// Implements DB. -func (ddb debugDB) Print() { - ddb.db.Print() -} - -// Implements DB. -func (ddb debugDB) Stats() map[string]string { - return ddb.db.Stats() -} - -//---------------------------------------- -// debugIterator - -type debugIterator struct { - label string - itr Iterator -} - -// For printing all operationgs to the console for debugging. -func NewDebugIterator(label string, itr Iterator) debugIterator { - return debugIterator{ - label: label, - itr: itr, - } -} - -// Implements Iterator. -func (ditr debugIterator) Domain() (start []byte, end []byte) { - defer func() { - fmt.Printf("%v.itr.Domain() (%X,%X)\n", ditr.label, start, end) - }() - start, end = ditr.itr.Domain() - return -} - -// Implements Iterator. -func (ditr debugIterator) Valid() (ok bool) { - defer func() { - fmt.Printf("%v.itr.Valid() %v\n", ditr.label, ok) - }() - ok = ditr.itr.Valid() - return -} - -// Implements Iterator. -func (ditr debugIterator) Next() { - fmt.Printf("%v.itr.Next()\n", ditr.label) - ditr.itr.Next() -} - -// Implements Iterator. -func (ditr debugIterator) Key() (key []byte) { - key = ditr.itr.Key() - fmt.Printf("%v.itr.Key() %v\n", ditr.label, - cmn.ColoredBytes(key, cmn.Cyan, cmn.Blue)) - return -} - -// Implements Iterator. -func (ditr debugIterator) Value() (value []byte) { - value = ditr.itr.Value() - fmt.Printf("%v.itr.Value() %v\n", ditr.label, - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - return -} - -// Implements Iterator. -func (ditr debugIterator) Close() { - fmt.Printf("%v.itr.Close()\n", ditr.label) - ditr.itr.Close() -} - -//---------------------------------------- -// debugBatch - -type debugBatch struct { - label string - bch Batch -} - -// For printing all operationgs to the console for debugging. -func NewDebugBatch(label string, bch Batch) debugBatch { - return debugBatch{ - label: label, - bch: bch, - } -} - -// Implements Batch. -func (dbch debugBatch) Set(key, value []byte) { - fmt.Printf("%v.batch.Set(%v, %v)\n", dbch.label, - cmn.ColoredBytes(key, cmn.Yellow, cmn.Blue), - cmn.ColoredBytes(value, cmn.Green, cmn.Blue)) - dbch.bch.Set(key, value) -} - -// Implements Batch. -func (dbch debugBatch) Delete(key []byte) { - fmt.Printf("%v.batch.Delete(%v)\n", dbch.label, - cmn.ColoredBytes(key, cmn.Red, cmn.Yellow)) - dbch.bch.Delete(key) -} - -// Implements Batch. -func (dbch debugBatch) Write() { - fmt.Printf("%v.batch.Write()\n", dbch.label) - dbch.bch.Write() -} - -// Implements Batch. -func (dbch debugBatch) WriteSync() { - fmt.Printf("%v.batch.WriteSync()\n", dbch.label) - dbch.bch.WriteSync() -} - -// Implements Batch. -func (dbch debugBatch) Close() { - dbch.bch.Close() -} diff --git a/libs/errors/errors.go b/libs/errors/errors.go deleted file mode 100644 index a03382780..000000000 --- a/libs/errors/errors.go +++ /dev/null @@ -1,21 +0,0 @@ -// Package errors contains errors that are thrown across packages. -package errors - -// // ErrPermissionsChanged occurs if the file permission have changed since the file was created. -// type ErrPermissionsChanged struct { -// name string -// got, want os.FileMode -// } - -// func NewErrPermissionsChanged(name string, got, want os.FileMode) *ErrPermissionsChanged { -// return &ErrPermissionsChanged{name: name, got: got, want: want} -// } - -// func (e ErrPermissionsChanged) Error() string { -// return fmt.Sprintf( -// "file: [%v]\nexpected file permissions: %v, got: %v", -// e.name, -// e.want, -// e.got, -// ) -// } From d9481e3648450cb99e15c6a070c1fb69aa0c255b Mon Sep 17 00:00:00 2001 From: Ivan Kushmantsev Date: Mon, 1 Jul 2019 12:48:54 +0400 Subject: [PATCH 254/259] config: make possible to set absolute paths for TLS cert and key (#3765) --- CHANGELOG_PENDING.md | 1 + config/config.go | 18 ++++++++++++++---- config/config_test.go | 16 ++++++++++++++++ config/toml.go | 6 ++++-- docs/tendermint-core/configuration.md | 6 ++++-- 5 files changed, 39 insertions(+), 8 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d57446a10..d81e815f3 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -24,5 +24,6 @@ program](https://hackerone.com/tendermint). ### FEATURES: ### IMPROVEMENTS: + - [rpc] \#3700 Make possible to set absolute paths for TLS cert and key (@climber73) ### BUG FIXES: diff --git a/config/config.go b/config/config.go index aa25cff6b..32e37f3e1 100644 --- a/config/config.go +++ b/config/config.go @@ -351,7 +351,8 @@ type RPCConfig struct { // See https://github.com/tendermint/tendermint/issues/3435 TimeoutBroadcastTxCommit time.Duration `mapstructure:"timeout_broadcast_tx_commit"` - // The name of a file containing certificate that is used to create the HTTPS server. + // The path to a file containing certificate that is used to create the HTTPS server. + // Migth be either absolute path or path related to tendermint's config directory. // // If the certificate is signed by a certificate authority, // the certFile should be the concatenation of the server's certificate, any intermediates, @@ -360,7 +361,8 @@ type RPCConfig struct { // NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. TLSCertFile string `mapstructure:"tls_cert_file"` - // The name of a file containing matching private key that is used to create the HTTPS server. + // The path to a file containing matching private key that is used to create the HTTPS server. + // Migth be either absolute path or path related to tendermint's config directory. // // NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. TLSKeyFile string `mapstructure:"tls_key_file"` @@ -424,11 +426,19 @@ func (cfg *RPCConfig) IsCorsEnabled() bool { } func (cfg RPCConfig) KeyFile() string { - return rootify(filepath.Join(defaultConfigDir, cfg.TLSKeyFile), cfg.RootDir) + path := cfg.TLSKeyFile + if filepath.IsAbs(path) { + return path + } + return rootify(filepath.Join(defaultConfigDir, path), cfg.RootDir) } func (cfg RPCConfig) CertFile() string { - return rootify(filepath.Join(defaultConfigDir, cfg.TLSCertFile), cfg.RootDir) + path := cfg.TLSCertFile + if filepath.IsAbs(path) { + return path + } + return rootify(filepath.Join(defaultConfigDir, path), cfg.RootDir) } func (cfg RPCConfig) IsTLSEnabled() bool { diff --git a/config/config_test.go b/config/config_test.go index afdbed181..6f9e3783e 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -36,3 +36,19 @@ func TestConfigValidateBasic(t *testing.T) { cfg.Consensus.TimeoutPropose = -10 * time.Second assert.Error(t, cfg.ValidateBasic()) } + +func TestTLSConfiguration(t *testing.T) { + assert := assert.New(t) + cfg := DefaultConfig() + cfg.SetRoot("/home/user") + + cfg.RPC.TLSCertFile = "file.crt" + assert.Equal("/home/user/config/file.crt", cfg.RPC.CertFile()) + cfg.RPC.TLSKeyFile = "file.key" + assert.Equal("/home/user/config/file.key", cfg.RPC.KeyFile()) + + cfg.RPC.TLSCertFile = "/abs/path/to/file.crt" + assert.Equal("/abs/path/to/file.crt", cfg.RPC.CertFile()) + cfg.RPC.TLSKeyFile = "/abs/path/to/file.key" + assert.Equal("/abs/path/to/file.key", cfg.RPC.KeyFile()) +} diff --git a/config/toml.go b/config/toml.go index b1541c05a..09117a0fb 100644 --- a/config/toml.go +++ b/config/toml.go @@ -192,14 +192,16 @@ max_subscriptions_per_client = {{ .RPC.MaxSubscriptionsPerClient }} # See https://github.com/tendermint/tendermint/issues/3435 timeout_broadcast_tx_commit = "{{ .RPC.TimeoutBroadcastTxCommit }}" -# The name of a file containing certificate that is used to create the HTTPS server. +# The path to a file containing certificate that is used to create the HTTPS server. +# Migth be either absolute path or path related to tendermint's config directory. # If the certificate is signed by a certificate authority, # the certFile should be the concatenation of the server's certificate, any intermediates, # and the CA's certificate. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_cert_file = "{{ .RPC.TLSCertFile }}" -# The name of a file containing matching private key that is used to create the HTTPS server. +# The path to a file containing matching private key that is used to create the HTTPS server. +# Migth be either absolute path or path related to tendermint's config directory. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_key_file = "{{ .RPC.TLSKeyFile }}" diff --git a/docs/tendermint-core/configuration.md b/docs/tendermint-core/configuration.md index f24e76d6d..df05f7c5d 100644 --- a/docs/tendermint-core/configuration.md +++ b/docs/tendermint-core/configuration.md @@ -138,14 +138,16 @@ max_subscriptions_per_client = 5 # See https://github.com/tendermint/tendermint/issues/3435 timeout_broadcast_tx_commit = "10s" -# The name of a file containing certificate that is used to create the HTTPS server. +# The path to a file containing certificate that is used to create the HTTPS server. +# Migth be either absolute path or path related to tendermint's config directory. # If the certificate is signed by a certificate authority, # the certFile should be the concatenation of the server's certificate, any intermediates, # and the CA's certificate. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_cert_file = "" -# The name of a file containing matching private key that is used to create the HTTPS server. +# The path to a file containing matching private key that is used to create the HTTPS server. +# Migth be either absolute path or path related to tendermint's config directory. # NOTE: both tls_cert_file and tls_key_file must be present for Tendermint to create HTTPS server. Otherwise, HTTP server is run. tls_key_file = "" From 62f97a69e97262b5feb57b1c2498f0c1e0e297b3 Mon Sep 17 00:00:00 2001 From: Thane Thomson Date: Tue, 2 Jul 2019 10:14:53 -0400 Subject: [PATCH 255/259] abci: Refactor CheckTx to notify of recheck (#3744) As per #2127, this refactors the RequestCheckTx ProtoBuf struct to allow for a flag indicating whether a query is a recheck or not (and allows for possible future, more nuanced states). In order to pass this extended information through to the ABCI app, the proxy.AppConnMempool (and, for consistency, the proxy.AppConnConsensus) interface seems to need to be refactored along with abcicli.Client. And, as per this comment, I've made the following modification to the protobuf definition for the RequestCheckTx structure: enum CheckTxType { New = 0; Recheck = 1; } message RequestCheckTx { bytes tx = 1; CheckTxType type = 2; } * Refactor ABCI CheckTx to notify of recheck As per #2127, this refactors the `RequestCheckTx` ProtoBuf struct to allow for: 1. a flag indicating whether a query is a recheck or not (and allows for possible future, more nuanced states) 2. an `additional_data` bytes array to provide information for those more nuanced states. In order to pass this extended information through to the ABCI app, the `proxy.AppConnMempool` (and, for consistency, the `proxy.AppConnConsensus`) interface seems to need to be refactored. Commits: * Fix linting issue * Add CHANGELOG_PENDING entry * Remove extraneous explicit initialization * Update ABCI spec doc to include new CheckTx params * Rename method param for consistency * Rename CheckTxType enum values and remove additional_data param --- CHANGELOG_PENDING.md | 6 +- abci/client/client.go | 8 +- abci/client/grpc_client.go | 16 +- abci/client/local_client.go | 20 +- abci/client/socket_client.go | 16 +- abci/cmd/abci-cli/abci-cli.go | 4 +- abci/example/example_test.go | 2 +- abci/example/kvstore/kvstore_test.go | 4 +- abci/tests/server/client.go | 4 +- abci/tests/test_app/app.go | 2 +- abci/types/messages.go | 8 +- abci/types/types.pb.go | 448 +++++++++++++++------------ abci/types/types.proto | 6 + consensus/replay_test.go | 2 +- docs/spec/abci/abci.md | 6 + mempool/clist_mempool.go | 7 +- mempool/clist_mempool_test.go | 4 +- proxy/app_conn.go | 12 +- state/execution.go | 2 +- 19 files changed, 331 insertions(+), 246 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index d81e815f3..9c94c55e7 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -14,9 +14,13 @@ program](https://hackerone.com/tendermint). * Apps * Go API + - [abci] \#2127 ABCI / mempool: Add a "Recheck Tx" indicator. Breaks the ABCI + client interface (`abcicli.Client`) to allow for supplying the ABCI + `types.RequestCheckTx` and `types.RequestDeliverTx` structs, and lets the + mempool indicate to the ABCI app whether a CheckTx request is a recheck or + not. - [libs] Remove unused `db/debugDB` and `common/colors.go` & `errors/errors.go` files (@marbar3778) - * Blockchain Protocol * P2P Protocol diff --git a/abci/client/client.go b/abci/client/client.go index e1eea5d4e..5f5ad7860 100644 --- a/abci/client/client.go +++ b/abci/client/client.go @@ -28,8 +28,8 @@ type Client interface { EchoAsync(msg string) *ReqRes InfoAsync(types.RequestInfo) *ReqRes SetOptionAsync(types.RequestSetOption) *ReqRes - DeliverTxAsync(tx []byte) *ReqRes - CheckTxAsync(tx []byte) *ReqRes + DeliverTxAsync(types.RequestDeliverTx) *ReqRes + CheckTxAsync(types.RequestCheckTx) *ReqRes QueryAsync(types.RequestQuery) *ReqRes CommitAsync() *ReqRes InitChainAsync(types.RequestInitChain) *ReqRes @@ -40,8 +40,8 @@ type Client interface { EchoSync(msg string) (*types.ResponseEcho, error) InfoSync(types.RequestInfo) (*types.ResponseInfo, error) SetOptionSync(types.RequestSetOption) (*types.ResponseSetOption, error) - DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) - CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) + DeliverTxSync(types.RequestDeliverTx) (*types.ResponseDeliverTx, error) + CheckTxSync(types.RequestCheckTx) (*types.ResponseCheckTx, error) QuerySync(types.RequestQuery) (*types.ResponseQuery, error) CommitSync() (*types.ResponseCommit, error) InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error) diff --git a/abci/client/grpc_client.go b/abci/client/grpc_client.go index d04f42b66..23d790550 100644 --- a/abci/client/grpc_client.go +++ b/abci/client/grpc_client.go @@ -159,8 +159,8 @@ func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes { return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{SetOption: res}}) } -func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes { - req := types.ToRequestDeliverTx(tx) +func (cli *grpcClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes { + req := types.ToRequestDeliverTx(params) res, err := cli.client.DeliverTx(context.Background(), req.GetDeliverTx(), grpc.FailFast(true)) if err != nil { cli.StopForError(err) @@ -168,8 +168,8 @@ func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes { return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{DeliverTx: res}}) } -func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes { - req := types.ToRequestCheckTx(tx) +func (cli *grpcClient) CheckTxAsync(params types.RequestCheckTx) *ReqRes { + req := types.ToRequestCheckTx(params) res, err := cli.client.CheckTx(context.Background(), req.GetCheckTx(), grpc.FailFast(true)) if err != nil { cli.StopForError(err) @@ -265,13 +265,13 @@ func (cli *grpcClient) SetOptionSync(req types.RequestSetOption) (*types.Respons return reqres.Response.GetSetOption(), cli.Error() } -func (cli *grpcClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) { - reqres := cli.DeliverTxAsync(tx) +func (cli *grpcClient) DeliverTxSync(params types.RequestDeliverTx) (*types.ResponseDeliverTx, error) { + reqres := cli.DeliverTxAsync(params) return reqres.Response.GetDeliverTx(), cli.Error() } -func (cli *grpcClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { - reqres := cli.CheckTxAsync(tx) +func (cli *grpcClient) CheckTxSync(params types.RequestCheckTx) (*types.ResponseCheckTx, error) { + reqres := cli.CheckTxAsync(params) return reqres.Response.GetCheckTx(), cli.Error() } diff --git a/abci/client/local_client.go b/abci/client/local_client.go index 4a3e6a5ee..bb009173a 100644 --- a/abci/client/local_client.go +++ b/abci/client/local_client.go @@ -81,24 +81,24 @@ func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes { ) } -func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes { +func (app *localClient) DeliverTxAsync(params types.RequestDeliverTx) *ReqRes { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.DeliverTx(types.RequestDeliverTx{Tx: tx}) + res := app.Application.DeliverTx(params) return app.callback( - types.ToRequestDeliverTx(tx), + types.ToRequestDeliverTx(params), types.ToResponseDeliverTx(res), ) } -func (app *localClient) CheckTxAsync(tx []byte) *ReqRes { +func (app *localClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.CheckTx(types.RequestCheckTx{Tx: tx}) + res := app.Application.CheckTx(req) return app.callback( - types.ToRequestCheckTx(tx), + types.ToRequestCheckTx(req), types.ToResponseCheckTx(res), ) } @@ -184,19 +184,19 @@ func (app *localClient) SetOptionSync(req types.RequestSetOption) (*types.Respon return &res, nil } -func (app *localClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) { +func (app *localClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.DeliverTx(types.RequestDeliverTx{Tx: tx}) + res := app.Application.DeliverTx(req) return &res, nil } -func (app *localClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { +func (app *localClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) { app.mtx.Lock() defer app.mtx.Unlock() - res := app.Application.CheckTx(types.RequestCheckTx{Tx: tx}) + res := app.Application.CheckTx(req) return &res, nil } diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index 3b401bd3c..16e39bf35 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -243,12 +243,12 @@ func (cli *socketClient) SetOptionAsync(req types.RequestSetOption) *ReqRes { return cli.queueRequest(types.ToRequestSetOption(req)) } -func (cli *socketClient) DeliverTxAsync(tx []byte) *ReqRes { - return cli.queueRequest(types.ToRequestDeliverTx(tx)) +func (cli *socketClient) DeliverTxAsync(req types.RequestDeliverTx) *ReqRes { + return cli.queueRequest(types.ToRequestDeliverTx(req)) } -func (cli *socketClient) CheckTxAsync(tx []byte) *ReqRes { - return cli.queueRequest(types.ToRequestCheckTx(tx)) +func (cli *socketClient) CheckTxAsync(req types.RequestCheckTx) *ReqRes { + return cli.queueRequest(types.ToRequestCheckTx(req)) } func (cli *socketClient) QueryAsync(req types.RequestQuery) *ReqRes { @@ -300,14 +300,14 @@ func (cli *socketClient) SetOptionSync(req types.RequestSetOption) (*types.Respo return reqres.Response.GetSetOption(), cli.Error() } -func (cli *socketClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) { - reqres := cli.queueRequest(types.ToRequestDeliverTx(tx)) +func (cli *socketClient) DeliverTxSync(req types.RequestDeliverTx) (*types.ResponseDeliverTx, error) { + reqres := cli.queueRequest(types.ToRequestDeliverTx(req)) cli.FlushSync() return reqres.Response.GetDeliverTx(), cli.Error() } -func (cli *socketClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) { - reqres := cli.queueRequest(types.ToRequestCheckTx(tx)) +func (cli *socketClient) CheckTxSync(req types.RequestCheckTx) (*types.ResponseCheckTx, error) { + reqres := cli.queueRequest(types.ToRequestCheckTx(req)) cli.FlushSync() return reqres.Response.GetCheckTx(), cli.Error() } diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index 7e55569cb..cd0a6fd1f 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -546,7 +546,7 @@ func cmdDeliverTx(cmd *cobra.Command, args []string) error { if err != nil { return err } - res, err := client.DeliverTxSync(txBytes) + res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes}) if err != nil { return err } @@ -572,7 +572,7 @@ func cmdCheckTx(cmd *cobra.Command, args []string) error { if err != nil { return err } - res, err := client.CheckTxSync(txBytes) + res, err := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes}) if err != nil { return err } diff --git a/abci/example/example_test.go b/abci/example/example_test.go index 677a2a481..6282f3a44 100644 --- a/abci/example/example_test.go +++ b/abci/example/example_test.go @@ -87,7 +87,7 @@ func testStream(t *testing.T, app types.Application) { // Write requests for counter := 0; counter < numDeliverTxs; counter++ { // Send request - reqRes := client.DeliverTxAsync([]byte("test")) + reqRes := client.DeliverTxAsync(types.RequestDeliverTx{Tx: []byte("test")}) _ = reqRes // check err ? diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go index 074baa49f..1649d3e84 100644 --- a/abci/example/kvstore/kvstore_test.go +++ b/abci/example/kvstore/kvstore_test.go @@ -283,11 +283,11 @@ func runClientTests(t *testing.T, client abcicli.Client) { } func testClient(t *testing.T, app abcicli.Client, tx []byte, key, value string) { - ar, err := app.DeliverTxSync(tx) + ar, err := app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) require.NoError(t, err) require.False(t, ar.IsErr(), ar) // repeating tx doesn't raise error - ar, err = app.DeliverTxSync(tx) + ar, err = app.DeliverTxSync(types.RequestDeliverTx{Tx: tx}) require.NoError(t, err) require.False(t, ar.IsErr(), ar) diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index 5daa1e6af..58a413a4e 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -58,7 +58,7 @@ func Commit(client abcicli.Client, hashExp []byte) error { } func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error { - res, _ := client.DeliverTxSync(txBytes) + res, _ := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes}) code, data, log := res.Code, res.Data, res.Log if code != codeExp { fmt.Println("Failed test: DeliverTx") @@ -77,7 +77,7 @@ func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp [] } func CheckTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) error { - res, _ := client.CheckTxSync(txBytes) + res, _ := client.CheckTxSync(types.RequestCheckTx{Tx: txBytes}) code, data, log := res.Code, res.Data, res.Log if code != codeExp { fmt.Println("Failed test: CheckTx") diff --git a/abci/tests/test_app/app.go b/abci/tests/test_app/app.go index 25ed2f582..9c32fcc7d 100644 --- a/abci/tests/test_app/app.go +++ b/abci/tests/test_app/app.go @@ -43,7 +43,7 @@ func commit(client abcicli.Client, hashExp []byte) { } func deliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []byte) { - res, err := client.DeliverTxSync(txBytes) + res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: txBytes}) if err != nil { panicf("client error: %v", err) } diff --git a/abci/types/messages.go b/abci/types/messages.go index cb64a15d6..ad18727a8 100644 --- a/abci/types/messages.go +++ b/abci/types/messages.go @@ -93,15 +93,15 @@ func ToRequestSetOption(req RequestSetOption) *Request { } } -func ToRequestDeliverTx(tx []byte) *Request { +func ToRequestDeliverTx(req RequestDeliverTx) *Request { return &Request{ - Value: &Request_DeliverTx{&RequestDeliverTx{Tx: tx}}, + Value: &Request_DeliverTx{&req}, } } -func ToRequestCheckTx(tx []byte) *Request { +func ToRequestCheckTx(req RequestCheckTx) *Request { return &Request{ - Value: &Request_CheckTx{&RequestCheckTx{Tx: tx}}, + Value: &Request_CheckTx{&req}, } } diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index a7455b523..926d528ad 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -38,6 +38,29 @@ var _ = time.Kitchen // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package +type CheckTxType int32 + +const ( + CheckTxType_New CheckTxType = 0 + CheckTxType_Recheck CheckTxType = 1 +) + +var CheckTxType_name = map[int32]string{ + 0: "New", + 1: "Recheck", +} +var CheckTxType_value = map[string]int32{ + "New": 0, + "Recheck": 1, +} + +func (x CheckTxType) String() string { + return proto.EnumName(CheckTxType_name, int32(x)) +} +func (CheckTxType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_types_30d8160a6576aafe, []int{0} +} + type Request struct { // Types that are valid to be assigned to Value: // *Request_Echo @@ -61,7 +84,7 @@ func (m *Request) Reset() { *m = Request{} } func (m *Request) String() string { return proto.CompactTextString(m) } func (*Request) ProtoMessage() {} func (*Request) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{0} + return fileDescriptor_types_30d8160a6576aafe, []int{0} } func (m *Request) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -483,7 +506,7 @@ func (m *RequestEcho) Reset() { *m = RequestEcho{} } func (m *RequestEcho) String() string { return proto.CompactTextString(m) } func (*RequestEcho) ProtoMessage() {} func (*RequestEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{1} + return fileDescriptor_types_30d8160a6576aafe, []int{1} } func (m *RequestEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -529,7 +552,7 @@ func (m *RequestFlush) Reset() { *m = RequestFlush{} } func (m *RequestFlush) String() string { return proto.CompactTextString(m) } func (*RequestFlush) ProtoMessage() {} func (*RequestFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{2} + return fileDescriptor_types_30d8160a6576aafe, []int{2} } func (m *RequestFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -571,7 +594,7 @@ func (m *RequestInfo) Reset() { *m = RequestInfo{} } func (m *RequestInfo) String() string { return proto.CompactTextString(m) } func (*RequestInfo) ProtoMessage() {} func (*RequestInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{3} + return fileDescriptor_types_30d8160a6576aafe, []int{3} } func (m *RequestInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -634,7 +657,7 @@ func (m *RequestSetOption) Reset() { *m = RequestSetOption{} } func (m *RequestSetOption) String() string { return proto.CompactTextString(m) } func (*RequestSetOption) ProtoMessage() {} func (*RequestSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{4} + return fileDescriptor_types_30d8160a6576aafe, []int{4} } func (m *RequestSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -692,7 +715,7 @@ func (m *RequestInitChain) Reset() { *m = RequestInitChain{} } func (m *RequestInitChain) String() string { return proto.CompactTextString(m) } func (*RequestInitChain) ProtoMessage() {} func (*RequestInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{5} + return fileDescriptor_types_30d8160a6576aafe, []int{5} } func (m *RequestInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -770,7 +793,7 @@ func (m *RequestQuery) Reset() { *m = RequestQuery{} } func (m *RequestQuery) String() string { return proto.CompactTextString(m) } func (*RequestQuery) ProtoMessage() {} func (*RequestQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{6} + return fileDescriptor_types_30d8160a6576aafe, []int{6} } func (m *RequestQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -841,7 +864,7 @@ func (m *RequestBeginBlock) Reset() { *m = RequestBeginBlock{} } func (m *RequestBeginBlock) String() string { return proto.CompactTextString(m) } func (*RequestBeginBlock) ProtoMessage() {} func (*RequestBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{7} + return fileDescriptor_types_30d8160a6576aafe, []int{7} } func (m *RequestBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -899,17 +922,18 @@ func (m *RequestBeginBlock) GetByzantineValidators() []Evidence { } type RequestCheckTx struct { - Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` + Type CheckTxType `protobuf:"varint,2,opt,name=type,proto3,enum=types.CheckTxType" json:"type,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *RequestCheckTx) Reset() { *m = RequestCheckTx{} } func (m *RequestCheckTx) String() string { return proto.CompactTextString(m) } func (*RequestCheckTx) ProtoMessage() {} func (*RequestCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{8} + return fileDescriptor_types_30d8160a6576aafe, []int{8} } func (m *RequestCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -945,6 +969,13 @@ func (m *RequestCheckTx) GetTx() []byte { return nil } +func (m *RequestCheckTx) GetType() CheckTxType { + if m != nil { + return m.Type + } + return CheckTxType_New +} + type RequestDeliverTx struct { Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` @@ -956,7 +987,7 @@ func (m *RequestDeliverTx) Reset() { *m = RequestDeliverTx{} } func (m *RequestDeliverTx) String() string { return proto.CompactTextString(m) } func (*RequestDeliverTx) ProtoMessage() {} func (*RequestDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{9} + return fileDescriptor_types_30d8160a6576aafe, []int{9} } func (m *RequestDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1003,7 +1034,7 @@ func (m *RequestEndBlock) Reset() { *m = RequestEndBlock{} } func (m *RequestEndBlock) String() string { return proto.CompactTextString(m) } func (*RequestEndBlock) ProtoMessage() {} func (*RequestEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{10} + return fileDescriptor_types_30d8160a6576aafe, []int{10} } func (m *RequestEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1049,7 +1080,7 @@ func (m *RequestCommit) Reset() { *m = RequestCommit{} } func (m *RequestCommit) String() string { return proto.CompactTextString(m) } func (*RequestCommit) ProtoMessage() {} func (*RequestCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{11} + return fileDescriptor_types_30d8160a6576aafe, []int{11} } func (m *RequestCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1102,7 +1133,7 @@ func (m *Response) Reset() { *m = Response{} } func (m *Response) String() string { return proto.CompactTextString(m) } func (*Response) ProtoMessage() {} func (*Response) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{12} + return fileDescriptor_types_30d8160a6576aafe, []int{12} } func (m *Response) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1555,7 +1586,7 @@ func (m *ResponseException) Reset() { *m = ResponseException{} } func (m *ResponseException) String() string { return proto.CompactTextString(m) } func (*ResponseException) ProtoMessage() {} func (*ResponseException) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{13} + return fileDescriptor_types_30d8160a6576aafe, []int{13} } func (m *ResponseException) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1602,7 +1633,7 @@ func (m *ResponseEcho) Reset() { *m = ResponseEcho{} } func (m *ResponseEcho) String() string { return proto.CompactTextString(m) } func (*ResponseEcho) ProtoMessage() {} func (*ResponseEcho) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{14} + return fileDescriptor_types_30d8160a6576aafe, []int{14} } func (m *ResponseEcho) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1648,7 +1679,7 @@ func (m *ResponseFlush) Reset() { *m = ResponseFlush{} } func (m *ResponseFlush) String() string { return proto.CompactTextString(m) } func (*ResponseFlush) ProtoMessage() {} func (*ResponseFlush) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{15} + return fileDescriptor_types_30d8160a6576aafe, []int{15} } func (m *ResponseFlush) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1692,7 +1723,7 @@ func (m *ResponseInfo) Reset() { *m = ResponseInfo{} } func (m *ResponseInfo) String() string { return proto.CompactTextString(m) } func (*ResponseInfo) ProtoMessage() {} func (*ResponseInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{16} + return fileDescriptor_types_30d8160a6576aafe, []int{16} } func (m *ResponseInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1771,7 +1802,7 @@ func (m *ResponseSetOption) Reset() { *m = ResponseSetOption{} } func (m *ResponseSetOption) String() string { return proto.CompactTextString(m) } func (*ResponseSetOption) ProtoMessage() {} func (*ResponseSetOption) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{17} + return fileDescriptor_types_30d8160a6576aafe, []int{17} } func (m *ResponseSetOption) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1833,7 +1864,7 @@ func (m *ResponseInitChain) Reset() { *m = ResponseInitChain{} } func (m *ResponseInitChain) String() string { return proto.CompactTextString(m) } func (*ResponseInitChain) ProtoMessage() {} func (*ResponseInitChain) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{18} + return fileDescriptor_types_30d8160a6576aafe, []int{18} } func (m *ResponseInitChain) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1896,7 +1927,7 @@ func (m *ResponseQuery) Reset() { *m = ResponseQuery{} } func (m *ResponseQuery) String() string { return proto.CompactTextString(m) } func (*ResponseQuery) ProtoMessage() {} func (*ResponseQuery) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{19} + return fileDescriptor_types_30d8160a6576aafe, []int{19} } func (m *ResponseQuery) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1999,7 +2030,7 @@ func (m *ResponseBeginBlock) Reset() { *m = ResponseBeginBlock{} } func (m *ResponseBeginBlock) String() string { return proto.CompactTextString(m) } func (*ResponseBeginBlock) ProtoMessage() {} func (*ResponseBeginBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{20} + return fileDescriptor_types_30d8160a6576aafe, []int{20} } func (m *ResponseBeginBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2053,7 +2084,7 @@ func (m *ResponseCheckTx) Reset() { *m = ResponseCheckTx{} } func (m *ResponseCheckTx) String() string { return proto.CompactTextString(m) } func (*ResponseCheckTx) ProtoMessage() {} func (*ResponseCheckTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{21} + return fileDescriptor_types_30d8160a6576aafe, []int{21} } func (m *ResponseCheckTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2156,7 +2187,7 @@ func (m *ResponseDeliverTx) Reset() { *m = ResponseDeliverTx{} } func (m *ResponseDeliverTx) String() string { return proto.CompactTextString(m) } func (*ResponseDeliverTx) ProtoMessage() {} func (*ResponseDeliverTx) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{22} + return fileDescriptor_types_30d8160a6576aafe, []int{22} } func (m *ResponseDeliverTx) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2254,7 +2285,7 @@ func (m *ResponseEndBlock) Reset() { *m = ResponseEndBlock{} } func (m *ResponseEndBlock) String() string { return proto.CompactTextString(m) } func (*ResponseEndBlock) ProtoMessage() {} func (*ResponseEndBlock) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{23} + return fileDescriptor_types_30d8160a6576aafe, []int{23} } func (m *ResponseEndBlock) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2316,7 +2347,7 @@ func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } func (m *ResponseCommit) String() string { return proto.CompactTextString(m) } func (*ResponseCommit) ProtoMessage() {} func (*ResponseCommit) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{24} + return fileDescriptor_types_30d8160a6576aafe, []int{24} } func (m *ResponseCommit) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2367,7 +2398,7 @@ func (m *ConsensusParams) Reset() { *m = ConsensusParams{} } func (m *ConsensusParams) String() string { return proto.CompactTextString(m) } func (*ConsensusParams) ProtoMessage() {} func (*ConsensusParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{25} + return fileDescriptor_types_30d8160a6576aafe, []int{25} } func (m *ConsensusParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2417,7 +2448,7 @@ func (m *ConsensusParams) GetValidator() *ValidatorParams { return nil } -// BlockParams contains limits on the block size and timestamp. +// BlockParams contains limits on the block size. type BlockParams struct { // Note: must be greater than 0 MaxBytes int64 `protobuf:"varint,1,opt,name=max_bytes,json=maxBytes,proto3" json:"max_bytes,omitempty"` @@ -2432,7 +2463,7 @@ func (m *BlockParams) Reset() { *m = BlockParams{} } func (m *BlockParams) String() string { return proto.CompactTextString(m) } func (*BlockParams) ProtoMessage() {} func (*BlockParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{26} + return fileDescriptor_types_30d8160a6576aafe, []int{26} } func (m *BlockParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2488,7 +2519,7 @@ func (m *EvidenceParams) Reset() { *m = EvidenceParams{} } func (m *EvidenceParams) String() string { return proto.CompactTextString(m) } func (*EvidenceParams) ProtoMessage() {} func (*EvidenceParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{27} + return fileDescriptor_types_30d8160a6576aafe, []int{27} } func (m *EvidenceParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2536,7 +2567,7 @@ func (m *ValidatorParams) Reset() { *m = ValidatorParams{} } func (m *ValidatorParams) String() string { return proto.CompactTextString(m) } func (*ValidatorParams) ProtoMessage() {} func (*ValidatorParams) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{28} + return fileDescriptor_types_30d8160a6576aafe, []int{28} } func (m *ValidatorParams) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2584,7 +2615,7 @@ func (m *LastCommitInfo) Reset() { *m = LastCommitInfo{} } func (m *LastCommitInfo) String() string { return proto.CompactTextString(m) } func (*LastCommitInfo) ProtoMessage() {} func (*LastCommitInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{29} + return fileDescriptor_types_30d8160a6576aafe, []int{29} } func (m *LastCommitInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2639,7 +2670,7 @@ func (m *Event) Reset() { *m = Event{} } func (m *Event) String() string { return proto.CompactTextString(m) } func (*Event) ProtoMessage() {} func (*Event) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{30} + return fileDescriptor_types_30d8160a6576aafe, []int{30} } func (m *Event) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2713,7 +2744,7 @@ func (m *Header) Reset() { *m = Header{} } func (m *Header) String() string { return proto.CompactTextString(m) } func (*Header) ProtoMessage() {} func (*Header) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{31} + return fileDescriptor_types_30d8160a6576aafe, []int{31} } func (m *Header) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2866,7 +2897,7 @@ func (m *Version) Reset() { *m = Version{} } func (m *Version) String() string { return proto.CompactTextString(m) } func (*Version) ProtoMessage() {} func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{32} + return fileDescriptor_types_30d8160a6576aafe, []int{32} } func (m *Version) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2921,7 +2952,7 @@ func (m *BlockID) Reset() { *m = BlockID{} } func (m *BlockID) String() string { return proto.CompactTextString(m) } func (*BlockID) ProtoMessage() {} func (*BlockID) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{33} + return fileDescriptor_types_30d8160a6576aafe, []int{33} } func (m *BlockID) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2976,7 +3007,7 @@ func (m *PartSetHeader) Reset() { *m = PartSetHeader{} } func (m *PartSetHeader) String() string { return proto.CompactTextString(m) } func (*PartSetHeader) ProtoMessage() {} func (*PartSetHeader) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{34} + return fileDescriptor_types_30d8160a6576aafe, []int{34} } func (m *PartSetHeader) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3033,7 +3064,7 @@ func (m *Validator) Reset() { *m = Validator{} } func (m *Validator) String() string { return proto.CompactTextString(m) } func (*Validator) ProtoMessage() {} func (*Validator) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{35} + return fileDescriptor_types_30d8160a6576aafe, []int{35} } func (m *Validator) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3089,7 +3120,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} } func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) } func (*ValidatorUpdate) ProtoMessage() {} func (*ValidatorUpdate) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{36} + return fileDescriptor_types_30d8160a6576aafe, []int{36} } func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3145,7 +3176,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} } func (m *VoteInfo) String() string { return proto.CompactTextString(m) } func (*VoteInfo) ProtoMessage() {} func (*VoteInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{37} + return fileDescriptor_types_30d8160a6576aafe, []int{37} } func (m *VoteInfo) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3200,7 +3231,7 @@ func (m *PubKey) Reset() { *m = PubKey{} } func (m *PubKey) String() string { return proto.CompactTextString(m) } func (*PubKey) ProtoMessage() {} func (*PubKey) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{38} + return fileDescriptor_types_30d8160a6576aafe, []int{38} } func (m *PubKey) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3258,7 +3289,7 @@ func (m *Evidence) Reset() { *m = Evidence{} } func (m *Evidence) String() string { return proto.CompactTextString(m) } func (*Evidence) ProtoMessage() {} func (*Evidence) Descriptor() ([]byte, []int) { - return fileDescriptor_types_62f0c59aeb977f78, []int{39} + return fileDescriptor_types_30d8160a6576aafe, []int{39} } func (m *Evidence) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -3403,6 +3434,8 @@ func init() { golang_proto.RegisterType((*PubKey)(nil), "types.PubKey") proto.RegisterType((*Evidence)(nil), "types.Evidence") golang_proto.RegisterType((*Evidence)(nil), "types.Evidence") + proto.RegisterEnum("types.CheckTxType", CheckTxType_name, CheckTxType_value) + golang_proto.RegisterEnum("types.CheckTxType", CheckTxType_name, CheckTxType_value) } func (this *Request) Equal(that interface{}) bool { if that == nil { @@ -3958,6 +3991,9 @@ func (this *RequestCheckTx) Equal(that interface{}) bool { if !bytes.Equal(this.Tx, that1.Tx) { return false } + if this.Type != that1.Type { + return false + } if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { return false } @@ -6211,6 +6247,11 @@ func (m *RequestCheckTx) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintTypes(dAtA, i, uint64(len(m.Tx))) i += copy(dAtA[i:], m.Tx) } + if m.Type != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintTypes(dAtA, i, uint64(m.Type)) + } if m.XXX_unrecognized != nil { i += copy(dAtA[i:], m.XXX_unrecognized) } @@ -7872,8 +7913,9 @@ func NewPopulatedRequestCheckTx(r randyTypes, easy bool) *RequestCheckTx { for i := 0; i < v11; i++ { this.Tx[i] = byte(r.Intn(256)) } + this.Type = CheckTxType([]int32{0, 1}[r.Intn(2)]) if !easy && r.Intn(10) != 0 { - this.XXX_unrecognized = randUnrecognizedTypes(r, 2) + this.XXX_unrecognized = randUnrecognizedTypes(r, 3) } return this } @@ -8906,6 +8948,9 @@ func (m *RequestCheckTx) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } + if m.Type != 0 { + n += 1 + sovTypes(uint64(m.Type)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -11127,6 +11172,25 @@ func (m *RequestCheckTx) Unmarshal(dAtA []byte) error { m.Tx = []byte{} } iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTypes + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= (CheckTxType(b) & 0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) @@ -15638,152 +15702,154 @@ var ( ErrIntOverflowTypes = fmt.Errorf("proto: integer overflow") ) -func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_62f0c59aeb977f78) } +func init() { proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_30d8160a6576aafe) } func init() { - golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_62f0c59aeb977f78) + golang_proto.RegisterFile("abci/types/types.proto", fileDescriptor_types_30d8160a6576aafe) } -var fileDescriptor_types_62f0c59aeb977f78 = []byte{ - // 2241 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0x4b, 0x73, 0x1c, 0x49, - 0xf1, 0x57, 0xcf, 0xbb, 0x73, 0x34, 0x0f, 0x97, 0x65, 0x7b, 0x3c, 0x7f, 0xff, 0x25, 0x47, 0x1b, - 0x76, 0x25, 0xd6, 0x3b, 0xda, 0xd5, 0x62, 0x42, 0xc6, 0xcb, 0x46, 0x68, 0x6c, 0x83, 0x14, 0x6b, - 0x40, 0xb4, 0x6d, 0x71, 0x21, 0xa2, 0xa3, 0x66, 0xba, 0x3c, 0xd3, 0xe1, 0x99, 0xee, 0xde, 0xee, - 0x9a, 0xd9, 0x11, 0x47, 0xce, 0x7b, 0xd8, 0x03, 0x1f, 0x81, 0x03, 0x1f, 0x61, 0x8f, 0x9c, 0x88, - 0x3d, 0x72, 0xe0, 0x6c, 0x40, 0x04, 0x17, 0x22, 0x38, 0x03, 0x37, 0xa2, 0xb2, 0xaa, 0x9f, 0xea, - 0x31, 0xbb, 0x86, 0x1b, 0x17, 0xa9, 0xab, 0xf2, 0x97, 0xf5, 0xc8, 0xc9, 0xcc, 0x5f, 0x66, 0xc1, - 0x75, 0x3a, 0x1a, 0x3b, 0xfb, 0xfc, 0xdc, 0x67, 0xa1, 0xfc, 0x3b, 0xf0, 0x03, 0x8f, 0x7b, 0xa4, - 0x8a, 0x83, 0xfe, 0xbb, 0x13, 0x87, 0x4f, 0x17, 0xa3, 0xc1, 0xd8, 0x9b, 0xef, 0x4f, 0xbc, 0x89, - 0xb7, 0x8f, 0xd2, 0xd1, 0xe2, 0x05, 0x8e, 0x70, 0x80, 0x5f, 0x52, 0xab, 0xff, 0x20, 0x05, 0xe7, - 0xcc, 0xb5, 0x59, 0x30, 0x77, 0x5c, 0x9e, 0xfe, 0x1c, 0x07, 0xe7, 0x3e, 0xf7, 0xf6, 0xe7, 0x2c, - 0x78, 0x39, 0x63, 0xea, 0x9f, 0x52, 0x3e, 0xfc, 0xb7, 0xca, 0x33, 0x67, 0x14, 0xee, 0x8f, 0xbd, - 0xf9, 0xdc, 0x73, 0xd3, 0x87, 0xed, 0xef, 0x4c, 0x3c, 0x6f, 0x32, 0x63, 0xc9, 0xe1, 0xb8, 0x33, - 0x67, 0x21, 0xa7, 0x73, 0x5f, 0x02, 0x8c, 0xdf, 0x56, 0xa0, 0x6e, 0xb2, 0x4f, 0x16, 0x2c, 0xe4, - 0x64, 0x17, 0x2a, 0x6c, 0x3c, 0xf5, 0x7a, 0xa5, 0xdb, 0xda, 0x6e, 0xf3, 0x80, 0x0c, 0xe4, 0x42, - 0x4a, 0xfa, 0x78, 0x3c, 0xf5, 0x8e, 0x37, 0x4c, 0x44, 0x90, 0x77, 0xa0, 0xfa, 0x62, 0xb6, 0x08, - 0xa7, 0xbd, 0x32, 0x42, 0xaf, 0x66, 0xa1, 0xdf, 0x17, 0xa2, 0xe3, 0x0d, 0x53, 0x62, 0xc4, 0xb2, - 0x8e, 0xfb, 0xc2, 0xeb, 0x55, 0x8a, 0x96, 0x3d, 0x71, 0x5f, 0xe0, 0xb2, 0x02, 0x41, 0x0e, 0x01, - 0x42, 0xc6, 0x2d, 0xcf, 0xe7, 0x8e, 0xe7, 0xf6, 0xaa, 0x88, 0xbf, 0x91, 0xc5, 0x3f, 0x65, 0xfc, - 0xc7, 0x28, 0x3e, 0xde, 0x30, 0xf5, 0x30, 0x1a, 0x08, 0x4d, 0xc7, 0x75, 0xb8, 0x35, 0x9e, 0x52, - 0xc7, 0xed, 0xd5, 0x8a, 0x34, 0x4f, 0x5c, 0x87, 0x3f, 0x14, 0x62, 0xa1, 0xe9, 0x44, 0x03, 0x71, - 0x95, 0x4f, 0x16, 0x2c, 0x38, 0xef, 0xd5, 0x8b, 0xae, 0xf2, 0x13, 0x21, 0x12, 0x57, 0x41, 0x0c, - 0x79, 0x00, 0xcd, 0x11, 0x9b, 0x38, 0xae, 0x35, 0x9a, 0x79, 0xe3, 0x97, 0xbd, 0x06, 0xaa, 0xf4, - 0xb2, 0x2a, 0x43, 0x01, 0x18, 0x0a, 0xf9, 0xf1, 0x86, 0x09, 0xa3, 0x78, 0x44, 0x0e, 0xa0, 0x31, - 0x9e, 0xb2, 0xf1, 0x4b, 0x8b, 0xaf, 0x7a, 0x3a, 0x6a, 0x5e, 0xcb, 0x6a, 0x3e, 0x14, 0xd2, 0x67, - 0xab, 0xe3, 0x0d, 0xb3, 0x3e, 0x96, 0x9f, 0xe4, 0x1e, 0xe8, 0xcc, 0xb5, 0xd5, 0x76, 0x4d, 0x54, - 0xba, 0x9e, 0xfb, 0x5d, 0x5c, 0x3b, 0xda, 0xac, 0xc1, 0xd4, 0x37, 0x19, 0x40, 0x4d, 0x38, 0x83, - 0xc3, 0x7b, 0x9b, 0xa8, 0xb3, 0x95, 0xdb, 0x08, 0x65, 0xc7, 0x1b, 0xa6, 0x42, 0x09, 0xf3, 0xd9, - 0x6c, 0xe6, 0x2c, 0x59, 0x20, 0x0e, 0x77, 0xb5, 0xc8, 0x7c, 0x8f, 0xa4, 0x1c, 0x8f, 0xa7, 0xdb, - 0xd1, 0x60, 0x58, 0x87, 0xea, 0x92, 0xce, 0x16, 0xcc, 0x78, 0x1b, 0x9a, 0x29, 0x4f, 0x21, 0x3d, - 0xa8, 0xcf, 0x59, 0x18, 0xd2, 0x09, 0xeb, 0x69, 0xb7, 0xb5, 0x5d, 0xdd, 0x8c, 0x86, 0x46, 0x1b, - 0x36, 0xd3, 0x7e, 0x62, 0xcc, 0x63, 0x45, 0xe1, 0x0b, 0x42, 0x71, 0xc9, 0x82, 0x50, 0x38, 0x80, - 0x52, 0x54, 0x43, 0x72, 0x07, 0x5a, 0x68, 0x07, 0x2b, 0x92, 0x0b, 0x3f, 0xad, 0x98, 0x9b, 0x38, - 0x79, 0xa6, 0x40, 0x3b, 0xd0, 0xf4, 0x0f, 0xfc, 0x18, 0x52, 0x46, 0x08, 0xf8, 0x07, 0xbe, 0x02, - 0x18, 0xdf, 0x85, 0x6e, 0xde, 0x95, 0x48, 0x17, 0xca, 0x2f, 0xd9, 0xb9, 0xda, 0x4f, 0x7c, 0x92, - 0x2d, 0x75, 0x2d, 0xdc, 0x43, 0x37, 0xd5, 0x1d, 0x3f, 0x2f, 0xc5, 0xca, 0xb1, 0x37, 0x91, 0x43, - 0xa8, 0x88, 0xa0, 0x42, 0xed, 0xe6, 0x41, 0x7f, 0x20, 0x23, 0x6e, 0x10, 0x45, 0xdc, 0xe0, 0x59, - 0x14, 0x71, 0xc3, 0xc6, 0x97, 0xaf, 0x76, 0x36, 0x3e, 0xff, 0xc3, 0x8e, 0x66, 0xa2, 0x06, 0xb9, - 0x29, 0x1c, 0x82, 0x3a, 0xae, 0xe5, 0xd8, 0x6a, 0x9f, 0x3a, 0x8e, 0x4f, 0x6c, 0x72, 0x04, 0xdd, - 0xb1, 0xe7, 0x86, 0xcc, 0x0d, 0x17, 0xa1, 0xe5, 0xd3, 0x80, 0xce, 0x43, 0x15, 0x6b, 0xd1, 0xcf, - 0xff, 0x30, 0x12, 0x9f, 0xa2, 0xd4, 0xec, 0x8c, 0xb3, 0x13, 0xe4, 0x43, 0x80, 0x25, 0x9d, 0x39, - 0x36, 0xe5, 0x5e, 0x10, 0xf6, 0x2a, 0xb7, 0xcb, 0x29, 0xe5, 0xb3, 0x48, 0xf0, 0xdc, 0xb7, 0x29, - 0x67, 0xc3, 0x8a, 0x38, 0x99, 0x99, 0xc2, 0x93, 0xb7, 0xa0, 0x43, 0x7d, 0xdf, 0x0a, 0x39, 0xe5, - 0xcc, 0x1a, 0x9d, 0x73, 0x16, 0x62, 0x3c, 0x6e, 0x9a, 0x2d, 0xea, 0xfb, 0x4f, 0xc5, 0xec, 0x50, - 0x4c, 0x1a, 0x76, 0xfc, 0x6b, 0x62, 0xa8, 0x10, 0x02, 0x15, 0x9b, 0x72, 0x8a, 0xd6, 0xd8, 0x34, - 0xf1, 0x5b, 0xcc, 0xf9, 0x94, 0x4f, 0xd5, 0x1d, 0xf1, 0x9b, 0x5c, 0x87, 0xda, 0x94, 0x39, 0x93, - 0x29, 0xc7, 0x6b, 0x95, 0x4d, 0x35, 0x12, 0x86, 0xf7, 0x03, 0x6f, 0xc9, 0x30, 0x5b, 0x34, 0x4c, - 0x39, 0x30, 0xfe, 0xa2, 0xc1, 0x95, 0x4b, 0xe1, 0x25, 0xd6, 0x9d, 0xd2, 0x70, 0x1a, 0xed, 0x25, - 0xbe, 0xc9, 0x3b, 0x62, 0x5d, 0x6a, 0xb3, 0x40, 0x65, 0xb1, 0x96, 0xba, 0xf1, 0x31, 0x4e, 0xaa, - 0x8b, 0x2a, 0x08, 0x79, 0x0c, 0xdd, 0x19, 0x0d, 0xb9, 0x25, 0xa3, 0xc0, 0xc2, 0x2c, 0x55, 0xce, - 0x44, 0xe6, 0x13, 0x1a, 0x45, 0x8b, 0x70, 0x4e, 0xa5, 0xde, 0x9e, 0x65, 0x66, 0xc9, 0x31, 0x6c, - 0x8d, 0xce, 0x7f, 0x4e, 0x5d, 0xee, 0xb8, 0xcc, 0xba, 0x64, 0xf3, 0x8e, 0x5a, 0xea, 0xf1, 0xd2, - 0xb1, 0x99, 0x3b, 0x8e, 0x8c, 0x7d, 0x35, 0x56, 0x89, 0x7f, 0x8c, 0xd0, 0xb8, 0x0d, 0xed, 0x6c, - 0x2e, 0x20, 0x6d, 0x28, 0xf1, 0x95, 0xba, 0x61, 0x89, 0xaf, 0x0c, 0x23, 0xf6, 0xc0, 0x38, 0x20, - 0x2f, 0x61, 0xf6, 0xa0, 0x93, 0x4b, 0x0e, 0x29, 0x73, 0x6b, 0x69, 0x73, 0x1b, 0x1d, 0x68, 0x65, - 0x72, 0x82, 0xf1, 0x59, 0x15, 0x1a, 0x26, 0x0b, 0x7d, 0xe1, 0x4c, 0xe4, 0x10, 0x74, 0xb6, 0x1a, - 0x33, 0x99, 0x8e, 0xb5, 0x5c, 0xb2, 0x93, 0x98, 0xc7, 0x91, 0x5c, 0xa4, 0x85, 0x18, 0x4c, 0xf6, - 0x32, 0x54, 0x72, 0x35, 0xaf, 0x94, 0xe6, 0x92, 0xbb, 0x59, 0x2e, 0xd9, 0xca, 0x61, 0x73, 0x64, - 0xb2, 0x97, 0x21, 0x93, 0xfc, 0xc2, 0x19, 0x36, 0xb9, 0x5f, 0xc0, 0x26, 0xf9, 0xe3, 0xaf, 0xa1, - 0x93, 0xfb, 0x05, 0x74, 0xd2, 0xbb, 0xb4, 0x57, 0x21, 0x9f, 0xdc, 0xcd, 0xf2, 0x49, 0xfe, 0x3a, - 0x39, 0x42, 0xf9, 0xb0, 0x88, 0x50, 0x6e, 0xe6, 0x74, 0xd6, 0x32, 0xca, 0x07, 0x97, 0x18, 0xe5, - 0x7a, 0x4e, 0xb5, 0x80, 0x52, 0xee, 0x67, 0x72, 0x3d, 0x14, 0xde, 0xad, 0x38, 0xd9, 0x93, 0xef, - 0x5c, 0x66, 0xa3, 0x1b, 0xf9, 0x9f, 0xb6, 0x88, 0x8e, 0xf6, 0x73, 0x74, 0x74, 0x2d, 0x7f, 0xca, - 0x1c, 0x1f, 0x25, 0xac, 0xb2, 0x27, 0xe2, 0x3e, 0xe7, 0x69, 0x22, 0x47, 0xb0, 0x20, 0xf0, 0x02, - 0x95, 0xb0, 0xe5, 0xc0, 0xd8, 0x15, 0x99, 0x28, 0xf1, 0xaf, 0xd7, 0x30, 0x10, 0x3a, 0x7d, 0xca, - 0xbb, 0x8c, 0x2f, 0xb4, 0x44, 0x17, 0x23, 0x3a, 0x9d, 0xc5, 0x74, 0x95, 0xc5, 0x52, 0xc4, 0x54, - 0xca, 0x12, 0xd3, 0x0e, 0x34, 0x45, 0xae, 0xcc, 0x71, 0x0e, 0xf5, 0x23, 0xce, 0x21, 0xdf, 0x82, - 0x2b, 0x98, 0x67, 0x24, 0x7d, 0xa9, 0x40, 0xac, 0x60, 0x20, 0x76, 0x84, 0x40, 0x5a, 0x4c, 0x26, - 0xc0, 0x77, 0xe1, 0x6a, 0x0a, 0x2b, 0xd6, 0xc5, 0x1c, 0x27, 0x93, 0x6f, 0x37, 0x46, 0x1f, 0xf9, - 0xfe, 0x31, 0x0d, 0xa7, 0xc6, 0x0f, 0x13, 0x03, 0x25, 0x7c, 0x46, 0xa0, 0x32, 0xf6, 0x6c, 0x79, - 0xef, 0x96, 0x89, 0xdf, 0x82, 0xe3, 0x66, 0xde, 0x04, 0x0f, 0xa7, 0x9b, 0xe2, 0x53, 0xa0, 0xe2, - 0x50, 0xd2, 0x65, 0xcc, 0x18, 0xbf, 0xd4, 0x92, 0xf5, 0x12, 0x8a, 0x2b, 0x62, 0x23, 0xed, 0x3f, - 0x61, 0xa3, 0xd2, 0xd7, 0x63, 0x23, 0xe3, 0x42, 0x4b, 0x7e, 0xb2, 0x98, 0x67, 0xde, 0xec, 0x8a, - 0xc2, 0x7b, 0x1c, 0xd7, 0x66, 0x2b, 0x34, 0x69, 0xd9, 0x94, 0x83, 0xa8, 0x04, 0xa8, 0xa1, 0x99, - 0xb3, 0x25, 0x40, 0x1d, 0xe7, 0xe4, 0x80, 0xdc, 0x41, 0x7e, 0xf2, 0x5e, 0xa8, 0x50, 0x6d, 0x0d, - 0x54, 0xa1, 0x7e, 0x2a, 0x26, 0x4d, 0x29, 0x4b, 0x65, 0x5b, 0x3d, 0x43, 0x6e, 0xb7, 0x40, 0x17, - 0x07, 0x0d, 0x7d, 0x3a, 0x66, 0x18, 0x79, 0xba, 0x99, 0x4c, 0x18, 0xcf, 0x80, 0x5c, 0x8e, 0x78, - 0xf2, 0x11, 0xd4, 0xd8, 0x92, 0xb9, 0x5c, 0x58, 0x5c, 0x18, 0x6d, 0x33, 0xa6, 0x13, 0xe6, 0xf2, - 0x61, 0x4f, 0x98, 0xea, 0xaf, 0xaf, 0x76, 0xba, 0x12, 0x73, 0xd7, 0x9b, 0x3b, 0x9c, 0xcd, 0x7d, - 0x7e, 0x6e, 0x2a, 0x2d, 0xe3, 0xef, 0x9a, 0x60, 0x83, 0x4c, 0x36, 0x28, 0x34, 0x5e, 0xe4, 0xf2, - 0xa5, 0x14, 0x71, 0x7f, 0x35, 0x83, 0xfe, 0x3f, 0xc0, 0x84, 0x86, 0xd6, 0xa7, 0xd4, 0xe5, 0xcc, - 0x56, 0x56, 0xd5, 0x27, 0x34, 0xfc, 0x29, 0x4e, 0x88, 0x2a, 0x47, 0x88, 0x17, 0x21, 0xb3, 0xd1, - 0xbc, 0x65, 0xb3, 0x3e, 0xa1, 0xe1, 0xf3, 0x90, 0xd9, 0xa9, 0xbb, 0xd5, 0xdf, 0xe4, 0x6e, 0x59, - 0x7b, 0x36, 0xf2, 0xf6, 0xfc, 0x67, 0xca, 0x97, 0x13, 0xb2, 0xfc, 0xdf, 0xb8, 0xfb, 0xdf, 0x34, - 0x51, 0x27, 0x64, 0x53, 0x32, 0x39, 0x81, 0x2b, 0x71, 0x4c, 0x59, 0x0b, 0x8c, 0xb5, 0xc8, 0xab, - 0x5e, 0x1f, 0x8a, 0xdd, 0x65, 0x76, 0x3a, 0x24, 0x3f, 0x82, 0x1b, 0xb9, 0x8c, 0x10, 0x2f, 0x58, - 0x7a, 0x6d, 0x62, 0xb8, 0x96, 0x4d, 0x0c, 0xd1, 0x7a, 0x89, 0x35, 0xca, 0x6f, 0xe4, 0xe5, 0xdf, - 0x10, 0x85, 0x53, 0x9a, 0x4c, 0x8a, 0x7e, 0x53, 0xe3, 0x57, 0x1a, 0x74, 0x72, 0x07, 0x22, 0xbb, - 0x50, 0x95, 0x7c, 0xa6, 0x65, 0xda, 0x53, 0xb4, 0x98, 0x3a, 0xb3, 0x04, 0x90, 0xf7, 0xa1, 0xc1, - 0x54, 0x0d, 0xa7, 0x2e, 0x79, 0x2d, 0x57, 0xda, 0x29, 0x7c, 0x0c, 0x23, 0xdf, 0x06, 0x3d, 0x36, - 0x5d, 0xae, 0x7e, 0x8f, 0x2d, 0xad, 0x94, 0x12, 0xa0, 0xf1, 0x10, 0x9a, 0xa9, 0xed, 0xc9, 0xff, - 0x81, 0x3e, 0xa7, 0x2b, 0x55, 0x84, 0xcb, 0xf2, 0xad, 0x31, 0xa7, 0x2b, 0xac, 0xbf, 0xc9, 0x0d, - 0xa8, 0x0b, 0xe1, 0x84, 0x4a, 0xc3, 0x97, 0xcd, 0xda, 0x9c, 0xae, 0x7e, 0x40, 0x43, 0x63, 0x0f, - 0xda, 0xd9, 0x63, 0x45, 0xd0, 0x88, 0x10, 0x25, 0xf4, 0x68, 0xc2, 0x8c, 0x7b, 0xd0, 0xc9, 0x9d, - 0x86, 0x18, 0xd0, 0xf2, 0x17, 0x23, 0xeb, 0x25, 0x3b, 0xb7, 0xf0, 0xb8, 0xe8, 0x26, 0xba, 0xd9, - 0xf4, 0x17, 0xa3, 0x8f, 0xd9, 0xf9, 0x33, 0x31, 0x65, 0x3c, 0x85, 0x76, 0xb6, 0x3c, 0x16, 0x29, - 0x33, 0xf0, 0x16, 0xae, 0x8d, 0xeb, 0x57, 0x4d, 0x39, 0x10, 0x1d, 0xf6, 0xd2, 0x93, 0x9e, 0x91, - 0xae, 0x87, 0xcf, 0x3c, 0xce, 0x52, 0x45, 0xb5, 0xc4, 0x18, 0x0e, 0x54, 0xf1, 0x37, 0x17, 0xbf, - 0x9f, 0xc0, 0x45, 0x14, 0x2c, 0xbe, 0xc9, 0x13, 0x00, 0xca, 0x79, 0xe0, 0x8c, 0x16, 0xc9, 0x72, - 0xed, 0x81, 0x7c, 0xf6, 0x18, 0x7c, 0x7c, 0x76, 0x4a, 0x9d, 0x60, 0x78, 0x4b, 0xf9, 0xca, 0x56, - 0x82, 0x4c, 0xf9, 0x4b, 0x4a, 0xdf, 0xf8, 0x45, 0x15, 0x6a, 0xb2, 0x2d, 0x20, 0x83, 0x6c, 0xd3, - 0x29, 0x56, 0x55, 0x87, 0x94, 0xb3, 0xea, 0x8c, 0x31, 0xe3, 0xbf, 0x95, 0xef, 0xdc, 0x86, 0xcd, - 0x8b, 0x57, 0x3b, 0x75, 0x64, 0xcb, 0x93, 0x47, 0x49, 0x1b, 0xb7, 0xae, 0xcb, 0x89, 0x7a, 0xc6, - 0xca, 0xd7, 0xee, 0x19, 0x6f, 0x40, 0xdd, 0x5d, 0xcc, 0x2d, 0xbe, 0x0a, 0x55, 0xb6, 0xa9, 0xb9, - 0x8b, 0xf9, 0xb3, 0x15, 0x7a, 0x09, 0xf7, 0x38, 0x9d, 0xa1, 0x48, 0xe6, 0x9a, 0x06, 0x4e, 0x08, - 0xe1, 0x21, 0xb4, 0x52, 0x45, 0x85, 0x63, 0xab, 0xe2, 0xb4, 0x9d, 0x76, 0xf6, 0x93, 0x47, 0xea, - 0x96, 0xcd, 0xb8, 0xc8, 0x38, 0xb1, 0xc9, 0x6e, 0xb6, 0x45, 0xc2, 0x5a, 0xa4, 0x81, 0x21, 0x95, - 0xea, 0x82, 0x44, 0x25, 0x22, 0x0e, 0x20, 0x82, 0x4c, 0x42, 0x74, 0x84, 0x34, 0xc4, 0x04, 0x0a, - 0xdf, 0x86, 0x4e, 0x42, 0xe7, 0x12, 0x02, 0x72, 0x95, 0x64, 0x1a, 0x81, 0xef, 0xc1, 0x96, 0xcb, - 0x56, 0xdc, 0xca, 0xa3, 0x9b, 0x88, 0x26, 0x42, 0x76, 0x96, 0xd5, 0xf8, 0x26, 0xb4, 0x93, 0x54, - 0x84, 0xd8, 0x4d, 0xd9, 0xa8, 0xc6, 0xb3, 0x08, 0xbb, 0x09, 0x8d, 0xb8, 0x98, 0x6a, 0x21, 0xa0, - 0x4e, 0x65, 0x0d, 0x15, 0x97, 0x67, 0x01, 0x0b, 0x17, 0x33, 0xae, 0x16, 0x69, 0x23, 0x06, 0xcb, - 0x33, 0x53, 0xce, 0x23, 0xf6, 0x0e, 0xb4, 0xa2, 0xe8, 0x96, 0xb8, 0x0e, 0xe2, 0x36, 0xa3, 0x49, - 0x04, 0xed, 0x41, 0xd7, 0x0f, 0x3c, 0xdf, 0x0b, 0x59, 0x60, 0x51, 0xdb, 0x0e, 0x58, 0x18, 0xf6, - 0xba, 0x72, 0xbd, 0x68, 0xfe, 0x48, 0x4e, 0x1b, 0xef, 0x43, 0x3d, 0xaa, 0x12, 0xb7, 0xa0, 0x3a, - 0x8c, 0x33, 0x51, 0xc5, 0x94, 0x03, 0xc1, 0x43, 0x47, 0xbe, 0xaf, 0xde, 0x3a, 0xc4, 0xa7, 0xf1, - 0x33, 0xa8, 0xab, 0x1f, 0xac, 0xb0, 0x03, 0xfe, 0x1e, 0x6c, 0xfa, 0x34, 0x10, 0xd7, 0x48, 0xf7, - 0xc1, 0x51, 0x1f, 0x72, 0x4a, 0x03, 0xfe, 0x94, 0xf1, 0x4c, 0x3b, 0xdc, 0x44, 0xbc, 0x9c, 0x32, - 0xee, 0x43, 0x2b, 0x83, 0x11, 0xc7, 0x42, 0x3f, 0x8a, 0x82, 0x1a, 0x07, 0xf1, 0xce, 0xa5, 0x64, - 0x67, 0xe3, 0x01, 0xe8, 0xf1, 0x6f, 0x23, 0xca, 0xe5, 0xe8, 0xea, 0x9a, 0x32, 0xb7, 0x1c, 0x62, - 0x8b, 0xef, 0x7d, 0xca, 0x02, 0x15, 0x13, 0x72, 0x60, 0x3c, 0x4f, 0x25, 0x21, 0xc9, 0x0a, 0xe4, - 0x2e, 0xd4, 0x55, 0x12, 0x52, 0x51, 0x19, 0x35, 0xf3, 0xa7, 0x98, 0x85, 0xa2, 0x66, 0x5e, 0xe6, - 0xa4, 0x64, 0xd9, 0x52, 0x7a, 0xd9, 0x19, 0x34, 0xa2, 0x44, 0x93, 0xcd, 0xc6, 0x72, 0xc5, 0x6e, - 0x3e, 0x1b, 0xab, 0x45, 0x13, 0xa0, 0xf0, 0x8e, 0xd0, 0x99, 0xb8, 0xcc, 0xb6, 0x92, 0x10, 0xc2, - 0x3d, 0x1a, 0x66, 0x47, 0x0a, 0x9e, 0x44, 0xf1, 0x62, 0xbc, 0x07, 0x35, 0x79, 0xb6, 0xc2, 0xf4, - 0x55, 0x44, 0x49, 0xbf, 0xd7, 0xa0, 0x11, 0xe5, 0xe9, 0x42, 0xa5, 0xcc, 0xa1, 0x4b, 0x5f, 0xf5, - 0xd0, 0xff, 0xfd, 0xc4, 0x73, 0x17, 0x88, 0xcc, 0x2f, 0x4b, 0x8f, 0x3b, 0xee, 0xc4, 0x92, 0xb6, - 0x96, 0x39, 0xa8, 0x8b, 0x92, 0x33, 0x14, 0x9c, 0x8a, 0xf9, 0x83, 0xcf, 0xaa, 0xd0, 0x39, 0x1a, - 0x3e, 0x3c, 0x39, 0xf2, 0xfd, 0x99, 0x33, 0xa6, 0xd8, 0x95, 0xec, 0x43, 0x05, 0x1b, 0xb3, 0x82, - 0x87, 0xe5, 0x7e, 0xd1, 0x0b, 0x01, 0x39, 0x80, 0x2a, 0xf6, 0x67, 0xa4, 0xe8, 0x7d, 0xb9, 0x5f, - 0xf8, 0x50, 0x20, 0x36, 0x91, 0x1d, 0xdc, 0xe5, 0x67, 0xe6, 0x7e, 0xd1, 0x6b, 0x01, 0xf9, 0x08, - 0xf4, 0xa4, 0x71, 0x5a, 0xf7, 0xd8, 0xdc, 0x5f, 0xfb, 0x6e, 0x20, 0xf4, 0x93, 0xe2, 0x72, 0xdd, - 0x9b, 0x69, 0x7f, 0x6d, 0x83, 0x4d, 0x0e, 0xa1, 0x1e, 0x95, 0xe5, 0xc5, 0xcf, 0xc1, 0xfd, 0x35, - 0x3d, 0xbd, 0x30, 0x8f, 0xec, 0x85, 0x8a, 0xde, 0xac, 0xfb, 0x85, 0x0f, 0x0f, 0xe4, 0x1e, 0xd4, - 0x54, 0x7d, 0x54, 0xf8, 0x24, 0xdc, 0x2f, 0xee, 0xcc, 0xc5, 0x25, 0x93, 0x6e, 0x70, 0xdd, 0xbb, - 0x7a, 0x7f, 0xed, 0x0b, 0x09, 0x39, 0x02, 0x48, 0xb5, 0x34, 0x6b, 0x1f, 0xcc, 0xfb, 0xeb, 0x5f, - 0x3e, 0xc8, 0x03, 0x68, 0x24, 0xaf, 0x59, 0xc5, 0x4f, 0xe0, 0xfd, 0x75, 0x8f, 0x11, 0xc3, 0x5b, - 0xff, 0xf8, 0xd3, 0xb6, 0xf6, 0xeb, 0x8b, 0x6d, 0xed, 0x8b, 0x8b, 0x6d, 0xed, 0xcb, 0x8b, 0x6d, - 0xed, 0x77, 0x17, 0xdb, 0xda, 0x1f, 0x2f, 0xb6, 0xb5, 0xdf, 0xfc, 0x79, 0x5b, 0x1b, 0xd5, 0xd0, - 0xfd, 0x3f, 0xf8, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3e, 0x33, 0xc0, 0x3b, 0xf2, 0x19, 0x00, - 0x00, +var fileDescriptor_types_30d8160a6576aafe = []byte{ + // 2282 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x58, 0xcd, 0x73, 0x1c, 0x47, + 0x15, 0xd7, 0xec, 0xf7, 0xbc, 0xd5, 0x7e, 0xb8, 0x2d, 0xdb, 0xeb, 0xc5, 0x48, 0xae, 0x31, 0x38, + 0x52, 0xe2, 0xac, 0x12, 0x05, 0x53, 0x32, 0x0e, 0xa9, 0xd2, 0xda, 0x06, 0xa9, 0x62, 0x82, 0x18, + 0xdb, 0xe2, 0x42, 0xd5, 0x54, 0xef, 0x4e, 0x7b, 0x77, 0xca, 0xbb, 0x33, 0x93, 0x99, 0x5e, 0x79, + 0xc5, 0x91, 0x73, 0x0e, 0x39, 0xf0, 0x27, 0x70, 0xe0, 0x4f, 0xc8, 0x91, 0x13, 0x95, 0x23, 0x07, + 0xce, 0x06, 0x44, 0x71, 0xa1, 0x8a, 0x33, 0x70, 0xa3, 0xfa, 0x75, 0xcf, 0xa7, 0x66, 0x4d, 0x62, + 0xb8, 0xe5, 0xb2, 0x3b, 0xdd, 0xef, 0xf7, 0x7a, 0xba, 0xdf, 0xbc, 0xf7, 0x7e, 0xef, 0x35, 0x5c, + 0xa5, 0xa3, 0xb1, 0xb3, 0xcb, 0xcf, 0x7c, 0x16, 0xca, 0xdf, 0x81, 0x1f, 0x78, 0xdc, 0x23, 0x55, + 0x1c, 0xf4, 0xdf, 0x9d, 0x38, 0x7c, 0xba, 0x18, 0x0d, 0xc6, 0xde, 0x7c, 0x77, 0xe2, 0x4d, 0xbc, + 0x5d, 0x94, 0x8e, 0x16, 0xcf, 0x71, 0x84, 0x03, 0x7c, 0x92, 0x5a, 0xfd, 0xfb, 0x29, 0x38, 0x67, + 0xae, 0xcd, 0x82, 0xb9, 0xe3, 0xf2, 0xf4, 0xe3, 0x38, 0x38, 0xf3, 0xb9, 0xb7, 0x3b, 0x67, 0xc1, + 0x8b, 0x19, 0x53, 0x7f, 0x4a, 0x79, 0xff, 0xbf, 0x2a, 0xcf, 0x9c, 0x51, 0xb8, 0x3b, 0xf6, 0xe6, + 0x73, 0xcf, 0x4d, 0x6f, 0xb6, 0xbf, 0x35, 0xf1, 0xbc, 0xc9, 0x8c, 0x25, 0x9b, 0xe3, 0xce, 0x9c, + 0x85, 0x9c, 0xce, 0x7d, 0x09, 0x30, 0x7e, 0x5f, 0x81, 0xba, 0xc9, 0x3e, 0x5d, 0xb0, 0x90, 0x93, + 0x6d, 0xa8, 0xb0, 0xf1, 0xd4, 0xeb, 0x95, 0x6e, 0x6a, 0xdb, 0xcd, 0x3d, 0x32, 0x90, 0x0b, 0x29, + 0xe9, 0xa3, 0xf1, 0xd4, 0x3b, 0x5c, 0x33, 0x11, 0x41, 0xde, 0x81, 0xea, 0xf3, 0xd9, 0x22, 0x9c, + 0xf6, 0xca, 0x08, 0xbd, 0x9c, 0x85, 0xfe, 0x48, 0x88, 0x0e, 0xd7, 0x4c, 0x89, 0x11, 0xcb, 0x3a, + 0xee, 0x73, 0xaf, 0x57, 0x29, 0x5a, 0xf6, 0xc8, 0x7d, 0x8e, 0xcb, 0x0a, 0x04, 0xd9, 0x07, 0x08, + 0x19, 0xb7, 0x3c, 0x9f, 0x3b, 0x9e, 0xdb, 0xab, 0x22, 0xfe, 0x5a, 0x16, 0xff, 0x84, 0xf1, 0x9f, + 0xa2, 0xf8, 0x70, 0xcd, 0xd4, 0xc3, 0x68, 0x20, 0x34, 0x1d, 0xd7, 0xe1, 0xd6, 0x78, 0x4a, 0x1d, + 0xb7, 0x57, 0x2b, 0xd2, 0x3c, 0x72, 0x1d, 0xfe, 0x40, 0x88, 0x85, 0xa6, 0x13, 0x0d, 0xc4, 0x51, + 0x3e, 0x5d, 0xb0, 0xe0, 0xac, 0x57, 0x2f, 0x3a, 0xca, 0xcf, 0x84, 0x48, 0x1c, 0x05, 0x31, 0xe4, + 0x3e, 0x34, 0x47, 0x6c, 0xe2, 0xb8, 0xd6, 0x68, 0xe6, 0x8d, 0x5f, 0xf4, 0x1a, 0xa8, 0xd2, 0xcb, + 0xaa, 0x0c, 0x05, 0x60, 0x28, 0xe4, 0x87, 0x6b, 0x26, 0x8c, 0xe2, 0x11, 0xd9, 0x83, 0xc6, 0x78, + 0xca, 0xc6, 0x2f, 0x2c, 0xbe, 0xec, 0xe9, 0xa8, 0x79, 0x25, 0xab, 0xf9, 0x40, 0x48, 0x9f, 0x2e, + 0x0f, 0xd7, 0xcc, 0xfa, 0x58, 0x3e, 0x92, 0xbb, 0xa0, 0x33, 0xd7, 0x56, 0xaf, 0x6b, 0xa2, 0xd2, + 0xd5, 0xdc, 0x77, 0x71, 0xed, 0xe8, 0x65, 0x0d, 0xa6, 0x9e, 0xc9, 0x00, 0x6a, 0xc2, 0x19, 0x1c, + 0xde, 0x5b, 0x47, 0x9d, 0x8d, 0xdc, 0x8b, 0x50, 0x76, 0xb8, 0x66, 0x2a, 0x94, 0x30, 0x9f, 0xcd, + 0x66, 0xce, 0x29, 0x0b, 0xc4, 0xe6, 0x2e, 0x17, 0x99, 0xef, 0xa1, 0x94, 0xe3, 0xf6, 0x74, 0x3b, + 0x1a, 0x0c, 0xeb, 0x50, 0x3d, 0xa5, 0xb3, 0x05, 0x33, 0xde, 0x82, 0x66, 0xca, 0x53, 0x48, 0x0f, + 0xea, 0x73, 0x16, 0x86, 0x74, 0xc2, 0x7a, 0xda, 0x4d, 0x6d, 0x5b, 0x37, 0xa3, 0xa1, 0xd1, 0x86, + 0xf5, 0xb4, 0x9f, 0x18, 0xf3, 0x58, 0x51, 0xf8, 0x82, 0x50, 0x3c, 0x65, 0x41, 0x28, 0x1c, 0x40, + 0x29, 0xaa, 0x21, 0xb9, 0x05, 0x2d, 0xb4, 0x83, 0x15, 0xc9, 0x85, 0x9f, 0x56, 0xcc, 0x75, 0x9c, + 0x3c, 0x51, 0xa0, 0x2d, 0x68, 0xfa, 0x7b, 0x7e, 0x0c, 0x29, 0x23, 0x04, 0xfc, 0x3d, 0x5f, 0x01, + 0x8c, 0x1f, 0x40, 0x37, 0xef, 0x4a, 0xa4, 0x0b, 0xe5, 0x17, 0xec, 0x4c, 0xbd, 0x4f, 0x3c, 0x92, + 0x0d, 0x75, 0x2c, 0x7c, 0x87, 0x6e, 0xaa, 0x33, 0x7e, 0x5e, 0x8a, 0x95, 0x63, 0x6f, 0x22, 0xfb, + 0x50, 0x11, 0x41, 0x85, 0xda, 0xcd, 0xbd, 0xfe, 0x40, 0x46, 0xdc, 0x20, 0x8a, 0xb8, 0xc1, 0xd3, + 0x28, 0xe2, 0x86, 0x8d, 0x2f, 0x5f, 0x6d, 0xad, 0x7d, 0xfe, 0xa7, 0x2d, 0xcd, 0x44, 0x0d, 0x72, + 0x5d, 0x38, 0x04, 0x75, 0x5c, 0xcb, 0xb1, 0xd5, 0x7b, 0xea, 0x38, 0x3e, 0xb2, 0xc9, 0x01, 0x74, + 0xc7, 0x9e, 0x1b, 0x32, 0x37, 0x5c, 0x84, 0x96, 0x4f, 0x03, 0x3a, 0x0f, 0x55, 0xac, 0x45, 0x9f, + 0xff, 0x41, 0x24, 0x3e, 0x46, 0xa9, 0xd9, 0x19, 0x67, 0x27, 0xc8, 0x87, 0x00, 0xa7, 0x74, 0xe6, + 0xd8, 0x94, 0x7b, 0x41, 0xd8, 0xab, 0xdc, 0x2c, 0xa7, 0x94, 0x4f, 0x22, 0xc1, 0x33, 0xdf, 0xa6, + 0x9c, 0x0d, 0x2b, 0x62, 0x67, 0x66, 0x0a, 0x4f, 0x6e, 0x43, 0x87, 0xfa, 0xbe, 0x15, 0x72, 0xca, + 0x99, 0x35, 0x3a, 0xe3, 0x2c, 0xc4, 0x78, 0x5c, 0x37, 0x5b, 0xd4, 0xf7, 0x9f, 0x88, 0xd9, 0xa1, + 0x98, 0x34, 0xec, 0xf8, 0x6b, 0x62, 0xa8, 0x10, 0x02, 0x15, 0x9b, 0x72, 0x8a, 0xd6, 0x58, 0x37, + 0xf1, 0x59, 0xcc, 0xf9, 0x94, 0x4f, 0xd5, 0x19, 0xf1, 0x99, 0x5c, 0x85, 0xda, 0x94, 0x39, 0x93, + 0x29, 0xc7, 0x63, 0x95, 0x4d, 0x35, 0x12, 0x86, 0xf7, 0x03, 0xef, 0x94, 0x61, 0xb6, 0x68, 0x98, + 0x72, 0x60, 0xfc, 0x4d, 0x83, 0x4b, 0x17, 0xc2, 0x4b, 0xac, 0x3b, 0xa5, 0xe1, 0x34, 0x7a, 0x97, + 0x78, 0x26, 0xef, 0x88, 0x75, 0xa9, 0xcd, 0x02, 0x95, 0xc5, 0x5a, 0xea, 0xc4, 0x87, 0x38, 0xa9, + 0x0e, 0xaa, 0x20, 0xe4, 0x11, 0x74, 0x67, 0x34, 0xe4, 0x96, 0x8c, 0x02, 0x0b, 0xb3, 0x54, 0x39, + 0x13, 0x99, 0x8f, 0x69, 0x14, 0x2d, 0xc2, 0x39, 0x95, 0x7a, 0x7b, 0x96, 0x99, 0x25, 0x87, 0xb0, + 0x31, 0x3a, 0xfb, 0x25, 0x75, 0xb9, 0xe3, 0x32, 0xeb, 0x82, 0xcd, 0x3b, 0x6a, 0xa9, 0x47, 0xa7, + 0x8e, 0xcd, 0xdc, 0x71, 0x64, 0xec, 0xcb, 0xb1, 0x4a, 0xfc, 0x31, 0x42, 0xe3, 0x10, 0xda, 0xd9, + 0x5c, 0x40, 0xda, 0x50, 0xe2, 0x4b, 0x75, 0xc2, 0x12, 0x5f, 0x92, 0xdb, 0x50, 0x11, 0xcb, 0xe1, + 0xe9, 0xda, 0x71, 0x32, 0x55, 0xe8, 0xa7, 0x67, 0x3e, 0x33, 0x51, 0x6e, 0x18, 0xb1, 0xa7, 0xc6, + 0x81, 0x9b, 0x5f, 0xcb, 0xd8, 0x81, 0x4e, 0x2e, 0x89, 0xa4, 0x3e, 0x8b, 0x96, 0xfe, 0x2c, 0x46, + 0x07, 0x5a, 0x99, 0xdc, 0x61, 0x7c, 0x56, 0x85, 0x86, 0xc9, 0x42, 0x5f, 0x38, 0x1d, 0xd9, 0x07, + 0x9d, 0x2d, 0xc7, 0x4c, 0xa6, 0x6d, 0x2d, 0x97, 0x14, 0x25, 0xe6, 0x51, 0x24, 0x17, 0xe9, 0x23, + 0x06, 0x93, 0x9d, 0x0c, 0xe5, 0x5c, 0xce, 0x2b, 0xa5, 0x39, 0xe7, 0x4e, 0x96, 0x73, 0x36, 0x72, + 0xd8, 0x1c, 0xe9, 0xec, 0x64, 0x48, 0x27, 0xbf, 0x70, 0x86, 0x75, 0xee, 0x15, 0xb0, 0x4e, 0x7e, + 0xfb, 0x2b, 0x68, 0xe7, 0x5e, 0x01, 0xed, 0xf4, 0x2e, 0xbc, 0xab, 0x90, 0x77, 0xee, 0x64, 0x79, + 0x27, 0x7f, 0x9c, 0x1c, 0xf1, 0x7c, 0x58, 0x44, 0x3c, 0xd7, 0x73, 0x3a, 0x2b, 0x99, 0xe7, 0x83, + 0x0b, 0xcc, 0x73, 0x35, 0xa7, 0x5a, 0x40, 0x3d, 0xf7, 0x32, 0x9c, 0x00, 0x85, 0x67, 0x2b, 0x26, + 0x05, 0xf2, 0xfd, 0x8b, 0xac, 0x75, 0x2d, 0xff, 0x69, 0x8b, 0x68, 0x6b, 0x37, 0x47, 0x5b, 0x57, + 0xf2, 0xbb, 0xcc, 0xf1, 0x56, 0xc2, 0x3e, 0x3b, 0x22, 0x3f, 0xe4, 0x3c, 0x4d, 0xe4, 0x12, 0x16, + 0x04, 0x5e, 0xa0, 0x12, 0xbb, 0x1c, 0x18, 0xdb, 0x22, 0x63, 0x25, 0xfe, 0xf5, 0x1a, 0xa6, 0x42, + 0xa7, 0x4f, 0x79, 0x97, 0xf1, 0x85, 0x96, 0xe8, 0x62, 0xe4, 0xa7, 0xb3, 0x9d, 0xae, 0xb2, 0x5d, + 0x8a, 0xc0, 0x4a, 0x59, 0x02, 0xdb, 0x82, 0xa6, 0xc8, 0xa9, 0x39, 0x6e, 0xa2, 0x7e, 0xc4, 0x4d, + 0xe4, 0x6d, 0xb8, 0x84, 0xf9, 0x48, 0xd2, 0x9c, 0x0a, 0xc4, 0x0a, 0x06, 0x62, 0x47, 0x08, 0xa4, + 0xc5, 0x64, 0xa2, 0x7c, 0x17, 0x2e, 0xa7, 0xb0, 0x62, 0x5d, 0xcc, 0x85, 0x32, 0x49, 0x77, 0x63, + 0xf4, 0x81, 0xef, 0x1f, 0xd2, 0x70, 0x6a, 0xfc, 0x24, 0x31, 0x50, 0xc2, 0x7b, 0x04, 0x2a, 0x63, + 0xcf, 0x96, 0xe7, 0x6e, 0x99, 0xf8, 0x2c, 0xb8, 0x70, 0xe6, 0x4d, 0x70, 0x73, 0xba, 0x29, 0x1e, + 0x05, 0x2a, 0x0e, 0x25, 0x5d, 0xc6, 0x8c, 0xf1, 0x6b, 0x2d, 0x59, 0x2f, 0xa1, 0xc2, 0x22, 0xd6, + 0xd2, 0xfe, 0x17, 0xd6, 0x2a, 0x7d, 0x3d, 0xd6, 0x32, 0xce, 0xb5, 0xe4, 0x93, 0xc5, 0x7c, 0xf4, + 0x66, 0x47, 0x14, 0xde, 0xe3, 0xb8, 0x36, 0x5b, 0xa2, 0x49, 0xcb, 0xa6, 0x1c, 0x44, 0xa5, 0x42, + 0x0d, 0xcd, 0x9c, 0x2d, 0x15, 0xea, 0x38, 0x27, 0x07, 0xe4, 0x16, 0xf2, 0x98, 0xf7, 0x5c, 0x85, + 0x6a, 0x6b, 0xa0, 0x0a, 0xfa, 0x63, 0x31, 0x69, 0x4a, 0x59, 0x2a, 0xdb, 0xea, 0x19, 0x12, 0xbc, + 0x01, 0xba, 0xd8, 0x68, 0xe8, 0xd3, 0x31, 0xc3, 0xc8, 0xd3, 0xcd, 0x64, 0xc2, 0x78, 0x0a, 0xe4, + 0x62, 0xc4, 0x93, 0x8f, 0xa0, 0xc6, 0x4e, 0x99, 0xcb, 0x85, 0xc5, 0x85, 0xd1, 0xd6, 0x63, 0xda, + 0x61, 0x2e, 0x1f, 0xf6, 0x84, 0xa9, 0xfe, 0xfe, 0x6a, 0xab, 0x2b, 0x31, 0x77, 0xbc, 0xb9, 0xc3, + 0xd9, 0xdc, 0xe7, 0x67, 0xa6, 0xd2, 0x32, 0xfe, 0xa9, 0x09, 0x36, 0xc8, 0x64, 0x83, 0x42, 0xe3, + 0x45, 0x2e, 0x5f, 0x4a, 0x11, 0xfc, 0x57, 0x33, 0xe8, 0xb7, 0x01, 0x26, 0x34, 0xb4, 0x5e, 0x52, + 0x97, 0x33, 0x5b, 0x59, 0x55, 0x9f, 0xd0, 0xf0, 0xe7, 0x38, 0x21, 0xaa, 0x21, 0x21, 0x5e, 0x84, + 0xcc, 0x46, 0xf3, 0x96, 0xcd, 0xfa, 0x84, 0x86, 0xcf, 0x42, 0x66, 0xa7, 0xce, 0x56, 0x7f, 0x93, + 0xb3, 0x65, 0xed, 0xd9, 0xc8, 0xdb, 0xf3, 0xdf, 0x29, 0x5f, 0x4e, 0xc8, 0xf2, 0x9b, 0x71, 0xf6, + 0x7f, 0x68, 0xa2, 0x4e, 0xc8, 0xa6, 0x64, 0x72, 0x04, 0x97, 0xe2, 0x98, 0xb2, 0x16, 0x18, 0x6b, + 0x91, 0x57, 0xbd, 0x3e, 0x14, 0xbb, 0xa7, 0xd9, 0xe9, 0x90, 0x7c, 0x02, 0xd7, 0x72, 0x19, 0x21, + 0x5e, 0xb0, 0xf4, 0xda, 0xc4, 0x70, 0x25, 0x9b, 0x18, 0xa2, 0xf5, 0x12, 0x6b, 0x94, 0xdf, 0xc8, + 0xcb, 0xbf, 0x23, 0x0a, 0xac, 0x34, 0x99, 0x14, 0x7d, 0x53, 0xe3, 0x37, 0x1a, 0x74, 0x72, 0x1b, + 0x22, 0xdb, 0x50, 0x95, 0x7c, 0xa6, 0x65, 0xda, 0x58, 0xb4, 0x98, 0xda, 0xb3, 0x04, 0x90, 0xf7, + 0xa1, 0xc1, 0x54, 0xad, 0xa7, 0x0e, 0x79, 0x25, 0x57, 0x02, 0x2a, 0x7c, 0x0c, 0x23, 0xdf, 0x03, + 0x3d, 0x36, 0x5d, 0xae, 0xce, 0x8f, 0x2d, 0xad, 0x94, 0x12, 0xa0, 0xf1, 0x00, 0x9a, 0xa9, 0xd7, + 0x93, 0x6f, 0x81, 0x3e, 0xa7, 0x4b, 0x55, 0xac, 0xcb, 0xf2, 0xad, 0x31, 0xa7, 0x4b, 0xac, 0xd3, + 0xc9, 0x35, 0xa8, 0x0b, 0xe1, 0x84, 0x4a, 0xc3, 0x97, 0xcd, 0xda, 0x9c, 0x2e, 0x7f, 0x4c, 0x43, + 0x63, 0x07, 0xda, 0xd9, 0x6d, 0x45, 0xd0, 0x88, 0x10, 0x25, 0xf4, 0x60, 0xc2, 0x8c, 0xbb, 0xd0, + 0xc9, 0xed, 0x86, 0x18, 0xd0, 0xf2, 0x17, 0x23, 0xeb, 0x05, 0x3b, 0xb3, 0x70, 0xbb, 0xe8, 0x26, + 0xba, 0xd9, 0xf4, 0x17, 0xa3, 0x8f, 0xd9, 0x99, 0xa8, 0x47, 0x43, 0xe3, 0x09, 0xb4, 0xb3, 0x65, + 0xb4, 0x48, 0x99, 0x81, 0xb7, 0x70, 0x6d, 0x5c, 0xbf, 0x6a, 0xca, 0x81, 0xe8, 0xc4, 0x4f, 0x3d, + 0xe9, 0x19, 0xe9, 0xba, 0xf9, 0xc4, 0xe3, 0x2c, 0x55, 0x7c, 0x4b, 0x8c, 0xe1, 0x40, 0x15, 0xbf, + 0xb9, 0xf8, 0x7e, 0x58, 0x10, 0x2b, 0x0a, 0x16, 0xcf, 0xe4, 0x31, 0x00, 0xe5, 0x3c, 0x70, 0x46, + 0x8b, 0x64, 0xb9, 0xf6, 0x40, 0x5e, 0x8f, 0x0c, 0x3e, 0x3e, 0x39, 0xa6, 0x4e, 0x30, 0xbc, 0xa1, + 0x7c, 0x65, 0x23, 0x41, 0xa6, 0xfc, 0x25, 0xa5, 0x6f, 0xfc, 0xaa, 0x0a, 0x35, 0xd9, 0x3e, 0x90, + 0x41, 0xb6, 0x39, 0x15, 0xab, 0xaa, 0x4d, 0xca, 0x59, 0xb5, 0xc7, 0x98, 0xf1, 0x6f, 0xe7, 0x3b, + 0xbc, 0x61, 0xf3, 0xfc, 0xd5, 0x56, 0x1d, 0xd9, 0xf2, 0xe8, 0x61, 0xd2, 0xee, 0xad, 0xea, 0x86, + 0xa2, 0xde, 0xb2, 0xf2, 0xb5, 0x7b, 0xcb, 0x6b, 0x50, 0x77, 0x17, 0x73, 0x8b, 0x2f, 0x43, 0x95, + 0x6d, 0x6a, 0xee, 0x62, 0xfe, 0x74, 0x89, 0x5e, 0xc2, 0x3d, 0x4e, 0x67, 0x28, 0x92, 0xb9, 0xa6, + 0x81, 0x13, 0x42, 0xb8, 0x0f, 0xad, 0x54, 0x51, 0xe1, 0xd8, 0xaa, 0x38, 0x6d, 0xa7, 0x9d, 0xfd, + 0xe8, 0xa1, 0x3a, 0x65, 0x33, 0x2e, 0x32, 0x8e, 0x6c, 0xb2, 0x9d, 0x6d, 0xa5, 0xb0, 0x16, 0x69, + 0x60, 0x48, 0xa5, 0xba, 0x25, 0x51, 0x89, 0x88, 0x0d, 0x88, 0x20, 0x93, 0x10, 0x1d, 0x21, 0x0d, + 0x31, 0x81, 0xc2, 0xb7, 0xa0, 0x93, 0xd0, 0xb9, 0x84, 0x80, 0x5c, 0x25, 0x99, 0x46, 0xe0, 0x7b, + 0xb0, 0xe1, 0xb2, 0x25, 0xb7, 0xf2, 0xe8, 0x26, 0xa2, 0x89, 0x90, 0x9d, 0x64, 0x35, 0xbe, 0x0b, + 0xed, 0x24, 0x15, 0x21, 0x76, 0x5d, 0x36, 0xb4, 0xf1, 0x2c, 0xc2, 0xae, 0x43, 0x23, 0x2e, 0xa6, + 0x5a, 0x08, 0xa8, 0x53, 0x59, 0x43, 0xc5, 0xe5, 0x59, 0xc0, 0xc2, 0xc5, 0x8c, 0xab, 0x45, 0xda, + 0x88, 0xc1, 0xf2, 0xcc, 0x94, 0xf3, 0x88, 0xbd, 0x05, 0xad, 0x28, 0xba, 0x25, 0xae, 0x83, 0xb8, + 0xf5, 0x68, 0x12, 0x41, 0x3b, 0xd0, 0xf5, 0x03, 0xcf, 0xf7, 0x42, 0x16, 0x58, 0xd4, 0xb6, 0x03, + 0x16, 0x86, 0xbd, 0xae, 0x5c, 0x2f, 0x9a, 0x3f, 0x90, 0xd3, 0xc6, 0xfb, 0x50, 0x8f, 0xaa, 0xc4, + 0x0d, 0xa8, 0x0e, 0xe3, 0x4c, 0x54, 0x31, 0xe5, 0x40, 0xf0, 0xd0, 0x81, 0xef, 0xab, 0x3b, 0x11, + 0xf1, 0x68, 0xfc, 0x02, 0xea, 0xea, 0x83, 0x15, 0x76, 0xca, 0x3f, 0x84, 0x75, 0x9f, 0x06, 0xe2, + 0x18, 0xe9, 0x7e, 0x39, 0xea, 0x43, 0x8e, 0x69, 0xc0, 0x9f, 0x30, 0x9e, 0x69, 0x9b, 0x9b, 0x88, + 0x97, 0x53, 0xc6, 0x3d, 0x68, 0x65, 0x30, 0x62, 0x5b, 0xe8, 0x47, 0x51, 0x50, 0xe3, 0x20, 0x7e, + 0x73, 0x29, 0x79, 0xb3, 0x71, 0x1f, 0xf4, 0xf8, 0xdb, 0x88, 0x72, 0x39, 0x3a, 0xba, 0xa6, 0xcc, + 0x2d, 0x87, 0x78, 0x15, 0xe0, 0xbd, 0x64, 0x81, 0x8a, 0x09, 0x39, 0x30, 0x9e, 0xa5, 0x92, 0x90, + 0x64, 0x05, 0x72, 0x07, 0xea, 0x2a, 0x09, 0xa9, 0xa8, 0x8c, 0x9a, 0xfe, 0x63, 0xcc, 0x42, 0x51, + 0xd3, 0x2f, 0x73, 0x52, 0xb2, 0x6c, 0x29, 0xbd, 0xec, 0x0c, 0x1a, 0x51, 0xa2, 0xc9, 0x66, 0x63, + 0xb9, 0x62, 0x37, 0x9f, 0x8d, 0xd5, 0xa2, 0x09, 0x50, 0x78, 0x47, 0xe8, 0x4c, 0x5c, 0x66, 0x5b, + 0x49, 0x08, 0xe1, 0x3b, 0x1a, 0x66, 0x47, 0x0a, 0x1e, 0x47, 0xf1, 0x62, 0xbc, 0x07, 0x35, 0xb9, + 0xb7, 0xc2, 0xf4, 0x55, 0x44, 0x49, 0x7f, 0xd4, 0xa0, 0x11, 0xe5, 0xe9, 0x42, 0xa5, 0xcc, 0xa6, + 0x4b, 0x5f, 0x75, 0xd3, 0xff, 0xff, 0xc4, 0x73, 0x07, 0x88, 0xcc, 0x2f, 0xa7, 0x1e, 0x77, 0xdc, + 0x89, 0x25, 0x6d, 0x2d, 0x73, 0x50, 0x17, 0x25, 0x27, 0x28, 0x38, 0x16, 0xf3, 0x6f, 0xdf, 0x82, + 0x66, 0xea, 0xee, 0x82, 0xd4, 0xa1, 0xfc, 0x09, 0x7b, 0xd9, 0x5d, 0x23, 0x4d, 0xa8, 0x9b, 0x0c, + 0x3b, 0xd1, 0xae, 0xb6, 0xf7, 0x59, 0x15, 0x3a, 0x07, 0xc3, 0x07, 0x47, 0x07, 0xbe, 0x3f, 0x73, + 0xc6, 0x14, 0x5b, 0x97, 0x5d, 0xa8, 0x60, 0xf7, 0x56, 0x70, 0x4b, 0xdd, 0x2f, 0xba, 0x46, 0x20, + 0x7b, 0x50, 0xc5, 0x26, 0x8e, 0x14, 0x5d, 0x56, 0xf7, 0x0b, 0x6f, 0x13, 0xc4, 0x4b, 0x64, 0x9b, + 0x77, 0xf1, 0xce, 0xba, 0x5f, 0x74, 0xa5, 0x40, 0x3e, 0x02, 0x3d, 0xe9, 0xae, 0x56, 0xdd, 0x5c, + 0xf7, 0x57, 0x5e, 0x2e, 0x08, 0xfd, 0xa4, 0x02, 0x5d, 0x75, 0x01, 0xdb, 0x5f, 0xd9, 0x85, 0x93, + 0x7d, 0xa8, 0x47, 0xb5, 0x7b, 0xf1, 0xdd, 0x72, 0x7f, 0x45, 0xe3, 0x2f, 0xcc, 0x23, 0x1b, 0xa6, + 0xa2, 0x0b, 0xf0, 0x7e, 0xe1, 0xed, 0x04, 0xb9, 0x0b, 0x35, 0x55, 0x44, 0x15, 0xde, 0x2f, 0xf7, + 0x8b, 0xdb, 0x77, 0x71, 0xc8, 0xa4, 0x65, 0x5c, 0x75, 0x49, 0xdf, 0x5f, 0x79, 0x8d, 0x42, 0x0e, + 0x00, 0x52, 0x7d, 0xcf, 0xca, 0xdb, 0xf7, 0xfe, 0xea, 0xeb, 0x11, 0x72, 0x1f, 0x1a, 0xc9, 0x95, + 0x57, 0xf1, 0x7d, 0x7a, 0x7f, 0xd5, 0x8d, 0xc5, 0xf0, 0xc6, 0xbf, 0xfe, 0xb2, 0xa9, 0xfd, 0xf6, + 0x7c, 0x53, 0xfb, 0xe2, 0x7c, 0x53, 0xfb, 0xf2, 0x7c, 0x53, 0xfb, 0xc3, 0xf9, 0xa6, 0xf6, 0xe7, + 0xf3, 0x4d, 0xed, 0x77, 0x7f, 0xdd, 0xd4, 0x46, 0x35, 0x8c, 0x91, 0x0f, 0xfe, 0x13, 0x00, 0x00, + 0xff, 0xff, 0x92, 0xed, 0x9f, 0xca, 0x3f, 0x1a, 0x00, 0x00, } diff --git a/abci/types/types.proto b/abci/types/types.proto index 8a2da5b47..8f9dda832 100644 --- a/abci/types/types.proto +++ b/abci/types/types.proto @@ -81,8 +81,14 @@ message RequestBeginBlock { repeated Evidence byzantine_validators = 4 [(gogoproto.nullable)=false]; } +enum CheckTxType { + New = 0; + Recheck = 1; +} + message RequestCheckTx { bytes tx = 1; + CheckTxType type = 2; } message RequestDeliverTx { diff --git a/consensus/replay_test.go b/consensus/replay_test.go index 724dd056b..bbb5b6678 100644 --- a/consensus/replay_test.go +++ b/consensus/replay_test.go @@ -565,7 +565,7 @@ func TestMockProxyApp(t *testing.T) { mock.SetResponseCallback(proxyCb) someTx := []byte("tx") - mock.DeliverTxAsync(someTx) + mock.DeliverTxAsync(abci.RequestDeliverTx{Tx: someTx}) }) assert.True(t, validTxs == 1) assert.True(t, invalidTxs == 0) diff --git a/docs/spec/abci/abci.md b/docs/spec/abci/abci.md index b7b2e09fe..abab7f548 100644 --- a/docs/spec/abci/abci.md +++ b/docs/spec/abci/abci.md @@ -296,6 +296,12 @@ Commit are included in the header of the next block. - **Request**: - `Tx ([]byte)`: The request transaction bytes + - `Type (CheckTxType)`: What type of `CheckTx` request is this? At present, + there are two possible values: `CheckTx_Unchecked` (the default, which says + that a full check is required), and `CheckTx_Checked` (when the mempool is + initiating a normal recheck of a transaction). + - `AdditionalData ([]byte)`: Reserved for future use. See + [here](https://github.com/tendermint/tendermint/issues/2127#issuecomment-456661420). - **Response**: - `Code (uint32)`: Response code - `Data ([]byte)`: Result bytes, if any. diff --git a/mempool/clist_mempool.go b/mempool/clist_mempool.go index 0d1f3c5b1..4042e9b4b 100644 --- a/mempool/clist_mempool.go +++ b/mempool/clist_mempool.go @@ -279,7 +279,7 @@ func (mem *CListMempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), t return err } - reqRes := mem.proxyAppConn.CheckTxAsync(tx) + reqRes := mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{Tx: tx}) reqRes.SetCallback(mem.reqResCb(tx, txInfo.SenderID, cb)) return nil @@ -591,7 +591,10 @@ func (mem *CListMempool) recheckTxs() { // NOTE: globalCb may be called concurrently. for e := mem.txs.Front(); e != nil; e = e.Next() { memTx := e.Value.(*mempoolTx) - mem.proxyAppConn.CheckTxAsync(memTx.tx) + mem.proxyAppConn.CheckTxAsync(abci.RequestCheckTx{ + Tx: memTx.tx, + Type: abci.CheckTxType_Recheck, + }) } mem.proxyAppConn.FlushAsync() diff --git a/mempool/clist_mempool_test.go b/mempool/clist_mempool_test.go index db6e800b8..90d0ed1ae 100644 --- a/mempool/clist_mempool_test.go +++ b/mempool/clist_mempool_test.go @@ -314,7 +314,7 @@ func TestSerialReap(t *testing.T) { for i := start; i < end; i++ { txBytes := make([]byte, 8) binary.BigEndian.PutUint64(txBytes, uint64(i)) - res, err := appConnCon.DeliverTxSync(txBytes) + res, err := appConnCon.DeliverTxSync(abci.RequestDeliverTx{Tx: txBytes}) if err != nil { t.Errorf("Client error committing tx: %v", err) } @@ -522,7 +522,7 @@ func TestMempoolTxsBytes(t *testing.T) { err = appConnCon.Start() require.Nil(t, err) defer appConnCon.Stop() - res, err := appConnCon.DeliverTxSync(txBytes) + res, err := appConnCon.DeliverTxSync(abci.RequestDeliverTx{Tx: txBytes}) require.NoError(t, err) require.EqualValues(t, 0, res.Code) res2, err := appConnCon.CommitSync() diff --git a/proxy/app_conn.go b/proxy/app_conn.go index 2f792671e..1698ab52f 100644 --- a/proxy/app_conn.go +++ b/proxy/app_conn.go @@ -15,7 +15,7 @@ type AppConnConsensus interface { InitChainSync(types.RequestInitChain) (*types.ResponseInitChain, error) BeginBlockSync(types.RequestBeginBlock) (*types.ResponseBeginBlock, error) - DeliverTxAsync(tx []byte) *abcicli.ReqRes + DeliverTxAsync(types.RequestDeliverTx) *abcicli.ReqRes EndBlockSync(types.RequestEndBlock) (*types.ResponseEndBlock, error) CommitSync() (*types.ResponseCommit, error) } @@ -24,7 +24,7 @@ type AppConnMempool interface { SetResponseCallback(abcicli.Callback) Error() error - CheckTxAsync(tx []byte) *abcicli.ReqRes + CheckTxAsync(types.RequestCheckTx) *abcicli.ReqRes FlushAsync() *abcicli.ReqRes FlushSync() error @@ -69,8 +69,8 @@ func (app *appConnConsensus) BeginBlockSync(req types.RequestBeginBlock) (*types return app.appConn.BeginBlockSync(req) } -func (app *appConnConsensus) DeliverTxAsync(tx []byte) *abcicli.ReqRes { - return app.appConn.DeliverTxAsync(tx) +func (app *appConnConsensus) DeliverTxAsync(req types.RequestDeliverTx) *abcicli.ReqRes { + return app.appConn.DeliverTxAsync(req) } func (app *appConnConsensus) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) { @@ -110,8 +110,8 @@ func (app *appConnMempool) FlushSync() error { return app.appConn.FlushSync() } -func (app *appConnMempool) CheckTxAsync(tx []byte) *abcicli.ReqRes { - return app.appConn.CheckTxAsync(tx) +func (app *appConnMempool) CheckTxAsync(req types.RequestCheckTx) *abcicli.ReqRes { + return app.appConn.CheckTxAsync(req) } //------------------------------------------------ diff --git a/state/execution.go b/state/execution.go index 7e49a9ad8..fd75b2959 100644 --- a/state/execution.go +++ b/state/execution.go @@ -284,7 +284,7 @@ func execBlockOnProxyApp( // Run txs of block. for _, tx := range block.Txs { - proxyAppConn.DeliverTxAsync(tx) + proxyAppConn.DeliverTxAsync(abci.RequestDeliverTx{Tx: tx}) if err := proxyAppConn.Error(); err != nil { return nil, err } From d0414768195807044fa8614b07f4e0035fd5f9e1 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 2 Jul 2019 21:19:58 +0400 Subject: [PATCH 256/259] p2p: dial addrs which came from seed instead of calling ensurePeers (#3762) Calling ensurePeers outside of ensurePeersRoutine can lead to nodes disconnecting from us due to "sent next PEX request too soon" error. Solution is to just dial addrs we got from src instead of calling ensurePeers. Refs #2093 Fixes #3338 --- CHANGELOG_PENDING.md | 2 ++ p2p/pex/pex_reactor.go | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 9c94c55e7..fa81d9bbb 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -31,3 +31,5 @@ program](https://hackerone.com/tendermint). - [rpc] \#3700 Make possible to set absolute paths for TLS cert and key (@climber73) ### BUG FIXES: +- [p2p] \#3338 Prevent "sent next PEX request too soon" errors by not calling + ensurePeers outside of ensurePeersRoutine diff --git a/p2p/pex/pex_reactor.go b/p2p/pex/pex_reactor.go index e77fa8eaa..20862d323 100644 --- a/p2p/pex/pex_reactor.go +++ b/p2p/pex/pex_reactor.go @@ -340,6 +340,15 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { if err != nil { return err } + + srcIsSeed := false + for _, seedAddr := range r.seedAddrs { + if seedAddr.Equals(srcAddr) { + srcIsSeed = true + break + } + } + for _, netAddr := range addrs { // Validate netAddr. Disconnect from a peer if it sends us invalid data. if netAddr == nil { @@ -365,13 +374,23 @@ func (r *PEXReactor) ReceiveAddrs(addrs []*p2p.NetAddress, src Peer) error { } // If this address came from a seed node, try to connect to it without - // waiting. - for _, seedAddr := range r.seedAddrs { - if seedAddr.Equals(srcAddr) { - r.ensurePeers() - } + // waiting (#2093) + if srcIsSeed { + r.Logger.Info("Will dial address, which came from seed", "addr", netAddr, "seed", srcAddr) + go func(addr *p2p.NetAddress) { + err := r.dialPeer(addr) + if err != nil { + switch err.(type) { + case errMaxAttemptsToDial, errTooEarlyToDial: + r.Logger.Debug(err.Error(), "addr", addr) + default: + r.Logger.Error(err.Error(), "addr", addr) + } + } + }(netAddr) } } + return nil } From e645442c9be7694a09fccfde206ff715ee462824 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Tue, 2 Jul 2019 22:01:29 +0400 Subject: [PATCH 257/259] abci: minor cleanups in the socket client (#3758) Follow up from #3512 Specifically: cli.conn.Close() need not be under the mutex (#3512 (comment)) call the reqRes callback after the resCb so they always happen in the same order (#3512) Fixes #3513 --- abci/client/socket_client.go | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/abci/client/socket_client.go b/abci/client/socket_client.go index 16e39bf35..7b1e4cd8d 100644 --- a/abci/client/socket_client.go +++ b/abci/client/socket_client.go @@ -55,10 +55,6 @@ func NewSocketClient(addr string, mustConnect bool) *socketClient { } func (cli *socketClient) OnStart() error { - if err := cli.BaseService.OnStart(); err != nil { - return err - } - var err error var conn net.Conn RETRY_LOOP: @@ -82,15 +78,12 @@ RETRY_LOOP: } func (cli *socketClient) OnStop() { - cli.BaseService.OnStop() - - cli.mtx.Lock() - defer cli.mtx.Unlock() if cli.conn != nil { - // does this really need a mutex? cli.conn.Close() } + cli.mtx.Lock() + defer cli.mtx.Unlock() cli.flushQueue() } @@ -209,19 +202,18 @@ func (cli *socketClient) didRecvResponse(res *types.Response) error { reqres.Done() // Release waiters cli.reqSent.Remove(next) // Pop first item from linked list - // Notify reqRes listener if set (request specific callback). - // NOTE: it is possible this callback isn't set on the reqres object. - // at this point, in which case it will be called after, when it is set. - // TODO: should we move this after the resCb call so the order is always consistent? - if cb := reqres.GetCallback(); cb != nil { - cb(res) - } - // Notify client listener if set (global callback). if cli.resCb != nil { cli.resCb(reqres.Request, res) } + // Notify reqRes listener if set (request specific callback). + // NOTE: it is possible this callback isn't set on the reqres object. + // at this point, in which case it will be called after, when it is set. + if cb := reqres.GetCallback(); cb != nil { + cb(res) + } + return nil } From f76684a05c9e179f8b5fcb528d90b74a1956b4b9 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Wed, 3 Jul 2019 17:17:59 +0400 Subject: [PATCH 258/259] node: allow registration of custom reactors while creating node (#3771) * change invocation of NewNode across * custom reactor name are prefixed with CUSTOM_ * upgate changelog pending * improve comments * node: refactor NewNode to use functional options --- CHANGELOG_PENDING.md | 4 ++++ node/node.go | 25 ++++++++++++++++++++++++- node/node_test.go | 34 +++++++++++++++++++++++++++++++++- p2p/mock/reactor.go | 23 +++++++++++++++++++++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 p2p/mock/reactor.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index fa81d9bbb..5a747650e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -26,6 +26,10 @@ program](https://hackerone.com/tendermint). * P2P Protocol ### FEATURES: +- [node] Refactor `NewNode` to use functional options to make it more flexible + and extensible in the future. +- [node] [\#3730](https://github.com/tendermint/tendermint/pull/3730) Add `CustomReactors` option to `NewNode` allowing caller to pass + custom reactors to run inside Tendermint node (@ParthDesai) ### IMPROVEMENTS: - [rpc] \#3700 Make possible to set absolute paths for TLS cert and key (@climber73) diff --git a/node/node.go b/node/node.go index c992e2424..9beb0669f 100644 --- a/node/node.go +++ b/node/node.go @@ -47,6 +47,10 @@ import ( "github.com/tendermint/tendermint/version" ) +// CustomReactorNamePrefix is a prefix for all custom reactors to prevent +// clashes with built-in reactors. +const CustomReactorNamePrefix = "CUSTOM_" + //------------------------------------------------------------------------------ // DBContext specifies config information for loading a new DB. @@ -136,6 +140,18 @@ func DefaultMetricsProvider(config *cfg.InstrumentationConfig) MetricsProvider { } } +// Option sets a parameter for the node. +type Option func(*Node) + +// CustomReactors allows you to add custom reactors to the node's Switch. +func CustomReactors(reactors map[string]p2p.Reactor) Option { + return func(n *Node) { + for name, reactor := range reactors { + n.sw.AddReactor(CustomReactorNamePrefix+name, reactor) + } + } +} + //------------------------------------------------------------------------------ // Node is the highest level interface to a full Tendermint node. @@ -433,6 +449,7 @@ func createSwitch(config *cfg.Config, sw.AddReactor("BLOCKCHAIN", bcReactor) sw.AddReactor("CONSENSUS", consensusReactor) sw.AddReactor("EVIDENCE", evidenceReactor) + sw.SetNodeInfo(nodeInfo) sw.SetNodeKey(nodeKey) @@ -495,7 +512,8 @@ func NewNode(config *cfg.Config, genesisDocProvider GenesisDocProvider, dbProvider DBProvider, metricsProvider MetricsProvider, - logger log.Logger) (*Node, error) { + logger log.Logger, + options ...Option) (*Node, error) { blockStore, stateDB, err := initDBs(config, dbProvider) if err != nil { @@ -661,6 +679,11 @@ func NewNode(config *cfg.Config, eventBus: eventBus, } node.BaseService = *cmn.NewBaseService(logger, "Node", node) + + for _, option := range options { + option(node) + } + return node, nil } diff --git a/node/node_test.go b/node/node_test.go index ce4e82c2d..841a04686 100644 --- a/node/node_test.go +++ b/node/node_test.go @@ -21,6 +21,7 @@ import ( "github.com/tendermint/tendermint/libs/log" mempl "github.com/tendermint/tendermint/mempool" "github.com/tendermint/tendermint/p2p" + p2pmock "github.com/tendermint/tendermint/p2p/mock" "github.com/tendermint/tendermint/privval" "github.com/tendermint/tendermint/proxy" sm "github.com/tendermint/tendermint/state" @@ -100,7 +101,10 @@ func TestNodeDelayedStart(t *testing.T) { n.GenesisDoc().GenesisTime = now.Add(2 * time.Second) require.NoError(t, err) - n.Start() + err = n.Start() + require.NoError(t, err) + defer n.Stop() + startTime := tmtime.Now() assert.Equal(t, true, startTime.After(n.GenesisDoc().GenesisTime)) } @@ -279,6 +283,34 @@ func TestCreateProposalBlock(t *testing.T) { assert.NoError(t, err) } +func TestNodeNewNodeCustomReactors(t *testing.T) { + config := cfg.ResetTestRoot("node_new_node_custom_reactors_test") + defer os.RemoveAll(config.RootDir) + + cr := p2pmock.NewReactor() + + nodeKey, err := p2p.LoadOrGenNodeKey(config.NodeKeyFile()) + require.NoError(t, err) + + n, err := NewNode(config, + privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile()), + nodeKey, + proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir()), + DefaultGenesisDocProviderFunc(config), + DefaultDBProvider, + DefaultMetricsProvider(config.Instrumentation), + log.TestingLogger(), + CustomReactors(map[string]p2p.Reactor{"FOO": cr}), + ) + require.NoError(t, err) + + err = n.Start() + require.NoError(t, err) + defer n.Stop() + + assert.True(t, cr.IsRunning()) +} + func state(nVals int, height int64) (sm.State, dbm.DB) { vals := make([]types.GenesisValidator, nVals) for i := 0; i < nVals; i++ { diff --git a/p2p/mock/reactor.go b/p2p/mock/reactor.go new file mode 100644 index 000000000..cfce12bd1 --- /dev/null +++ b/p2p/mock/reactor.go @@ -0,0 +1,23 @@ +package mock + +import ( + "github.com/tendermint/tendermint/libs/log" + "github.com/tendermint/tendermint/p2p" + "github.com/tendermint/tendermint/p2p/conn" +) + +type Reactor struct { + p2p.BaseReactor +} + +func NewReactor() *Reactor { + r := &Reactor{} + r.BaseReactor = *p2p.NewBaseReactor("Reactor", r) + r.SetLogger(log.TestingLogger()) + return r +} + +func (r *Reactor) GetChannels() []*conn.ChannelDescriptor { return []*conn.ChannelDescriptor{} } +func (r *Reactor) AddPeer(peer p2p.Peer) {} +func (r *Reactor) RemovePeer(peer p2p.Peer, reason interface{}) {} +func (r *Reactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) {} From 94e0176ac24090d8048c1fa71b51e84ad8e77701 Mon Sep 17 00:00:00 2001 From: Jay Namsayin <31609693+jim380@users.noreply.github.com> Date: Thu, 4 Jul 2019 00:26:13 -0700 Subject: [PATCH 259/259] behaviour: return correct reason in MessageOutOfOrder (#3772) --- CHANGELOG_PENDING.md | 2 ++ behaviour/peer_behaviour.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 5a747650e..9c4c8b684 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -37,3 +37,5 @@ program](https://hackerone.com/tendermint). ### BUG FIXES: - [p2p] \#3338 Prevent "sent next PEX request too soon" errors by not calling ensurePeers outside of ensurePeersRoutine +- [behaviour] Return correct reason in MessageOutOfOrder (@jim380) + diff --git a/behaviour/peer_behaviour.go b/behaviour/peer_behaviour.go index 36630f46b..f7cfd00f0 100644 --- a/behaviour/peer_behaviour.go +++ b/behaviour/peer_behaviour.go @@ -27,7 +27,7 @@ type messageOutOfOrder struct { // MessageOutOfOrder returns a messagOutOfOrder PeerBehaviour. func MessageOutOfOrder(peerID p2p.ID, explanation string) PeerBehaviour { - return PeerBehaviour{peerID: peerID, reason: badMessage{explanation}} + return PeerBehaviour{peerID: peerID, reason: messageOutOfOrder{explanation}} } type consensusVote struct {