mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-17 10:12:50 +00:00
Compare commits
343 Commits
v0.23.1
...
checked_in
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79210e658d | ||
|
|
bbf15b3d09 | ||
|
|
6643c5dd11 | ||
|
|
9795e12ef2 | ||
|
|
be929acd6a | ||
|
|
fe1d59ab7b | ||
|
|
f94eb42ebe | ||
|
|
9d62bd0ad3 | ||
|
|
30519e8361 | ||
|
|
7c6519adbd | ||
|
|
f536089f0b | ||
|
|
746d137f86 | ||
|
|
e798766a27 | ||
|
|
c3384e88e5 | ||
|
|
ed4ce5ff6c | ||
|
|
055d7adffb | ||
|
|
14c1baeb24 | ||
|
|
455d34134c | ||
|
|
6a07f415e9 | ||
|
|
d20693fb16 | ||
|
|
f60713bca8 | ||
|
|
ed107d0e84 | ||
|
|
80562669bf | ||
|
|
55362ed766 | ||
|
|
124d0db1e0 | ||
|
|
4ab7dcf3ac | ||
|
|
26462025bc | ||
|
|
287b25a059 | ||
|
|
37928cb990 | ||
|
|
0790223518 | ||
|
|
0baa7588c2 | ||
|
|
8888595b94 | ||
|
|
1b51cf3f46 | ||
|
|
2363d88979 | ||
|
|
e1538bf67e | ||
|
|
3744e8271d | ||
|
|
7b48ea1788 | ||
|
|
69ecda18f9 | ||
|
|
cc0bea522c | ||
|
|
feb08fa4f8 | ||
|
|
9a6cdaddf2 | ||
|
|
12fa9d1cab | ||
|
|
92343ef484 | ||
|
|
ee7b3d260e | ||
|
|
6ec52a9233 | ||
|
|
05a119aab5 | ||
|
|
d7341c4057 | ||
|
|
3fcb62b931 | ||
|
|
8761b27489 | ||
|
|
e7708850c0 | ||
|
|
561fc2d717 | ||
|
|
724e264ff5 | ||
|
|
989a2f32b1 | ||
|
|
4b2bf023dd | ||
|
|
c17547ac2f | ||
|
|
b1e7fac787 | ||
|
|
35b671214c | ||
|
|
4c0c6e0116 | ||
|
|
b8556b97b8 | ||
|
|
5f88fe0e9b | ||
|
|
1d8348d707 | ||
|
|
f471fc4963 | ||
|
|
2d726a620b | ||
|
|
dfda7b442f | ||
|
|
6e5f58191e | ||
|
|
c648c93807 | ||
|
|
5b120d788a | ||
|
|
e6a55b7be0 | ||
|
|
d2be7482e1 | ||
|
|
44a72fb642 | ||
|
|
c15fc9ff63 | ||
|
|
be1760cc25 | ||
|
|
5b1b1ea58a | ||
|
|
f11aef20a0 | ||
|
|
303649818c | ||
|
|
12675ecd92 | ||
|
|
cb2e58411f | ||
|
|
0755a5203d | ||
|
|
c94133ed1b | ||
|
|
f3d08f969d | ||
|
|
5c6999cf8f | ||
|
|
fd1b8598bc | ||
|
|
32e274cff0 | ||
|
|
ccd04587ff | ||
|
|
52e21cebcf | ||
|
|
69c7aa77bc | ||
|
|
ead9fc0179 | ||
|
|
f36ed7e7ff | ||
|
|
71a34adfe5 | ||
|
|
fc073746a0 | ||
|
|
47bc15c27a | ||
|
|
8dda3c3b28 | ||
|
|
5173fe9414 | ||
|
|
d007ade6c3 | ||
|
|
4c4a95ca53 | ||
|
|
df329e8f27 | ||
|
|
cf8b42d813 | ||
|
|
110b07fb3f | ||
|
|
587116dae1 | ||
|
|
eb0da7f9cb | ||
|
|
d12e55c494 | ||
|
|
d297f02227 | ||
|
|
d0f6864c69 | ||
|
|
0c9c3292c9 | ||
|
|
e4ee34cfbc | ||
|
|
d16f52eab3 | ||
|
|
27ba6e8a42 | ||
|
|
a8eee4ab28 | ||
|
|
cd172acee8 | ||
|
|
97b43d875a | ||
|
|
b394bd5b5c | ||
|
|
f5824bc837 | ||
|
|
111e627037 | ||
|
|
ee8b8bbefb | ||
|
|
dde0936fb8 | ||
|
|
2dfde37f44 | ||
|
|
f99e4010f2 | ||
|
|
f11db8c1b0 | ||
|
|
886a83dfb8 | ||
|
|
8d50bb9dad | ||
|
|
7b727bf3d0 | ||
|
|
84b518b8d3 | ||
|
|
bd951171db | ||
|
|
0d6b75bd53 | ||
|
|
f76312ffe6 | ||
|
|
8aad09d9d4 | ||
|
|
faa3509646 | ||
|
|
a045c562a2 | ||
|
|
26aa978456 | ||
|
|
aa5495f24e | ||
|
|
c0cdb9d441 | ||
|
|
91a8767083 | ||
|
|
3e099f75c7 | ||
|
|
bdd01310a0 | ||
|
|
be5d68ea4f | ||
|
|
89462c52d9 | ||
|
|
2fbf810cd8 | ||
|
|
64fc8f8157 | ||
|
|
e1bda36c6c | ||
|
|
ff9d0cdfb6 | ||
|
|
788474d08d | ||
|
|
484194789f | ||
|
|
747797bf3b | ||
|
|
76302c651f | ||
|
|
5bfb9001eb | ||
|
|
38bced2440 | ||
|
|
4fe9906361 | ||
|
|
fc7f9bcaf6 | ||
|
|
8ae3334423 | ||
|
|
c6c0b52d0c | ||
|
|
e3e3c13741 | ||
|
|
1ea64fc27f | ||
|
|
0e1cd88863 | ||
|
|
33b4617e9a | ||
|
|
503de8c9b8 | ||
|
|
dea4e96f66 | ||
|
|
a57aae7072 | ||
|
|
0bec20a1e3 | ||
|
|
83a7c04bce | ||
|
|
d419fffe18 | ||
|
|
c8895dab98 | ||
|
|
8b94deca73 | ||
|
|
4cd2e40fb1 | ||
|
|
47dc4e6428 | ||
|
|
94288006ba | ||
|
|
22445a5029 | ||
|
|
299d46304d | ||
|
|
5106af484f | ||
|
|
114c405120 | ||
|
|
246a56283a | ||
|
|
1144e72c61 | ||
|
|
3fd54c5df5 | ||
|
|
20c55cffc4 | ||
|
|
dea34506fb | ||
|
|
54fe6ef73c | ||
|
|
6fd79d1545 | ||
|
|
8fcabe8b05 | ||
|
|
e0fa827a53 | ||
|
|
bdf3238710 | ||
|
|
ed9e00a8a7 | ||
|
|
604eae86b6 | ||
|
|
b616e54c9b | ||
|
|
fae21c9f52 | ||
|
|
8c0c4844b6 | ||
|
|
bbc30f992e | ||
|
|
91f8af8fd8 | ||
|
|
0881068d76 | ||
|
|
80c217089a | ||
|
|
61914cf48e | ||
|
|
4416c9e4bc | ||
|
|
c9510d0f50 | ||
|
|
892b170818 | ||
|
|
7f6bd5c161 | ||
|
|
eb5cf0f0dd | ||
|
|
2a3520a714 | ||
|
|
39ab199181 | ||
|
|
4c7591cf6b | ||
|
|
0149c8ffee | ||
|
|
9db66deaa2 | ||
|
|
098681fd91 | ||
|
|
cb91cd5965 | ||
|
|
92185c017c | ||
|
|
d0bb1ab2b0 | ||
|
|
d27cd972d2 | ||
|
|
29d2db352e | ||
|
|
eabb1ece8e | ||
|
|
166fd82b70 | ||
|
|
1de32fba17 | ||
|
|
7b88172f41 | ||
|
|
ffe91ae9e3 | ||
|
|
03afad3218 | ||
|
|
5ecdfacb8e | ||
|
|
4147f856dc | ||
|
|
02d1b03abb | ||
|
|
e873fed815 | ||
|
|
e957f322c7 | ||
|
|
ad3d42981a | ||
|
|
0f7485690e | ||
|
|
d73c5cbdb1 | ||
|
|
7b2f7090fd | ||
|
|
61ab10d655 | ||
|
|
3a6cc5e6af | ||
|
|
c43fb700e3 | ||
|
|
bd531401a0 | ||
|
|
9d06d7e306 | ||
|
|
b1bc3e4f89 | ||
|
|
38b401657e | ||
|
|
8972b6e293 | ||
|
|
5f255f0f71 | ||
|
|
20e35654c6 | ||
|
|
8a84593c02 | ||
|
|
aab5947d82 | ||
|
|
2f7fc87230 | ||
|
|
1cf6712a36 | ||
|
|
43ebc77f9b | ||
|
|
debe56326f | ||
|
|
6dde320591 | ||
|
|
62b2093da5 | ||
|
|
76bb4b15c7 | ||
|
|
0cbf9b2a7d | ||
|
|
0701d79046 | ||
|
|
4f61b97bbe | ||
|
|
1111c1848d | ||
|
|
c919643c3e | ||
|
|
b189ab676f | ||
|
|
fe6a504374 | ||
|
|
91376627ea | ||
|
|
e3f54ece2f | ||
|
|
f26b83f15f | ||
|
|
1f6c7bf22a | ||
|
|
d69cf9dd2f | ||
|
|
4e78badac9 | ||
|
|
684e3cb446 | ||
|
|
a649deb6ee | ||
|
|
5446452b01 | ||
|
|
ad24d66750 | ||
|
|
eb98f1c3a9 | ||
|
|
728d2ed266 | ||
|
|
e10666859f | ||
|
|
6fad8eaf5a | ||
|
|
db53dc5fd4 | ||
|
|
2fe34491ba | ||
|
|
80e49abada | ||
|
|
0f931eeb10 | ||
|
|
89668c3179 | ||
|
|
d0dcb1cde1 | ||
|
|
ed08ae7321 | ||
|
|
6beaf6e72d | ||
|
|
3624a17642 | ||
|
|
8a1a79257e | ||
|
|
9c6fdad276 | ||
|
|
8d28344e84 | ||
|
|
4cf1dbd676 | ||
|
|
00db469fc0 | ||
|
|
93aadf160f | ||
|
|
2756be5a59 | ||
|
|
fc7c298cc0 | ||
|
|
785786bec4 | ||
|
|
aab26c3ff7 | ||
|
|
5a8fe61200 | ||
|
|
3c98cec2c2 | ||
|
|
1fbca09e3c | ||
|
|
37ea7040ef | ||
|
|
6770992b01 | ||
|
|
b30596b3a1 | ||
|
|
ef5c27a2d2 | ||
|
|
e1b9bf7c81 | ||
|
|
4e7bf10b59 | ||
|
|
67b6d51ff4 | ||
|
|
6dbbdb9438 | ||
|
|
e7dd76c28d | ||
|
|
21448bcf4f | ||
|
|
ec3e34efd8 | ||
|
|
b19e148bc5 | ||
|
|
6f8b62d1f3 | ||
|
|
e0e19a24a4 | ||
|
|
087b657008 | ||
|
|
fe835cd456 | ||
|
|
d7035abe73 | ||
|
|
f2b629680a | ||
|
|
720ce658f1 | ||
|
|
ea67fb55eb | ||
|
|
e1062a657f | ||
|
|
4d998b7c03 | ||
|
|
bec9d5cba9 | ||
|
|
06a157ad06 | ||
|
|
7538864c15 | ||
|
|
279259ec8e | ||
|
|
ca9d07e5e4 | ||
|
|
0f80a7da82 | ||
|
|
ae2238efe6 | ||
|
|
96fdec0fca | ||
|
|
fe5e7808f2 | ||
|
|
2d1c5a1ce6 | ||
|
|
00ebdcd581 | ||
|
|
e719a93d1d | ||
|
|
eb9b37e196 | ||
|
|
f2f53442c6 | ||
|
|
24ae878b9f | ||
|
|
619bb3b2d7 | ||
|
|
a657870b3d | ||
|
|
f6705f02c7 | ||
|
|
ea31c4836a | ||
|
|
f1093edbe2 | ||
|
|
9018acde5f | ||
|
|
5453aa6169 | ||
|
|
b51ed132f7 | ||
|
|
8524a8da7f | ||
|
|
cfcbc61449 | ||
|
|
9184733261 | ||
|
|
363146dacf | ||
|
|
ad1b722898 | ||
|
|
8163b99a75 | ||
|
|
835c2ee74a | ||
|
|
19fc4ac47c | ||
|
|
acd976ad5b | ||
|
|
37ef5485b4 | ||
|
|
7f4498f8b1 | ||
|
|
538c410bcd | ||
|
|
c3296f2e01 | ||
|
|
242a6037e8 | ||
|
|
bf0ff212b9 | ||
|
|
a5b7ea93c4 |
1550
.circleci/codecov.sh
Normal file
1550
.circleci/codecov.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -41,10 +41,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
|
||||
@@ -53,8 +53,23 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# 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
|
||||
|
||||
- run:
|
||||
name: slate docs
|
||||
command: |
|
||||
@@ -69,8 +84,22 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run:
|
||||
name: metalinter
|
||||
command: |
|
||||
@@ -91,8 +120,22 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run:
|
||||
name: Run abci apps tests
|
||||
command: |
|
||||
@@ -108,8 +151,22 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run:
|
||||
name: Run abci-cli tests
|
||||
command: |
|
||||
@@ -123,8 +180,22 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run: sudo apt-get update && sudo apt-get install -y --no-install-recommends bsdmainutils
|
||||
- run:
|
||||
name: Run tests
|
||||
@@ -138,8 +209,22 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run: mkdir -p /tmp/logs
|
||||
- run:
|
||||
name: Run tests
|
||||
@@ -163,12 +248,48 @@ jobs:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-pkg-cache
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run:
|
||||
name: Run tests
|
||||
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
|
||||
make get_tools
|
||||
make get_vendor_deps
|
||||
make build-linux
|
||||
make localnet-start &
|
||||
./scripts/localnet-blocks-test.sh 40 5 10 localhost
|
||||
|
||||
test_p2p:
|
||||
environment:
|
||||
GOBIN: /home/circleci/.go_workspace/bin
|
||||
@@ -180,14 +301,30 @@ jobs:
|
||||
- run: mkdir -p $GOPATH/src/github.com/tendermint
|
||||
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
|
||||
- run: bash test/p2p/circleci.sh
|
||||
- store_artifacts:
|
||||
path: /home/circleci/project/test/p2p/logs
|
||||
|
||||
upload_coverage:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: /tmp/workspace
|
||||
- restore_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# - 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
|
||||
|
||||
- run:
|
||||
name: gather
|
||||
command: |
|
||||
@@ -199,7 +336,7 @@ jobs:
|
||||
done
|
||||
- run:
|
||||
name: upload
|
||||
command: bash <(curl -s https://codecov.io/bash) -f coverage.txt
|
||||
command: bash .circleci/codecov.sh -f coverage.txt
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
@@ -224,6 +361,9 @@ workflows:
|
||||
- test_persistence:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- localnet:
|
||||
requires:
|
||||
- setup_dependencies
|
||||
- test_p2p
|
||||
- upload_coverage:
|
||||
requires:
|
||||
|
||||
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -4,4 +4,4 @@
|
||||
* @ebuchman @melekes @xla
|
||||
|
||||
# Precious documentation
|
||||
/docs/ @zramsay @jolesbi
|
||||
/docs/ @zramsay
|
||||
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -14,6 +14,7 @@ test/p2p/data/
|
||||
test/logs
|
||||
coverage.txt
|
||||
docs/_build
|
||||
docs/dist
|
||||
*.log
|
||||
abci-cli
|
||||
docs/node_modules/
|
||||
@@ -25,12 +26,19 @@ scripts/cutWALUntil/cutWALUntil
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
.vscode/
|
||||
|
||||
libs/pubsub/query/fuzz_test/output
|
||||
shunit2
|
||||
|
||||
.tendermint-lite
|
||||
addrbook.json
|
||||
|
||||
*/vendor
|
||||
*/.glide
|
||||
.terraform
|
||||
terraform.tfstate
|
||||
terraform.tfstate.backup
|
||||
terraform.tfstate.d
|
||||
|
||||
.vscode
|
||||
|
||||
161
CHANGELOG.md
161
CHANGELOG.md
@@ -1,5 +1,164 @@
|
||||
# Changelog
|
||||
|
||||
## v0.25.0
|
||||
|
||||
*September 22, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@scriptionist, @bradyjoestar, @WALL-E
|
||||
|
||||
This release is mostly about the ConsensusParams - removing fields and enforcing MaxGas.
|
||||
It also addresses some issues found via security audit, removes various unused
|
||||
functions from `libs/common`, and implements
|
||||
[ADR-012](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-012-peer-transport.md).
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
* [rpc] [\#2391](https://github.com/tendermint/tendermint/issues/2391) /status `result.node_info.other` became a map
|
||||
* [types] [\#2364](https://github.com/tendermint/tendermint/issues/2364) Remove `TxSize` and `BlockGossip` from `ConsensusParams`
|
||||
* Maximum tx size is now set implicitly via the `BlockSize.MaxBytes`
|
||||
* The size of block parts in the consensus is now fixed to 64kB
|
||||
|
||||
* Apps
|
||||
* [mempool] [\#2360](https://github.com/tendermint/tendermint/issues/2360) Mempool tracks the `ResponseCheckTx.GasWanted` and
|
||||
`ConsensusParams.BlockSize.MaxGas` and enforces:
|
||||
- `GasWanted <= MaxGas` for every tx
|
||||
- `(sum of GasWanted in block) <= MaxGas` for block proposal
|
||||
|
||||
* Go API
|
||||
* [libs/common] [\#2431](https://github.com/tendermint/tendermint/issues/2431) Remove Word256 due to lack of use
|
||||
* [libs/common] [\#2452](https://github.com/tendermint/tendermint/issues/2452) Remove the following functions due to lack of use:
|
||||
* byteslice.go: cmn.IsZeros, cmn.RightPadBytes, cmn.LeftPadBytes, cmn.PrefixEndBytes
|
||||
* strings.go: cmn.IsHex, cmn.StripHex
|
||||
* int.go: Uint64Slice, all put/get int64 methods
|
||||
|
||||
FEATURES:
|
||||
- [rpc] [\#2415](https://github.com/tendermint/tendermint/issues/2415) New `/consensus_params?height=X` endpoint to query the consensus
|
||||
params at any height (@scriptonist)
|
||||
- [types] [\#1714](https://github.com/tendermint/tendermint/issues/1714) Add Address to GenesisValidator
|
||||
- [metrics] [\#2337](https://github.com/tendermint/tendermint/issues/2337) `consensus.block_interval_metrics` is now gauge, not histogram (you will be able to see spikes, if any)
|
||||
- [libs] [\#2286](https://github.com/tendermint/tendermint/issues/2286) Panic if `autofile` or `db/fsdb` permissions change from 0600.
|
||||
|
||||
IMPROVEMENTS:
|
||||
- [libs/db] [\#2371](https://github.com/tendermint/tendermint/issues/2371) Output error instead of panic when the given `db_backend` is not initialised (@bradyjoestar)
|
||||
- [mempool] [\#2399](https://github.com/tendermint/tendermint/issues/2399) Make mempool cache a proper LRU (@bradyjoestar)
|
||||
- [p2p] [\#2126](https://github.com/tendermint/tendermint/issues/2126) Introduce PeerTransport interface to improve isolation of concerns
|
||||
- [libs/common] [\#2326](https://github.com/tendermint/tendermint/issues/2326) Service returns ErrNotStarted
|
||||
|
||||
BUG FIXES:
|
||||
- [node] [\#2294](https://github.com/tendermint/tendermint/issues/2294) Delay starting node until Genesis time
|
||||
- [consensus] [\#2048](https://github.com/tendermint/tendermint/issues/2048) Correct peer statistics for marking peer as good
|
||||
- [rpc] [\#2460](https://github.com/tendermint/tendermint/issues/2460) StartHTTPAndTLSServer() now passes StartTLS() errors back to the caller rather than hanging forever.
|
||||
- [p2p] [\#2047](https://github.com/tendermint/tendermint/issues/2047) Accept new connections asynchronously
|
||||
- [tm-bench] [\#2410](https://github.com/tendermint/tendermint/issues/2410) Enforce minimum transaction size (@WALL-E)
|
||||
|
||||
## 0.24.0
|
||||
|
||||
*September 6th, 2018*
|
||||
|
||||
Special thanks to external contributors with PRs included in this release: ackratos, james-ray, bradyjoestar,
|
||||
peerlink, Ahmah2009, bluele, b00f.
|
||||
|
||||
This release includes breaking upgrades in the block header,
|
||||
including the long awaited changes for delaying validator set updates by one
|
||||
block to better support light clients.
|
||||
It also fixes enforcement on the maximum size of blocks, and includes a BFT
|
||||
timestamp in each block that can be safely used by applications.
|
||||
There are also some minor breaking changes to the rpc, config, and ABCI.
|
||||
|
||||
See the [UPGRADING.md](UPGRADING.md#v0.24.0) for details on upgrading to the new
|
||||
version.
|
||||
|
||||
From here on, breaking changes will be broken down to better reflect how users
|
||||
are affected by a change.
|
||||
|
||||
A few more breaking changes are in the works - each will come with a clear
|
||||
Architecture Decision Record (ADR) explaining the change. You can review ADRs
|
||||
[here](https://github.com/tendermint/tendermint/tree/develop/docs/architecture)
|
||||
or in the [open Pull Requests](https://github.com/tendermint/tendermint/pulls).
|
||||
You can also check in on the [issues marked as
|
||||
breaking](https://github.com/tendermint/tendermint/issues?q=is%3Aopen+is%3Aissue+label%3Abreaking).
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
- [config] [\#2169](https://github.com/tendermint/tendermint/issues/2169) Replace MaxNumPeers with MaxNumInboundPeers and MaxNumOutboundPeers
|
||||
- [config] [\#2300](https://github.com/tendermint/tendermint/issues/2300) Reduce default mempool size from 100k to 5k, until ABCI rechecking is implemented.
|
||||
- [rpc] [\#1815](https://github.com/tendermint/tendermint/issues/1815) `/commit` returns a `signed_header` field instead of everything being top-level
|
||||
|
||||
* Apps
|
||||
- [abci] Added address of the original proposer of the block to Header
|
||||
- [abci] Change ABCI Header to match Tendermint exactly
|
||||
- [abci] [\#2159](https://github.com/tendermint/tendermint/issues/2159) Update use of `Validator` (see
|
||||
[ADR-018](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-018-ABCI-Validators.md)):
|
||||
- Remove PubKey from `Validator` (so it's just Address and Power)
|
||||
- Introduce `ValidatorUpdate` (with just PubKey and Power)
|
||||
- InitChain and EndBlock use ValidatorUpdate
|
||||
- Update field names and types in BeginBlock
|
||||
- [state] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Validator set changes are now delayed by one block
|
||||
- updates returned in ResponseEndBlock for block H will be included in RequestBeginBlock for block H+2
|
||||
|
||||
* Go API
|
||||
- [lite] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Complete refactor of the package
|
||||
- [node] [\#2212](https://github.com/tendermint/tendermint/issues/2212) NewNode now accepts a `*p2p.NodeKey` (@bradyjoestar)
|
||||
- [libs/common] [\#2199](https://github.com/tendermint/tendermint/issues/2199) Remove Fmt, in favor of fmt.Sprintf
|
||||
- [libs/common] SplitAndTrim was deleted
|
||||
- [libs/common] [\#2274](https://github.com/tendermint/tendermint/issues/2274) Remove unused Math functions like MaxInt, MaxInt64,
|
||||
MinInt, MinInt64 (@Ahmah2009)
|
||||
- [libs/clist] Panics if list extends beyond MaxLength
|
||||
- [crypto] [\#2205](https://github.com/tendermint/tendermint/issues/2205) Rename AminoRoute variables to no longer be prefixed by signature type.
|
||||
|
||||
* Blockchain Protocol
|
||||
- [state] [\#1815](https://github.com/tendermint/tendermint/issues/1815) Validator set changes are now delayed by one block (!)
|
||||
- Add NextValidatorSet to State, changes on-disk representation of state
|
||||
- [state] [\#2184](https://github.com/tendermint/tendermint/issues/2184) Enforce ConsensusParams.BlockSize.MaxBytes (See
|
||||
[ADR-020](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-020-block-size.md)).
|
||||
- Remove ConsensusParams.BlockSize.MaxTxs
|
||||
- Introduce maximum sizes for all components of a block, including ChainID
|
||||
- [types] Updates to the block Header:
|
||||
- [\#1815](https://github.com/tendermint/tendermint/issues/1815) NextValidatorsHash - hash of the validator set for the next block,
|
||||
so the current validators actually sign over the hash for the new
|
||||
validators
|
||||
- [\#2106](https://github.com/tendermint/tendermint/issues/2106) ProposerAddress - address of the block's original proposer
|
||||
- [consensus] [\#2203](https://github.com/tendermint/tendermint/issues/2203) Implement BFT time
|
||||
- Timestamp in block must be monotonic and equal the median of timestamps in block's LastCommit
|
||||
- [crypto] [\#2239](https://github.com/tendermint/tendermint/issues/2239) Secp256k1 signature changes (See
|
||||
[ADR-014](https://github.com/tendermint/tendermint/blob/develop/docs/architecture/adr-014-secp-malleability.md)):
|
||||
- format changed from DER to `r || s`, both little endian encoded as 32 bytes.
|
||||
- malleability removed by requiring `s` to be in canonical form.
|
||||
|
||||
* P2P Protocol
|
||||
- [p2p] [\#2263](https://github.com/tendermint/tendermint/issues/2263) Update secret connection to use a little endian encoded nonce
|
||||
- [blockchain] [\#2213](https://github.com/tendermint/tendermint/issues/2213) Fix Amino routes for blockchain reactor messages
|
||||
(@peerlink)
|
||||
|
||||
|
||||
FEATURES:
|
||||
- [types] [\#2015](https://github.com/tendermint/tendermint/issues/2015) Allow genesis file to have 0 validators (@b00f)
|
||||
- Initial validator set can be determined by the app in ResponseInitChain
|
||||
- [rpc] [\#2161](https://github.com/tendermint/tendermint/issues/2161) New event `ValidatorSetUpdates` for when the validator set changes
|
||||
- [crypto/multisig] [\#2164](https://github.com/tendermint/tendermint/issues/2164) Introduce multisig pubkey and signature format
|
||||
- [libs/db] [\#2293](https://github.com/tendermint/tendermint/issues/2293) Allow passing options through when creating instances of leveldb dbs
|
||||
|
||||
IMPROVEMENTS:
|
||||
- [docs] Lint documentation with `write-good` and `stop-words`.
|
||||
- [docs] [\#2249](https://github.com/tendermint/tendermint/issues/2249) Refactor, deduplicate, and improve the ABCI docs and spec (with thanks to @ttmc).
|
||||
- [scripts] [\#2196](https://github.com/tendermint/tendermint/issues/2196) Added json2wal tool, which is supposed to help our users restore (@bradyjoestar)
|
||||
corrupted WAL files and compose test WAL files (@bradyjoestar)
|
||||
- [mempool] [\#2234](https://github.com/tendermint/tendermint/issues/2234) Now stores txs by hash inside of the cache, to mitigate memory leakage
|
||||
- [mempool] [\#2166](https://github.com/tendermint/tendermint/issues/2166) Set explicit capacity for map when updating txs (@bluele)
|
||||
|
||||
BUG FIXES:
|
||||
- [config] [\#2284](https://github.com/tendermint/tendermint/issues/2284) Replace `db_path` with `db_dir` from automatically generated configuration files.
|
||||
- [mempool] [\#2188](https://github.com/tendermint/tendermint/issues/2188) Fix OOM issue from cache map and list getting out of sync
|
||||
- [state] [\#2051](https://github.com/tendermint/tendermint/issues/2051) KV store index supports searching by `tx.height` (@ackratos)
|
||||
- [rpc] [\#2327](https://github.com/tendermint/tendermint/issues/2327) `/dial_peers` does not try to dial existing peers
|
||||
- [node] [\#2323](https://github.com/tendermint/tendermint/issues/2323) Filter empty strings from config lists (@james-ray)
|
||||
- [abci/client] [\#2236](https://github.com/tendermint/tendermint/issues/2236) Fix closing GRPC connection (@bradyjoestar)
|
||||
|
||||
## 0.23.1
|
||||
|
||||
*August 22nd, 2018*
|
||||
@@ -646,7 +805,7 @@ BREAKING CHANGES:
|
||||
- use scripts/wal2json to convert to json for debugging
|
||||
|
||||
FEATURES:
|
||||
- new `certifiers` pkg contains the tendermint light-client library (name subject to change)!
|
||||
- new `Verifiers` pkg contains the tendermint light-client library (name subject to change)!
|
||||
- rpc: `/genesis` includes the `app_options` .
|
||||
- rpc: `/abci_query` takes an additional `height` parameter to support historical queries.
|
||||
- rpc/client: new ABCIQueryWithOptions supports options like `trusted` (set false to get a proof) and `height` to query a historical height.
|
||||
|
||||
107
CHANGELOG_PENDING.md
Normal file
107
CHANGELOG_PENDING.md
Normal file
@@ -0,0 +1,107 @@
|
||||
# Pending
|
||||
|
||||
## v0.26.0
|
||||
|
||||
*October 19, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@bradyjoestar, @connorwstein, @goolAdapter, @HaoyangLiu,
|
||||
@james-ray, @overbool, @phymbert, @Slamper, @Uzair1995
|
||||
|
||||
This release is primarily about adding Version fields to various data structures,
|
||||
optimizing consensus messages for signing and verification in
|
||||
restricted environments (like HSMs and the Ethereum Virtual Machine), and
|
||||
aligning the consensus code with the [specification](https://arxiv.org/abs/1807.04938).
|
||||
It also includes our first take at a generalized merkle proof system.
|
||||
|
||||
See the [UPGRADING.md](UPGRADING.md#v0.26.0) for details on upgrading to the new
|
||||
version.
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) timeouts as time.Duration, not ints
|
||||
* [config] [\#2505](https://github.com/tendermint/tendermint/issues/2505) Remove Mempool.RecheckEmpty (it was effectively useless anyways)
|
||||
* [config] [\#2490](https://github.com/tendermint/tendermint/issues/2490) `mempool.wal` is disabled by default
|
||||
* [privval] [\#2459](https://github.com/tendermint/tendermint/issues/2459) Split `SocketPVMsg`s implementations into Request and Response, where the Response may contain a error message (returned by the remote signer)
|
||||
* [state] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version field to State, breaking the format of State as
|
||||
encoded on disk.
|
||||
* [rpc] [\#2298](https://github.com/tendermint/tendermint/issues/2298) `/abci_query` takes `prove` argument instead of `trusted` and switches the default
|
||||
behaviour to `prove=false`
|
||||
* [rpc] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Remove all `node_info.other.*_version` fields in `/status` and
|
||||
`/net_info`
|
||||
|
||||
* Apps
|
||||
* [abci] [\#2298](https://github.com/tendermint/tendermint/issues/2298) ResponseQuery.Proof is now a structured merkle.Proof, not just
|
||||
arbitrary bytes
|
||||
* [abci] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version to Header and shift all fields by one
|
||||
* [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Bump the field numbers for some `ResponseInfo` fields to make room for
|
||||
`AppVersion`
|
||||
|
||||
* Go API
|
||||
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) timeouts as time.Duration, not ints
|
||||
* [crypto/merkle & lite] [\#2298](https://github.com/tendermint/tendermint/issues/2298) Various changes to accomodate General Merkle trees
|
||||
* [crypto/merkle] [\#2595](https://github.com/tendermint/tendermint/issues/2595) Remove all Hasher objects in favor of byte slices
|
||||
* [crypto/merkle] [\#2635](https://github.com/tendermint/tendermint/issues/2635) merkle.SimpleHashFromTwoHashes is no longer exported
|
||||
* [node] [\#2479](https://github.com/tendermint/tendermint/issues/2479) Remove node.RunForever
|
||||
* [rpc/client] [\#2298](https://github.com/tendermint/tendermint/issues/2298) `ABCIQueryOptions.Trusted` -> `ABCIQueryOptions.Prove`
|
||||
* [types] [\#2298](https://github.com/tendermint/tendermint/issues/2298) Remove `Index` and `Total` fields from `TxProof`.
|
||||
* [types] [\#2598](https://github.com/tendermint/tendermint/issues/2598) `VoteTypeXxx` are now of type `SignedMsgType byte` and named `XxxType`, eg. `PrevoteType`,
|
||||
`PrecommitType`.
|
||||
|
||||
* Blockchain Protocol
|
||||
* [types] Update SignBytes for `Vote`/`Proposal`/`Heartbeat`:
|
||||
* [\#2459](https://github.com/tendermint/tendermint/issues/2459) Use amino encoding instead of JSON in `SignBytes`.
|
||||
* [\#2598](https://github.com/tendermint/tendermint/issues/2598) Reorder fields and use fixed sized encoding.
|
||||
* [\#2598](https://github.com/tendermint/tendermint/issues/2598) Change `Type` field fromt `string` to `byte` and use new
|
||||
`SignedMsgType` to enumerate.
|
||||
* [types] [\#2512](https://github.com/tendermint/tendermint/issues/2512) Remove the pubkey field from the validator hash
|
||||
* [types] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Add Version struct to Header
|
||||
* [types] [\#2609](https://github.com/tendermint/tendermint/issues/2609) ConsensusParams.Hash() is the hash of the amino encoded
|
||||
struct instead of the Merkle tree of the fields
|
||||
* [state] [\#2587](https://github.com/tendermint/tendermint/issues/2587) Require block.Time of the fist block to be genesis time
|
||||
* [state] [\#2644](https://github.com/tendermint/tendermint/issues/2644) Require block.Version to match state.Version
|
||||
* [types] [\#2670](https://github.com/tendermint/tendermint/issues/2670) Header.Hash() builds Merkle tree out of fields in the same
|
||||
order they appear in the header, instead of sorting by field name
|
||||
|
||||
* P2P Protocol
|
||||
* [p2p] [\#2654](https://github.com/tendermint/tendermint/issues/2654) Add `ProtocolVersion` struct with protocol versions to top of
|
||||
DefaultNodeInfo and require `ProtocolVersion.Block` to match during peer handshake
|
||||
|
||||
FEATURES:
|
||||
- [abci] [\#2557](https://github.com/tendermint/tendermint/issues/2557) Add `Codespace` field to `Response{CheckTx, DeliverTx, Query}`
|
||||
- [abci] [\#2662](https://github.com/tendermint/tendermint/issues/2662) Add `BlockVersion` and `P2PVersion` to `RequestInfo`
|
||||
- [crypto/merkle] [\#2298](https://github.com/tendermint/tendermint/issues/2298) General Merkle Proof scheme for chaining various types of Merkle trees together
|
||||
|
||||
IMPROVEMENTS:
|
||||
- Additional Metrics
|
||||
- [consensus] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169)
|
||||
- [p2p] [\#2169](https://github.com/cosmos/cosmos-sdk/issues/2169)
|
||||
- [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Added ValidateBasic method, which performs basic checks
|
||||
- [crypto/ed25519] [\#2558](https://github.com/tendermint/tendermint/issues/2558) Switch to use latest `golang.org/x/crypto` through our fork at
|
||||
github.com/tendermint/crypto
|
||||
- [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit
|
||||
|
||||
BUG FIXES:
|
||||
- [autofile] [\#2428](https://github.com/tendermint/tendermint/issues/2428) Group.RotateFile need call Flush() before rename (@goolAdapter)
|
||||
- [common] [\#2533](https://github.com/tendermint/tendermint/issues/2533) Fixed a bug in the `BitArray.Or` method
|
||||
- [common] [\#2506](https://github.com/tendermint/tendermint/issues/2506) Fixed a bug in the `BitArray.Sub` method (@james-ray)
|
||||
- [common] [\#2534](https://github.com/tendermint/tendermint/issues/2534) Fix `BitArray.PickRandom` to choose uniformly from true bits
|
||||
- [consensus] [\#1690](https://github.com/tendermint/tendermint/issues/1690) Wait for
|
||||
timeoutPrecommit before starting next round
|
||||
- [consensus] [\#1745](https://github.com/tendermint/tendermint/issues/1745) Wait for
|
||||
Proposal or timeoutProposal before entering prevote
|
||||
- [consensus] [\#2642](https://github.com/tendermint/tendermint/issues/2642) Only propose ValidBlock, not LockedBlock
|
||||
- [consensus] [\#2642](https://github.com/tendermint/tendermint/issues/2642) Initialized ValidRound and LockedRound to -1
|
||||
- [consensus] [\#1637](https://github.com/tendermint/tendermint/issues/1637) Limit the amount of evidence that can be included in a
|
||||
block
|
||||
- [evidence] [\#2515](https://github.com/tendermint/tendermint/issues/2515) Fix db iter leak (@goolAdapter)
|
||||
- [libs/event] [\#2518](https://github.com/tendermint/tendermint/issues/2518) Fix event concurrency flaw (@goolAdapter)
|
||||
- [node] [\#2434](https://github.com/tendermint/tendermint/issues/2434) Make node respond to signal interrupts while sleeping for genesis time
|
||||
- [state] [\#2616](https://github.com/tendermint/tendermint/issues/2616) Pass nil to NewValidatorSet() when genesis file's Validators field is nil
|
||||
- [p2p] [\#2555](https://github.com/tendermint/tendermint/issues/2555) Fix p2p switch FlushThrottle value (@goolAdapter)
|
||||
- [p2p] [\#2668](https://github.com/tendermint/tendermint/issues/2668) Reconnect to originally dialed address (not self-reported
|
||||
address) for persistent peers
|
||||
|
||||
98
Gopkg.lock
generated
98
Gopkg.lock
generated
@@ -11,11 +11,11 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
|
||||
digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7"
|
||||
name = "github.com/btcsuite/btcd"
|
||||
packages = ["btcec"]
|
||||
pruneopts = "UT"
|
||||
revision = "f5e261fc9ec3437697fb31d8b38453c293204b29"
|
||||
revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf"
|
||||
@@ -28,12 +28,12 @@
|
||||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39"
|
||||
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
pruneopts = "UT"
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c7644c73a3d23741fdba8a99b1464e021a224b7e205be497271a8003a15ca41b"
|
||||
@@ -83,12 +83,12 @@
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c4a2528ccbcabf90f9f3c464a5fc9e302d592861bbfd0b7135a7de8a943d0406"
|
||||
digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
|
||||
name = "github.com/go-stack/stack"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
|
||||
version = "v1.7.0"
|
||||
revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
|
||||
@@ -136,8 +136,7 @@
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:12247a2e99a060cc692f6680e5272c8adf0b8f572e6bce0d7095e624c958a240"
|
||||
digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
@@ -151,7 +150,8 @@
|
||||
"json/token",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||
@@ -193,12 +193,12 @@
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:5ab79470a1d0fb19b041a624415612f8236b3c06070161a910562f2b2d064355"
|
||||
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
||||
@@ -244,7 +244,7 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
|
||||
digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
@@ -252,11 +252,11 @@
|
||||
"model",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
|
||||
revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
|
||||
digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
@@ -265,7 +265,7 @@
|
||||
"xfs",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
|
||||
revision = "185b4288413d2a0dd0806f78c90dde719829e5ae"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
|
||||
@@ -275,15 +275,15 @@
|
||||
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:bd1ae00087d17c5a748660b8e89e1043e1e5479d0fea743352cda2f8dd8c4f84"
|
||||
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
|
||||
version = "v1.1.1"
|
||||
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:516e71bed754268937f57d4ecb190e01958452336fa73dbac880894164e91c1f"
|
||||
@@ -302,20 +302,20 @@
|
||||
version = "v0.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:080e5f630945ad754f4b920e60b4d3095ba0237ebf88dc462eb28002932e3805"
|
||||
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
|
||||
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:9424f440bba8f7508b69414634aef3b2b3a877e522d8a4624692412805407bb7"
|
||||
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
|
||||
version = "v1.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96"
|
||||
@@ -338,7 +338,7 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:b3cfb8d82b1601a846417c3f31c03a7961862cb2c98dcf0959c473843e6d9a2b"
|
||||
digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c"
|
||||
name = "github.com/syndtr/goleveldb"
|
||||
packages = [
|
||||
"leveldb",
|
||||
@@ -355,37 +355,33 @@
|
||||
"leveldb/util",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
|
||||
revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:087aaa7920e5d0bf79586feb57ce01c35c830396ab4392798112e8aae8c47722"
|
||||
name = "github.com/tendermint/ed25519"
|
||||
packages = [
|
||||
".",
|
||||
"edwards25519",
|
||||
"extra25519",
|
||||
]
|
||||
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
|
||||
name = "github.com/tendermint/btcd"
|
||||
packages = ["btcec"]
|
||||
pruneopts = "UT"
|
||||
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
|
||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:e9113641c839c21d8eaeb2c907c7276af1eddeed988df8322168c56b7e06e0e1"
|
||||
digest = "1:5f52e817b6c9d52ddba70dece0ea31134d82a52c05bce98fbc739ab2a832df28"
|
||||
name = "github.com/tendermint/go-amino"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
|
||||
version = "0.10.1"
|
||||
revision = "cb07448b240918aa8d8df4505153549b86b77134"
|
||||
version = "v0.13.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c31a37cafc12315b8bd745c8ad6a006ac25350472488162a821e557b3e739d67"
|
||||
digest = "1:72b71e3a29775e5752ed7a8012052a3dee165e27ec18cedddae5288058f09acf"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"bcrypt",
|
||||
"blowfish",
|
||||
"chacha20poly1305",
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"hkdf",
|
||||
"internal/chacha20",
|
||||
"internal/subtle",
|
||||
@@ -398,7 +394,8 @@
|
||||
"salsa20/salsa",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c126467f60eb25f8f27e5a981f32a87e3965053f"
|
||||
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
|
||||
source = "github.com/tendermint/crypto"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||
@@ -418,14 +415,14 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:bb0fe59917bdd5b89f49b9a8b26e5f465e325d9223b3a8e32254314bdf51e0f1"
|
||||
digest = "1:d1da39c9bac61327dbef1d8ef9f210425e99fd2924b6fb5f0bc587a193353637"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"cpu",
|
||||
"unix",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "3dc4335d56c789b04b0ba99b7a37249d9b614314"
|
||||
revision = "8a28ead16f52c8aaeffbf79239b251dfdf6c4f96"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
@@ -452,11 +449,11 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
|
||||
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
pruneopts = "UT"
|
||||
revision = "daca94659cb50e9f37c1b834680f2e46358f10b0"
|
||||
revision = "94acd270e44e65579b9ee3cdab25034d33fed608"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
||||
@@ -504,7 +501,6 @@
|
||||
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/ebuchman/fail-test",
|
||||
@@ -536,12 +532,12 @@
|
||||
"github.com/syndtr/goleveldb/leveldb/errors",
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator",
|
||||
"github.com/syndtr/goleveldb/leveldb/opt",
|
||||
"github.com/tendermint/ed25519",
|
||||
"github.com/tendermint/ed25519/extra25519",
|
||||
"github.com/tendermint/btcd/btcec",
|
||||
"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",
|
||||
|
||||
11
Gopkg.toml
11
Gopkg.toml
@@ -58,7 +58,7 @@
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/go-amino"
|
||||
version = "=v0.10.1"
|
||||
version = "v0.13.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
@@ -72,6 +72,11 @@
|
||||
## Some repos dont have releases.
|
||||
## Pin to revision
|
||||
|
||||
[[constraint]]
|
||||
name = "golang.org/x/crypto"
|
||||
source = "github.com/tendermint/crypto"
|
||||
revision = "3764759f34a542a3aef74d6b02e35be7ab893bba"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/jmhodges/levigo"
|
||||
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
|
||||
@@ -85,6 +90,10 @@
|
||||
name = "github.com/btcsuite/btcutil"
|
||||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/btcd"
|
||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
||||
|
||||
# Haven't made a release since 2016.
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
|
||||
48
Makefile
48
Makefile
@@ -1,16 +1,18 @@
|
||||
GOTOOLS = \
|
||||
github.com/mitchellh/gox \
|
||||
github.com/golang/dep/cmd/dep \
|
||||
gopkg.in/alecthomas/gometalinter.v2 \
|
||||
github.com/alecthomas/gometalinter \
|
||||
github.com/gogo/protobuf/protoc-gen-gogo \
|
||||
github.com/gogo/protobuf/gogoproto \
|
||||
github.com/square/certstrap
|
||||
GOBIN?=${GOPATH}/bin
|
||||
PACKAGES=$(shell go list ./...)
|
||||
|
||||
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`"
|
||||
|
||||
LINT_FLAGS = --exclude '.*\.pb\.go' --exclude 'vendor/*' --vendor --deadline=600s
|
||||
|
||||
all: check build test install
|
||||
|
||||
check: check_tools get_vendor_deps
|
||||
@@ -22,28 +24,32 @@ check: check_tools get_vendor_deps
|
||||
build:
|
||||
CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o build/tendermint ./cmd/tendermint/
|
||||
|
||||
build_c:
|
||||
CGO_ENABLED=1 go build $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" -o build/tendermint ./cmd/tendermint/
|
||||
|
||||
build_race:
|
||||
CGO_ENABLED=0 go build -race $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o build/tendermint ./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
|
||||
|
||||
########################################
|
||||
### Protobuf
|
||||
|
||||
protoc_all: protoc_libs protoc_abci protoc_grpc
|
||||
protoc_all: protoc_libs protoc_merkle protoc_abci protoc_grpc
|
||||
|
||||
%.pb.go: %.proto
|
||||
## If you get the following error,
|
||||
## "error while loading shared libraries: libprotobuf.so.14: cannot open shared object file: No such file or directory"
|
||||
## See https://stackoverflow.com/a/25518702
|
||||
## Note the $< here is substituted for the %.proto
|
||||
## Note the $@ here is substituted for the %.pb.go
|
||||
protoc $(INCLUDE) $< --gogo_out=Mgoogle/protobuf/timestamp.proto=github.com/golang/protobuf/ptypes/timestamp,plugins=grpc:.
|
||||
@echo "--> adding nolint declarations to protobuf generated files"
|
||||
@awk -i inplace '/^\s*package \w+/ { print "//nolint" }1' $@
|
||||
|
||||
########################################
|
||||
### Build ABCI
|
||||
|
||||
# see protobuf section above
|
||||
protoc_abci: abci/types/types.pb.go
|
||||
|
||||
build_abci:
|
||||
@@ -70,12 +76,13 @@ check_tools:
|
||||
|
||||
get_tools:
|
||||
@echo "--> Installing tools"
|
||||
go get -u -v $(GOTOOLS)
|
||||
@gometalinter.v2 --install
|
||||
./scripts/get_tools.sh
|
||||
@echo "--> Downloading linters (this may take awhile)"
|
||||
$(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN)
|
||||
|
||||
update_tools:
|
||||
@echo "--> Updating tools"
|
||||
@go get -u $(GOTOOLS)
|
||||
./scripts/get_tools.sh
|
||||
|
||||
#Update dependencies
|
||||
get_vendor_deps:
|
||||
@@ -85,13 +92,15 @@ get_vendor_deps:
|
||||
#For ABCI and libs
|
||||
get_protoc:
|
||||
@# https://github.com/google/protobuf/releases
|
||||
curl -L https://github.com/google/protobuf/releases/download/v3.4.1/protobuf-cpp-3.4.1.tar.gz | tar xvz && \
|
||||
cd protobuf-3.4.1 && \
|
||||
curl -L https://github.com/google/protobuf/releases/download/v3.6.1/protobuf-cpp-3.6.1.tar.gz | tar xvz && \
|
||||
cd protobuf-3.6.1 && \
|
||||
DIST_LANG=cpp ./configure && \
|
||||
make && \
|
||||
make install && \
|
||||
make check && \
|
||||
sudo make install && \
|
||||
sudo ldconfig && \
|
||||
cd .. && \
|
||||
rm -rf protobuf-3.4.1
|
||||
rm -rf protobuf-3.6.1
|
||||
|
||||
draw_deps:
|
||||
@# requires brew install graphviz or apt-get install graphviz
|
||||
@@ -130,6 +139,8 @@ grpc_dbserver:
|
||||
|
||||
protoc_grpc: rpc/grpc/types.pb.go
|
||||
|
||||
protoc_merkle: crypto/merkle/merkle.pb.go
|
||||
|
||||
########################################
|
||||
### Testing
|
||||
|
||||
@@ -173,6 +184,9 @@ test_p2p:
|
||||
cd ..
|
||||
# requires 'tester' the image from above
|
||||
bash test/p2p/test.sh tester
|
||||
# the `docker cp` takes a really long time; uncomment for debugging
|
||||
#
|
||||
# mkdir -p test/p2p/logs && docker cp rsyslog:/var/log test/p2p/logs
|
||||
|
||||
test_integrations:
|
||||
make build_docker_test_image
|
||||
@@ -200,11 +214,11 @@ vagrant_test:
|
||||
### go tests
|
||||
test:
|
||||
@echo "--> Running go test"
|
||||
@GOCACHE=off go test $(PACKAGES)
|
||||
@GOCACHE=off go test -p 1 $(PACKAGES)
|
||||
|
||||
test_race:
|
||||
@echo "--> Running go test --race"
|
||||
@go test -v -race $(PACKAGES)
|
||||
@GOCACHE=off go test -p 1 -v -race $(PACKAGES)
|
||||
|
||||
|
||||
########################################
|
||||
@@ -215,7 +229,7 @@ fmt:
|
||||
|
||||
metalinter:
|
||||
@echo "--> Running linter"
|
||||
@gometalinter.v2 --vendor --deadline=600s --disable-all \
|
||||
@gometalinter $(LINT_FLAGS) --disable-all \
|
||||
--enable=deadcode \
|
||||
--enable=gosimple \
|
||||
--enable=misspell \
|
||||
@@ -244,7 +258,7 @@ metalinter:
|
||||
|
||||
metalinter_all:
|
||||
@echo "--> Running linter (all)"
|
||||
gometalinter.v2 --vendor --deadline=600s --enable-all --disable=lll ./...
|
||||
gometalinter $(LINT_FLAGS) --enable-all --disable=lll ./...
|
||||
|
||||
DESTINATION = ./index.html.md
|
||||
|
||||
|
||||
44
README.md
44
README.md
@@ -8,7 +8,7 @@ Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)) for short.
|
||||
[](https://godoc.org/github.com/tendermint/tendermint)
|
||||
[](https://github.com/moovweb/gvm)
|
||||
[](https://github.com/moovweb/gvm)
|
||||
[](https://riot.im/app/#/room/#tendermint:matrix.org)
|
||||
[](https://github.com/tendermint/tendermint/blob/master/LICENSE)
|
||||
[](https://github.com/tendermint/tendermint)
|
||||
@@ -22,7 +22,10 @@ develop | [ middleware that takes a state transition machine - written in any programming language -
|
||||
and securely replicates it on many machines.
|
||||
|
||||
For protocol details, see [the specification](/docs/spec). For a consensus proof and detailed protocol analysis checkout our recent paper, "[The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)".
|
||||
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
|
||||
|
||||
@@ -30,7 +33,7 @@ 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*.
|
||||
Thus, we tag the releases as *alpha software*.
|
||||
|
||||
In any case, if you intend to run Tendermint in production,
|
||||
please [contact us](https://riot.im/app/#/room/#tendermint:matrix.org) :)
|
||||
@@ -46,7 +49,7 @@ For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.
|
||||
|
||||
Requirement|Notes
|
||||
---|---
|
||||
Go version | Go1.9 or higher
|
||||
Go version | Go1.10 or higher
|
||||
|
||||
## Install
|
||||
|
||||
@@ -54,10 +57,10 @@ See the [install instructions](/docs/introduction/install.md)
|
||||
|
||||
## Quick Start
|
||||
|
||||
- [Single node](/docs/using-tendermint.md)
|
||||
- [Single node](/docs/tendermint-core/using-tendermint.md)
|
||||
- [Local cluster using docker-compose](/networks/local)
|
||||
- [Remote cluster using terraform and ansible](/docs/networks/terraform-and-ansible.md)
|
||||
- [Join the public testnet](https://cosmos.network/testnet)
|
||||
- [Join the Cosmos testnet](https://cosmos.network/testnet)
|
||||
|
||||
## Resources
|
||||
|
||||
@@ -66,30 +69,31 @@ See the [install instructions](/docs/introduction/install.md)
|
||||
For details about the blockchain data structures and the p2p protocols, see the
|
||||
the [Tendermint specification](/docs/spec).
|
||||
|
||||
For details on using the software, [Read The Docs](https://tendermint.readthedocs.io/en/master/).
|
||||
Additional information about some - and eventually all - of the sub-projects below, can be found at Read The Docs.
|
||||
For details on using the software, see the [documentation](/docs/) which is also
|
||||
hosted at: https://tendermint.com/docs/
|
||||
|
||||
### Tools
|
||||
|
||||
Benchmarking and monitoring is provided by `tm-bench` and `tm-monitor`, respectively.
|
||||
Their code is found [here](/tools) and these binaries need to be built seperately.
|
||||
Additional documentation is found [here](/docs/tools).
|
||||
|
||||
### Sub-projects
|
||||
|
||||
* [Amino](http://github.com/tendermint/go-amino), a reflection-based improvement on proto3
|
||||
* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation
|
||||
|
||||
### Tools
|
||||
* [Deployment, Benchmarking, and Monitoring](http://tendermint.readthedocs.io/projects/tools/en/develop/index.html#tendermint-tools)
|
||||
|
||||
### Applications
|
||||
|
||||
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
|
||||
* [Ethermint](http://github.com/tendermint/ethermint); Ethereum on Tendermint
|
||||
* [Many more](https://tendermint.readthedocs.io/en/master/ecosystem.html#abci-applications)
|
||||
* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint
|
||||
* [Many more](https://tendermint.com/ecosystem)
|
||||
|
||||
### More
|
||||
### Research
|
||||
|
||||
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
|
||||
* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
|
||||
* [Tendermint Blog](https://blog.cosmos.network/tendermint/home)
|
||||
* [Cosmos Blog](https://blog.cosmos.network)
|
||||
* [Blog](https://blog.cosmos.network/tendermint/home)
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -114,6 +118,12 @@ CHANGELOG even if they don't lead to MINOR version bumps:
|
||||
- rpc/client
|
||||
- config
|
||||
- node
|
||||
- libs
|
||||
- bech32
|
||||
- common
|
||||
- db
|
||||
- errors
|
||||
- log
|
||||
|
||||
Exported objects in these packages that are not covered by the versioning scheme
|
||||
are explicitly marked by `// UNSTABLE` in their go doc comment and may change at any
|
||||
@@ -130,6 +140,8 @@ data into the new chain.
|
||||
However, any bump in the PATCH version should be compatible with existing histories
|
||||
(if not please open an [issue](https://github.com/tendermint/tendermint/issues)).
|
||||
|
||||
For more information on upgrading, see [here](./UPGRADING.md)
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please read, understand and adhere to our [code of conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
148
UPGRADING.md
Normal file
148
UPGRADING.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Upgrading Tendermint Core
|
||||
|
||||
This guide provides steps to be followed when you upgrade your applications to
|
||||
a newer version of Tendermint Core.
|
||||
|
||||
## v0.26.0
|
||||
|
||||
New 0.26.0 release contains a lot of changes to core data types. It is not
|
||||
compatible to the old versions and there is no straight forward way to update
|
||||
old data to be compatible with the new version.
|
||||
|
||||
To reset the state do:
|
||||
|
||||
```
|
||||
$ tendermint unsafe_reset_all
|
||||
```
|
||||
|
||||
Here we summarize some other notable changes to be mindful of.
|
||||
|
||||
### Config Changes
|
||||
|
||||
All timeouts must be changed from integers to strings with their duration, for
|
||||
instance `flush_throttle_timeout = 100` would be changed to
|
||||
`flush_throttle_timeout = "100ms"` and `timeout_propose = 3000` would be changed
|
||||
to `timeout_propose = "3s"`.
|
||||
|
||||
### RPC Changes
|
||||
|
||||
The default behaviour of `/abci_query` has been changed to not return a proof,
|
||||
and the name of the parameter that controls this has been changed from `trusted`
|
||||
to `prove`. To get proofs with your queries, ensure you set `prove=true`.
|
||||
|
||||
Various version fields like `amino_version`, `p2p_version`, `consensus_version`,
|
||||
and `rpc_version` have been removed from the `node_info.other` and are
|
||||
consolidated under the tendermint semantic version (ie. `node_info.version`) and
|
||||
the new `block` and `p2p` protocol versions under `node_info.protocol_version`..
|
||||
|
||||
### ABCI Changes
|
||||
|
||||
Field numbers were bumped in the `Header` and `ResponseInfo` messages to make
|
||||
room for new `version` fields. It should be straight forward to recompile the
|
||||
protobuf file for these changes.
|
||||
|
||||
#### Proofs
|
||||
|
||||
The `ResponseQuery.Proof` field is now structured as a `[]ProofOp` to support
|
||||
generalized Merkle tree constructions where the leaves of one Merkle tree are
|
||||
the root of another. If you don't need this functionaluty, and you used to
|
||||
return `<proof bytes>` here, you should instead return a single `ProofOp` with
|
||||
just the `Data` field set:
|
||||
|
||||
```
|
||||
[]ProofOp{
|
||||
ProofOp{
|
||||
Data: <proof bytes>,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For more information, see:
|
||||
|
||||
- [ADR-026](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/docs/architecture/adr-026-general-merkle-proof.md)
|
||||
- [Relevant ABCI
|
||||
documentation](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/docs/spec/abci/apps.md#query-proofs)
|
||||
- [Description of
|
||||
keys](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/crypto/merkle/proof_key_path.go#L14)
|
||||
|
||||
### Go API Changes
|
||||
|
||||
#### crypto.merkle
|
||||
|
||||
The `merkle.Hasher` interface was removed. Functions which used to take `Hasher`
|
||||
now simply take `[]byte`. This means that any objects being Merklized should be
|
||||
serialized before they are passed in.
|
||||
|
||||
#### node
|
||||
|
||||
The `node.RunForever` function was removed. Signal handling and running forever
|
||||
should instead be explicitly configured by the caller. See how we do it
|
||||
[here](https://github.com/tendermint/tendermint/blob/30519e8361c19f4bf320ef4d26288ebc621ad725/cmd/tendermint/commands/run_node.go#L60).
|
||||
|
||||
## v0.25.0
|
||||
|
||||
This release has minimal impact.
|
||||
|
||||
If you use GasWanted in ABCI and want to enforce it, set the MaxGas in the genesis file (default is no max).
|
||||
|
||||
## v0.24.0
|
||||
|
||||
New 0.24.0 release contains a lot of changes to the state and types. It's not
|
||||
compatible to the old versions and there is no straight forward way to update
|
||||
old data to be compatible with the new version.
|
||||
|
||||
To reset the state do:
|
||||
|
||||
```
|
||||
$ tendermint unsafe_reset_all
|
||||
```
|
||||
|
||||
Here we summarize some other notable changes to be mindful of.
|
||||
|
||||
### Config changes
|
||||
|
||||
`p2p.max_num_peers` was removed in favor of `p2p.max_num_inbound_peers` and
|
||||
`p2p.max_num_outbound_peers`.
|
||||
|
||||
```
|
||||
# Maximum number of inbound peers
|
||||
max_num_inbound_peers = 40
|
||||
|
||||
# Maximum number of outbound peers to connect to, excluding persistent peers
|
||||
max_num_outbound_peers = 10
|
||||
```
|
||||
|
||||
As you can see, the default ratio of inbound/outbound peers is 4/1. The reason
|
||||
is we want it to be easier for new nodes to connect to the network. You can
|
||||
tweak these parameters to alter the network topology.
|
||||
|
||||
### RPC Changes
|
||||
|
||||
The result of `/commit` used to contain `header` and `commit` fields at the top level. These are now contained under the `signed_header` field.
|
||||
|
||||
### ABCI Changes
|
||||
|
||||
The header has been upgraded and contains new fields, but none of the existing
|
||||
fields were changed, except their order.
|
||||
|
||||
The `Validator` type was split into two, one containing an `Address` and one
|
||||
containing a `PubKey`. When processing `RequestBeginBlock`, use the `Validator`
|
||||
type, which contains just the `Address`. When returning `ResponseEndBlock`, use
|
||||
the `ValidatorUpdate` type, which contains just the `PubKey`.
|
||||
|
||||
### Validator Set Updates
|
||||
|
||||
Validator set updates returned in ResponseEndBlock for height `H` used to take
|
||||
effect immediately at height `H+1`. Now they will be delayed one block, to take
|
||||
effect at height `H+2`. Note this means that the change will be seen by the ABCI
|
||||
app in the `RequestBeginBlock.LastCommitInfo` at block `H+3`. Apps were already
|
||||
required to maintain a map from validator addresses to pubkeys since v0.23 (when
|
||||
pubkeys were removed from RequestBeginBlock), but now they may need to track
|
||||
multiple validator sets at once to accomodate this delay.
|
||||
|
||||
|
||||
### Block Size
|
||||
|
||||
The `ConsensusParams.BlockSize.MaxTxs` was removed in favour of
|
||||
`ConsensusParams.BlockSize.MaxBytes`, which is now enforced. This means blocks
|
||||
are limitted only by byte-size, not by number of transactions.
|
||||
6
Vagrantfile
vendored
6
Vagrantfile
vendored
@@ -29,10 +29,10 @@ Vagrant.configure("2") do |config|
|
||||
usermod -a -G docker vagrant
|
||||
|
||||
# install go
|
||||
wget -q https://dl.google.com/go/go1.10.1.linux-amd64.tar.gz
|
||||
tar -xvf go1.10.1.linux-amd64.tar.gz
|
||||
wget -q https://dl.google.com/go/go1.11.linux-amd64.tar.gz
|
||||
tar -xvf go1.11.linux-amd64.tar.gz
|
||||
mv go /usr/local
|
||||
rm -f go1.10.1.linux-amd64.tar.gz
|
||||
rm -f go1.11.linux-amd64.tar.gz
|
||||
|
||||
# cleanup
|
||||
apt-get autoremove -y
|
||||
|
||||
@@ -17,14 +17,14 @@ The community has provided a number of addtional implementations, see the [Tende
|
||||
A detailed description of the ABCI methods and message types is contained in:
|
||||
|
||||
- [A prose specification](specification.md)
|
||||
- [A protobuf file](https://github.com/tendermint/abci/blob/master/types/types.proto)
|
||||
- [A Go interface](https://github.com/tendermint/abci/blob/master/types/application.go).
|
||||
- [A protobuf file](https://github.com/tendermint/tendermint/blob/master/abci/types/types.proto)
|
||||
- [A Go interface](https://github.com/tendermint/tendermint/blob/master/abci/types/application.go).
|
||||
|
||||
For more background information on ABCI, motivations, and tendermint, please visit [the documentation](http://tendermint.readthedocs.io/en/master/).
|
||||
For more background information on ABCI, motivations, and tendermint, please visit [the documentation](https://tendermint.com/docs/).
|
||||
The two guides to focus on are the `Application Development Guide` and `Using ABCI-CLI`.
|
||||
|
||||
|
||||
## Protocl Buffers
|
||||
## Protocol Buffers
|
||||
|
||||
To compile the protobuf file, run:
|
||||
|
||||
@@ -42,10 +42,13 @@ The `abci-cli` is a simple tool for debugging ABCI servers and running some
|
||||
example apps. To install it:
|
||||
|
||||
```
|
||||
go get github.com/tendermint/abci
|
||||
cd $GOPATH/src/github.com/tendermint/abci
|
||||
mkdir -p $GOPATH/src/github.com/tendermint
|
||||
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
|
||||
make install_abci
|
||||
```
|
||||
|
||||
## Implementation
|
||||
@@ -91,7 +94,7 @@ Note the length-prefixing used in the socket implementation does not apply for G
|
||||
The `abci-cli` tool wraps an ABCI client and can be used for probing/testing an ABCI server.
|
||||
For instance, `abci-cli test` will run a test sequence against a listening server running the Counter application (see below).
|
||||
It can also be used to run some example applications.
|
||||
See [the documentation](http://tendermint.readthedocs.io/en/master/) for more details.
|
||||
See [the documentation](https://tendermint.com/docs/) for more details.
|
||||
|
||||
### Examples
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ type grpcClient struct {
|
||||
mustConnect bool
|
||||
|
||||
client types.ABCIApplicationClient
|
||||
conn *grpc.ClientConn
|
||||
|
||||
mtx sync.Mutex
|
||||
addr string
|
||||
@@ -60,6 +61,7 @@ RETRY_LOOP:
|
||||
|
||||
cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
|
||||
client := types.NewABCIApplicationClient(conn)
|
||||
cli.conn = conn
|
||||
|
||||
ENSURE_CONNECTED:
|
||||
for {
|
||||
@@ -78,12 +80,10 @@ RETRY_LOOP:
|
||||
|
||||
func (cli *grpcClient) OnStop() {
|
||||
cli.BaseService.OnStop()
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
// TODO: how to close conn? its not a net.Conn and grpc doesn't expose a Close()
|
||||
/*if cli.client.conn != nil {
|
||||
cli.client.conn.Close()
|
||||
}*/
|
||||
|
||||
if cli.conn != nil {
|
||||
cli.conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *grpcClient) StopForError(err error) {
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
servertest "github.com/tendermint/tendermint/abci/tests/server"
|
||||
"github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/abci/version"
|
||||
"github.com/tendermint/tendermint/crypto/merkle"
|
||||
)
|
||||
|
||||
// client is a global variable so it can be reused by the console
|
||||
@@ -100,7 +101,7 @@ type queryResponse struct {
|
||||
Key []byte
|
||||
Value []byte
|
||||
Height int64
|
||||
Proof []byte
|
||||
Proof *merkle.Proof
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
@@ -477,11 +478,8 @@ func muxOnCommands(cmd *cobra.Command, pArgs []string) error {
|
||||
}
|
||||
|
||||
func cmdUnimplemented(cmd *cobra.Command, args []string) error {
|
||||
// TODO: Print out all the sub-commands available
|
||||
msg := "unimplemented command"
|
||||
if err := cmd.Help(); err != nil {
|
||||
msg = err.Error()
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
msg += fmt.Sprintf(" args: [%s]", strings.Join(args, " "))
|
||||
}
|
||||
@@ -489,6 +487,17 @@ func cmdUnimplemented(cmd *cobra.Command, args []string) error {
|
||||
Code: codeBad,
|
||||
Log: msg,
|
||||
})
|
||||
|
||||
fmt.Println("Available commands:")
|
||||
fmt.Printf("%s: %s\n", echoCmd.Use, echoCmd.Short)
|
||||
fmt.Printf("%s: %s\n", infoCmd.Use, infoCmd.Short)
|
||||
fmt.Printf("%s: %s\n", checkTxCmd.Use, checkTxCmd.Short)
|
||||
fmt.Printf("%s: %s\n", deliverTxCmd.Use, deliverTxCmd.Short)
|
||||
fmt.Printf("%s: %s\n", queryCmd.Use, queryCmd.Short)
|
||||
fmt.Printf("%s: %s\n", commitCmd.Use, commitCmd.Short)
|
||||
fmt.Printf("%s: %s\n", setOptionCmd.Use, setOptionCmd.Short)
|
||||
fmt.Println("Use \"[command] --help\" for more information about a command.")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -740,7 +749,7 @@ func printResponse(cmd *cobra.Command, args []string, rsp response) {
|
||||
fmt.Printf("-> value.hex: %X\n", rsp.Query.Value)
|
||||
}
|
||||
if rsp.Query.Proof != nil {
|
||||
fmt.Printf("-> proof: %X\n", rsp.Query.Proof)
|
||||
fmt.Printf("-> proof: %#v\n", rsp.Query.Proof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,4 +6,5 @@ const (
|
||||
CodeTypeEncodingError uint32 = 1
|
||||
CodeTypeBadNonce uint32 = 2
|
||||
CodeTypeUnauthorized uint32 = 3
|
||||
CodeTypeUnknownError uint32 = 4
|
||||
)
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/code"
|
||||
"github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
type CounterApplication struct {
|
||||
@@ -22,7 +21,7 @@ func NewCounterApplication(serial bool) *CounterApplication {
|
||||
}
|
||||
|
||||
func (app *CounterApplication) Info(req types.RequestInfo) types.ResponseInfo {
|
||||
return types.ResponseInfo{Data: cmn.Fmt("{\"hashes\":%v,\"txs\":%v}", app.hashCount, app.txCount)}
|
||||
return types.ResponseInfo{Data: fmt.Sprintf("{\"hashes\":%v,\"txs\":%v}", app.hashCount, app.txCount)}
|
||||
}
|
||||
|
||||
func (app *CounterApplication) SetOption(req types.RequestSetOption) types.ResponseSetOption {
|
||||
@@ -34,7 +33,7 @@ func (app *CounterApplication) SetOption(req types.RequestSetOption) types.Respo
|
||||
TODO Panic and have the ABCI server pass an exception.
|
||||
The client can call SetOptionSync() and get an `error`.
|
||||
return types.ResponseSetOption{
|
||||
Error: cmn.Fmt("Unknown key (%s) or value (%s)", key, value),
|
||||
Error: fmt.Sprintf("Unknown key (%s) or value (%s)", key, value),
|
||||
}
|
||||
*/
|
||||
return types.ResponseSetOption{}
|
||||
@@ -95,10 +94,10 @@ func (app *CounterApplication) Commit() (resp types.ResponseCommit) {
|
||||
func (app *CounterApplication) Query(reqQuery types.RequestQuery) types.ResponseQuery {
|
||||
switch reqQuery.Path {
|
||||
case "hash":
|
||||
return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.hashCount))}
|
||||
return types.ResponseQuery{Value: []byte(fmt.Sprintf("%v", app.hashCount))}
|
||||
case "tx":
|
||||
return types.ResponseQuery{Value: []byte(cmn.Fmt("%v", app.txCount))}
|
||||
return types.ResponseQuery{Value: []byte(fmt.Sprintf("%v", app.txCount))}
|
||||
default:
|
||||
return types.ResponseQuery{Log: cmn.Fmt("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)}
|
||||
return types.ResponseQuery{Log: fmt.Sprintf("Invalid query path. Expected hash or tx, got %v", reqQuery.Path)}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestGRPC(t *testing.T) {
|
||||
}
|
||||
|
||||
func testStream(t *testing.T, app types.Application) {
|
||||
numDeliverTxs := 200000
|
||||
numDeliverTxs := 20000
|
||||
|
||||
// Start the listener
|
||||
server := abciserver.NewSocketServer("unix://test.sock", app)
|
||||
@@ -72,7 +72,7 @@ func testStream(t *testing.T, app types.Application) {
|
||||
}
|
||||
if counter == numDeliverTxs {
|
||||
go func() {
|
||||
time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow
|
||||
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
|
||||
close(done)
|
||||
}()
|
||||
return
|
||||
@@ -148,7 +148,7 @@ func testGRPCSync(t *testing.T, app *types.GRPCApplication) {
|
||||
t.Log("response", counter)
|
||||
if counter == numDeliverTxs {
|
||||
go func() {
|
||||
time.Sleep(time.Second * 2) // Wait for a bit to allow counter overflow
|
||||
time.Sleep(time.Second * 1) // Wait for a bit to allow counter overflow
|
||||
}()
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,10 @@ import (
|
||||
|
||||
// RandVal creates one random validator, with a key derived
|
||||
// from the input value
|
||||
func RandVal(i int) types.Validator {
|
||||
addr := cmn.RandBytes(20)
|
||||
func RandVal(i int) types.ValidatorUpdate {
|
||||
pubkey := cmn.RandBytes(32)
|
||||
power := cmn.RandUint16() + 1
|
||||
v := types.Ed25519Validator(pubkey, int64(power))
|
||||
v.Address = addr
|
||||
v := types.Ed25519ValidatorUpdate(pubkey, int64(power))
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -20,8 +18,8 @@ func RandVal(i int) types.Validator {
|
||||
// the application. Note that the keys are deterministically
|
||||
// derived from the index in the array, while the power is
|
||||
// random (Change this if not desired)
|
||||
func RandVals(cnt int) []types.Validator {
|
||||
res := make([]types.Validator, cnt)
|
||||
func RandVals(cnt int) []types.ValidatorUpdate {
|
||||
res := make([]types.ValidatorUpdate, cnt)
|
||||
for i := 0; i < cnt; i++ {
|
||||
res[i] = RandVal(i)
|
||||
}
|
||||
|
||||
@@ -10,11 +10,14 @@ import (
|
||||
"github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
var (
|
||||
stateKey = []byte("stateKey")
|
||||
kvPairPrefixKey = []byte("kvPairKey:")
|
||||
|
||||
ProtocolVersion version.Protocol = 0x1
|
||||
)
|
||||
|
||||
type State struct {
|
||||
@@ -65,7 +68,11 @@ func NewKVStoreApplication() *KVStoreApplication {
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
// tx is either "key=value" or just arbitrary bytes
|
||||
@@ -81,14 +88,14 @@ func (app *KVStoreApplication) DeliverTx(tx []byte) types.ResponseDeliverTx {
|
||||
app.state.Size += 1
|
||||
|
||||
tags := []cmn.KVPair{
|
||||
{[]byte("app.creator"), []byte("jae")},
|
||||
{[]byte("app.key"), key},
|
||||
{Key: []byte("app.creator"), Value: []byte("Cosmoshi Netowoko")},
|
||||
{Key: []byte("app.key"), Value: key},
|
||||
}
|
||||
return types.ResponseDeliverTx{Code: code.CodeTypeOK, Tags: tags}
|
||||
}
|
||||
|
||||
func (app *KVStoreApplication) CheckTx(tx []byte) types.ResponseCheckTx {
|
||||
return types.ResponseCheckTx{Code: code.CodeTypeOK}
|
||||
return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
|
||||
}
|
||||
|
||||
func (app *KVStoreApplication) Commit() types.ResponseCommit {
|
||||
@@ -114,6 +121,7 @@ func (app *KVStoreApplication) Query(reqQuery types.RequestQuery) (resQuery type
|
||||
}
|
||||
return
|
||||
} else {
|
||||
resQuery.Key = reqQuery.Data
|
||||
value := app.state.db.Get(prefixKey(reqQuery.Data))
|
||||
resQuery.Value = value
|
||||
if value != nil {
|
||||
|
||||
@@ -2,6 +2,7 @@ package kvstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"sort"
|
||||
"testing"
|
||||
@@ -121,11 +122,11 @@ func TestValUpdates(t *testing.T) {
|
||||
vals1, vals2 := vals[:nInit], kvstore.Validators()
|
||||
valsEqual(t, vals1, vals2)
|
||||
|
||||
var v1, v2, v3 types.Validator
|
||||
var v1, v2, v3 types.ValidatorUpdate
|
||||
|
||||
// add some validators
|
||||
v1, v2 = vals[nInit], vals[nInit+1]
|
||||
diff := []types.Validator{v1, v2}
|
||||
diff := []types.ValidatorUpdate{v1, v2}
|
||||
tx1 := MakeValSetChangeTx(v1.PubKey, v1.Power)
|
||||
tx2 := MakeValSetChangeTx(v2.PubKey, v2.Power)
|
||||
|
||||
@@ -139,7 +140,7 @@ func TestValUpdates(t *testing.T) {
|
||||
v1.Power = 0
|
||||
v2.Power = 0
|
||||
v3.Power = 0
|
||||
diff = []types.Validator{v1, v2, v3}
|
||||
diff = []types.ValidatorUpdate{v1, v2, v3}
|
||||
tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
|
||||
tx2 = MakeValSetChangeTx(v2.PubKey, v2.Power)
|
||||
tx3 := MakeValSetChangeTx(v3.PubKey, v3.Power)
|
||||
@@ -157,18 +158,18 @@ func TestValUpdates(t *testing.T) {
|
||||
} else {
|
||||
v1.Power = 5
|
||||
}
|
||||
diff = []types.Validator{v1}
|
||||
diff = []types.ValidatorUpdate{v1}
|
||||
tx1 = MakeValSetChangeTx(v1.PubKey, v1.Power)
|
||||
|
||||
makeApplyBlock(t, kvstore, 3, diff, tx1)
|
||||
|
||||
vals1 = append([]types.Validator{v1}, vals1[1:]...)
|
||||
vals1 = append([]types.ValidatorUpdate{v1}, vals1[1:]...)
|
||||
vals2 = kvstore.Validators()
|
||||
valsEqual(t, vals1, vals2)
|
||||
|
||||
}
|
||||
|
||||
func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff []types.Validator, txs ...[]byte) {
|
||||
func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff []types.ValidatorUpdate, txs ...[]byte) {
|
||||
// make and apply block
|
||||
height := int64(heightInt)
|
||||
hash := []byte("foo")
|
||||
@@ -190,12 +191,12 @@ func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff
|
||||
}
|
||||
|
||||
// order doesn't matter
|
||||
func valsEqual(t *testing.T, vals1, vals2 []types.Validator) {
|
||||
func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) {
|
||||
if len(vals1) != len(vals2) {
|
||||
t.Fatalf("vals dont match in len. got %d, expected %d", len(vals2), len(vals1))
|
||||
}
|
||||
sort.Sort(types.Validators(vals1))
|
||||
sort.Sort(types.Validators(vals2))
|
||||
sort.Sort(types.ValidatorUpdates(vals1))
|
||||
sort.Sort(types.ValidatorUpdates(vals2))
|
||||
for i, v1 := range vals1 {
|
||||
v2 := vals2[i]
|
||||
if !bytes.Equal(v1.PubKey.Data, v2.PubKey.Data) ||
|
||||
@@ -207,7 +208,7 @@ func valsEqual(t *testing.T, vals1, vals2 []types.Validator) {
|
||||
|
||||
func makeSocketClientServer(app types.Application, name string) (abcicli.Client, cmn.Service, error) {
|
||||
// Start the listener
|
||||
socket := cmn.Fmt("unix://%s.sock", name)
|
||||
socket := fmt.Sprintf("unix://%s.sock", name)
|
||||
logger := log.TestingLogger()
|
||||
|
||||
server := abciserver.NewSocketServer(socket, app)
|
||||
@@ -229,7 +230,7 @@ func makeSocketClientServer(app types.Application, name string) (abcicli.Client,
|
||||
|
||||
func makeGRPCClientServer(app types.Application, name string) (abcicli.Client, cmn.Service, error) {
|
||||
// Start the listener
|
||||
socket := cmn.Fmt("unix://%s.sock", name)
|
||||
socket := fmt.Sprintf("unix://%s.sock", name)
|
||||
logger := log.TestingLogger()
|
||||
|
||||
gapp := types.NewGRPCApplication(app)
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/code"
|
||||
"github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
)
|
||||
@@ -26,7 +25,7 @@ type PersistentKVStoreApplication struct {
|
||||
app *KVStoreApplication
|
||||
|
||||
// validator set
|
||||
ValUpdates []types.Validator
|
||||
ValUpdates []types.ValidatorUpdate
|
||||
|
||||
logger log.Logger
|
||||
}
|
||||
@@ -102,7 +101,7 @@ func (app *PersistentKVStoreApplication) InitChain(req types.RequestInitChain) t
|
||||
// Track the block hash and header information
|
||||
func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
|
||||
// reset valset changes
|
||||
app.ValUpdates = make([]types.Validator, 0)
|
||||
app.ValUpdates = make([]types.ValidatorUpdate, 0)
|
||||
return types.ResponseBeginBlock{}
|
||||
}
|
||||
|
||||
@@ -114,11 +113,11 @@ func (app *PersistentKVStoreApplication) EndBlock(req types.RequestEndBlock) typ
|
||||
//---------------------------------------------
|
||||
// update validators
|
||||
|
||||
func (app *PersistentKVStoreApplication) Validators() (validators []types.Validator) {
|
||||
func (app *PersistentKVStoreApplication) Validators() (validators []types.ValidatorUpdate) {
|
||||
itr := app.app.state.db.Iterator(nil, nil)
|
||||
for ; itr.Valid(); itr.Next() {
|
||||
if isValidatorTx(itr.Key()) {
|
||||
validator := new(types.Validator)
|
||||
validator := new(types.ValidatorUpdate)
|
||||
err := types.ReadMessage(bytes.NewBuffer(itr.Value()), validator)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -130,7 +129,7 @@ func (app *PersistentKVStoreApplication) Validators() (validators []types.Valida
|
||||
}
|
||||
|
||||
func MakeValSetChangeTx(pubkey types.PubKey, power int64) []byte {
|
||||
return []byte(cmn.Fmt("val:%X/%d", pubkey.Data, power))
|
||||
return []byte(fmt.Sprintf("val:%X/%d", pubkey.Data, power))
|
||||
}
|
||||
|
||||
func isValidatorTx(tx []byte) bool {
|
||||
@@ -168,11 +167,11 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon
|
||||
}
|
||||
|
||||
// update
|
||||
return app.updateValidator(types.Ed25519Validator(pubkey, int64(power)))
|
||||
return app.updateValidator(types.Ed25519ValidatorUpdate(pubkey, int64(power)))
|
||||
}
|
||||
|
||||
// add, update, or remove a validator
|
||||
func (app *PersistentKVStoreApplication) updateValidator(v types.Validator) types.ResponseDeliverTx {
|
||||
func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx {
|
||||
key := []byte("val:" + string(v.PubKey.Data))
|
||||
if v.Power == 0 {
|
||||
// remove validator
|
||||
|
||||
@@ -12,11 +12,11 @@ import (
|
||||
|
||||
func InitChain(client abcicli.Client) error {
|
||||
total := 10
|
||||
vals := make([]types.Validator, total)
|
||||
vals := make([]types.ValidatorUpdate, total)
|
||||
for i := 0; i < total; i++ {
|
||||
pubkey := cmn.RandBytes(33)
|
||||
power := cmn.RandInt()
|
||||
vals[i] = types.Ed25519Validator(pubkey, int64(power))
|
||||
vals[i] = types.Ed25519ValidatorUpdate(pubkey, int64(power))
|
||||
}
|
||||
_, err := client.InitChainSync(types.RequestInitChain{
|
||||
Validators: vals,
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
-> code: OK
|
||||
-> log: exists
|
||||
-> height: 0
|
||||
-> key: abc
|
||||
-> key.hex: 616263
|
||||
-> value: abc
|
||||
-> value.hex: 616263
|
||||
|
||||
@@ -42,6 +44,8 @@
|
||||
-> code: OK
|
||||
-> log: exists
|
||||
-> height: 0
|
||||
-> key: def
|
||||
-> key.hex: 646566
|
||||
-> value: xyz
|
||||
-> value.hex: 78797A
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ func TestMarshalJSON(t *testing.T) {
|
||||
Data: []byte("hello"),
|
||||
GasWanted: 43,
|
||||
Tags: []cmn.KVPair{
|
||||
{[]byte("pho"), []byte("bo")},
|
||||
{Key: []byte("pho"), Value: []byte("bo")},
|
||||
},
|
||||
}
|
||||
b, err = json.Marshal(&r1)
|
||||
@@ -83,7 +83,7 @@ func TestWriteReadMessage2(t *testing.T) {
|
||||
Log: phrase,
|
||||
GasWanted: 10,
|
||||
Tags: []cmn.KVPair{
|
||||
cmn.KVPair{[]byte("abc"), []byte("def")},
|
||||
cmn.KVPair{Key: []byte("abc"), Value: []byte("def")},
|
||||
},
|
||||
},
|
||||
// TODO: add the rest
|
||||
|
||||
@@ -4,8 +4,8 @@ const (
|
||||
PubKeyEd25519 = "ed25519"
|
||||
)
|
||||
|
||||
func Ed25519Validator(pubkey []byte, power int64) Validator {
|
||||
return Validator{
|
||||
func Ed25519ValidatorUpdate(pubkey []byte, power int64) ValidatorUpdate {
|
||||
return ValidatorUpdate{
|
||||
// Address:
|
||||
PubKey: PubKey{
|
||||
Type: PubKeyEd25519,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ package types;
|
||||
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";
|
||||
|
||||
// This file is copied from http://github.com/tendermint/abci
|
||||
// NOTE: When using custom types, mind the warnings.
|
||||
@@ -48,6 +49,8 @@ message RequestFlush {
|
||||
|
||||
message RequestInfo {
|
||||
string version = 1;
|
||||
uint64 block_version = 2;
|
||||
uint64 p2p_version = 3;
|
||||
}
|
||||
|
||||
// nondeterministic
|
||||
@@ -60,7 +63,7 @@ message RequestInitChain {
|
||||
google.protobuf.Timestamp time = 1 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
|
||||
string chain_id = 2;
|
||||
ConsensusParams consensus_params = 3;
|
||||
repeated Validator validators = 4 [(gogoproto.nullable)=false];
|
||||
repeated ValidatorUpdate validators = 4 [(gogoproto.nullable)=false];
|
||||
bytes app_state_bytes = 5;
|
||||
}
|
||||
|
||||
@@ -128,9 +131,12 @@ message ResponseFlush {
|
||||
|
||||
message ResponseInfo {
|
||||
string data = 1;
|
||||
|
||||
string version = 2;
|
||||
int64 last_block_height = 3;
|
||||
bytes last_block_app_hash = 4;
|
||||
uint64 app_version = 3;
|
||||
|
||||
int64 last_block_height = 4;
|
||||
bytes last_block_app_hash = 5;
|
||||
}
|
||||
|
||||
// nondeterministic
|
||||
@@ -143,7 +149,7 @@ message ResponseSetOption {
|
||||
|
||||
message ResponseInitChain {
|
||||
ConsensusParams consensus_params = 1;
|
||||
repeated Validator validators = 2 [(gogoproto.nullable)=false];
|
||||
repeated ValidatorUpdate validators = 2 [(gogoproto.nullable)=false];
|
||||
}
|
||||
|
||||
message ResponseQuery {
|
||||
@@ -154,8 +160,9 @@ message ResponseQuery {
|
||||
int64 index = 5;
|
||||
bytes key = 6;
|
||||
bytes value = 7;
|
||||
bytes proof = 8;
|
||||
merkle.Proof proof = 8;
|
||||
int64 height = 9;
|
||||
string codespace = 10;
|
||||
}
|
||||
|
||||
message ResponseBeginBlock {
|
||||
@@ -170,6 +177,7 @@ message ResponseCheckTx {
|
||||
int64 gas_wanted = 5;
|
||||
int64 gas_used = 6;
|
||||
repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
|
||||
string codespace = 8;
|
||||
}
|
||||
|
||||
message ResponseDeliverTx {
|
||||
@@ -180,10 +188,11 @@ message ResponseDeliverTx {
|
||||
int64 gas_wanted = 5;
|
||||
int64 gas_used = 6;
|
||||
repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
|
||||
string codespace = 8;
|
||||
}
|
||||
|
||||
message ResponseEndBlock {
|
||||
repeated Validator validator_updates = 1 [(gogoproto.nullable)=false];
|
||||
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"];
|
||||
}
|
||||
@@ -200,67 +209,90 @@ message ResponseCommit {
|
||||
// that can be adjusted by the abci app
|
||||
message ConsensusParams {
|
||||
BlockSize block_size = 1;
|
||||
TxSize tx_size = 2;
|
||||
BlockGossip block_gossip = 3;
|
||||
EvidenceParams evidence_params = 2;
|
||||
}
|
||||
|
||||
// BlockSize contains limits on the block size.
|
||||
message BlockSize {
|
||||
int32 max_bytes = 1;
|
||||
int32 max_txs = 2;
|
||||
int64 max_gas = 3;
|
||||
}
|
||||
|
||||
// TxSize contains limits on the tx size.
|
||||
message TxSize {
|
||||
int32 max_bytes = 1;
|
||||
// Note: must be greater than 0
|
||||
int64 max_bytes = 1;
|
||||
// Note: must be greater or equal to -1
|
||||
int64 max_gas = 2;
|
||||
}
|
||||
|
||||
// BlockGossip determine consensus critical
|
||||
// elements of how blocks are gossiped
|
||||
message BlockGossip {
|
||||
// Note: must not be 0
|
||||
int32 block_part_size_bytes = 1;
|
||||
// EvidenceParams contains limits on the evidence.
|
||||
message EvidenceParams {
|
||||
// Note: must be greater than 0
|
||||
int64 max_age = 1;
|
||||
}
|
||||
|
||||
message LastCommitInfo {
|
||||
int32 commit_round = 1;
|
||||
repeated SigningValidator validators = 2 [(gogoproto.nullable)=false];
|
||||
int32 round = 1;
|
||||
repeated VoteInfo votes = 2 [(gogoproto.nullable)=false];
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Blockchain Types
|
||||
|
||||
// just the minimum the app might need
|
||||
message Header {
|
||||
// basics
|
||||
string chain_id = 1 [(gogoproto.customname)="ChainID"];
|
||||
int64 height = 2;
|
||||
google.protobuf.Timestamp time = 3 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
|
||||
// basic block info
|
||||
Version version = 1 [(gogoproto.nullable)=false];
|
||||
string chain_id = 2 [(gogoproto.customname)="ChainID"];
|
||||
int64 height = 3;
|
||||
google.protobuf.Timestamp time = 4 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
|
||||
int64 num_txs = 5;
|
||||
int64 total_txs = 6;
|
||||
|
||||
// txs
|
||||
int32 num_txs = 4;
|
||||
int64 total_txs = 5;
|
||||
// prev block info
|
||||
BlockID last_block_id = 7 [(gogoproto.nullable)=false];
|
||||
|
||||
// hashes
|
||||
bytes last_block_hash = 6;
|
||||
bytes validators_hash = 7;
|
||||
bytes app_hash = 8;
|
||||
// hashes of block data
|
||||
bytes last_commit_hash = 8; // commit from validators from the last block
|
||||
bytes data_hash = 9; // transactions
|
||||
|
||||
// consensus
|
||||
Validator proposer = 9 [(gogoproto.nullable)=false];
|
||||
// hashes from the app output from the prev block
|
||||
bytes validators_hash = 10; // validators for the current block
|
||||
bytes next_validators_hash = 11; // validators for the next block
|
||||
bytes consensus_hash = 12; // consensus params for current block
|
||||
bytes app_hash = 13; // state after txs from the previous block
|
||||
bytes last_results_hash = 14;// root hash of all results from the txs from the previous block
|
||||
|
||||
// consensus info
|
||||
bytes evidence_hash = 15; // evidence included in the block
|
||||
bytes proposer_address = 16; // original proposer of the block
|
||||
}
|
||||
|
||||
message Version {
|
||||
uint64 Block = 1;
|
||||
uint64 App = 2;
|
||||
}
|
||||
|
||||
|
||||
message BlockID {
|
||||
bytes hash = 1;
|
||||
PartSetHeader parts_header = 2 [(gogoproto.nullable)=false];
|
||||
}
|
||||
|
||||
message PartSetHeader {
|
||||
int32 total = 1;
|
||||
bytes hash = 2;
|
||||
}
|
||||
|
||||
// Validator
|
||||
message Validator {
|
||||
bytes address = 1;
|
||||
PubKey pub_key = 2 [(gogoproto.nullable)=false];
|
||||
//PubKey pub_key = 2 [(gogoproto.nullable)=false];
|
||||
int64 power = 3;
|
||||
}
|
||||
|
||||
// Validator with an extra bool
|
||||
message SigningValidator {
|
||||
// ValidatorUpdate
|
||||
message ValidatorUpdate {
|
||||
PubKey pub_key = 1 [(gogoproto.nullable)=false];
|
||||
int64 power = 2;
|
||||
}
|
||||
|
||||
// VoteInfo
|
||||
message VoteInfo {
|
||||
Validator validator = 1 [(gogoproto.nullable)=false];
|
||||
bool signed_last_block = 2;
|
||||
}
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
asrt "github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestConsensusParams(t *testing.T) {
|
||||
assert := asrt.New(t)
|
||||
|
||||
params := &ConsensusParams{
|
||||
BlockSize: &BlockSize{MaxGas: 12345},
|
||||
BlockGossip: &BlockGossip{BlockPartSizeBytes: 54321},
|
||||
}
|
||||
var noParams *ConsensusParams // nil
|
||||
|
||||
// no error with nil fields
|
||||
assert.Nil(noParams.GetBlockSize())
|
||||
assert.EqualValues(noParams.GetBlockSize().GetMaxGas(), 0)
|
||||
|
||||
// get values with real fields
|
||||
assert.NotNil(params.GetBlockSize())
|
||||
assert.EqualValues(params.GetBlockSize().GetMaxTxs(), 0)
|
||||
assert.EqualValues(params.GetBlockSize().GetMaxGas(), 12345)
|
||||
assert.NotNil(params.GetBlockGossip())
|
||||
assert.EqualValues(params.GetBlockGossip().GetBlockPartSizeBytes(), 54321)
|
||||
assert.Nil(params.GetTxSize())
|
||||
assert.EqualValues(params.GetTxSize().GetMaxBytes(), 0)
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
import _ "github.com/golang/protobuf/ptypes/timestamp"
|
||||
import _ "github.com/tendermint/tendermint/crypto/merkle"
|
||||
import _ "github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@@ -1534,15 +1535,15 @@ func TestBlockSizeMarshalTo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxSizeProto(t *testing.T) {
|
||||
func TestEvidenceParamsProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedTxSize(popr, false)
|
||||
p := NewPopulatedEvidenceParams(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &TxSize{}
|
||||
msg := &EvidenceParams{}
|
||||
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -1565,10 +1566,10 @@ func TestTxSizeProto(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxSizeMarshalTo(t *testing.T) {
|
||||
func TestEvidenceParamsMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedTxSize(popr, false)
|
||||
p := NewPopulatedEvidenceParams(popr, false)
|
||||
size := p.Size()
|
||||
dAtA := make([]byte, size)
|
||||
for i := range dAtA {
|
||||
@@ -1578,63 +1579,7 @@ func TestTxSizeMarshalTo(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &TxSize{}
|
||||
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 TestBlockGossipProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockGossip(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &BlockGossip{}
|
||||
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 TestBlockGossipMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockGossip(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 := &BlockGossip{}
|
||||
msg := &EvidenceParams{}
|
||||
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -1758,6 +1703,174 @@ func TestHeaderMarshalTo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVersion(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &Version{}
|
||||
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 TestVersionMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVersion(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 := &Version{}
|
||||
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 TestBlockIDProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockID(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &BlockID{}
|
||||
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 TestBlockIDMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockID(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 := &BlockID{}
|
||||
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 TestPartSetHeaderProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedPartSetHeader(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &PartSetHeader{}
|
||||
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 TestPartSetHeaderMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedPartSetHeader(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 := &PartSetHeader{}
|
||||
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 TestValidatorProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
@@ -1814,15 +1927,15 @@ func TestValidatorMarshalTo(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigningValidatorProto(t *testing.T) {
|
||||
func TestValidatorUpdateProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedSigningValidator(popr, false)
|
||||
p := NewPopulatedValidatorUpdate(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &SigningValidator{}
|
||||
msg := &ValidatorUpdate{}
|
||||
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -1845,10 +1958,10 @@ func TestSigningValidatorProto(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigningValidatorMarshalTo(t *testing.T) {
|
||||
func TestValidatorUpdateMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedSigningValidator(popr, false)
|
||||
p := NewPopulatedValidatorUpdate(popr, false)
|
||||
size := p.Size()
|
||||
dAtA := make([]byte, size)
|
||||
for i := range dAtA {
|
||||
@@ -1858,7 +1971,63 @@ func TestSigningValidatorMarshalTo(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &SigningValidator{}
|
||||
msg := &ValidatorUpdate{}
|
||||
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 TestVoteInfoProto(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVoteInfo(popr, false)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
msg := &VoteInfo{}
|
||||
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 TestVoteInfoMarshalTo(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVoteInfo(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 := &VoteInfo{}
|
||||
if err := github_com_gogo_protobuf_proto.Unmarshal(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -2468,34 +2637,16 @@ func TestBlockSizeJSON(t *testing.T) {
|
||||
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
|
||||
}
|
||||
}
|
||||
func TestTxSizeJSON(t *testing.T) {
|
||||
func TestEvidenceParamsJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedTxSize(popr, true)
|
||||
p := NewPopulatedEvidenceParams(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 := &TxSize{}
|
||||
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 TestBlockGossipJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockGossip(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 := &BlockGossip{}
|
||||
msg := &EvidenceParams{}
|
||||
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
@@ -2540,6 +2691,60 @@ func TestHeaderJSON(t *testing.T) {
|
||||
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
|
||||
}
|
||||
}
|
||||
func TestVersionJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVersion(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 := &Version{}
|
||||
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 TestBlockIDJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockID(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 := &BlockID{}
|
||||
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 TestPartSetHeaderJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedPartSetHeader(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 := &PartSetHeader{}
|
||||
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 TestValidatorJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
@@ -2558,16 +2763,34 @@ func TestValidatorJSON(t *testing.T) {
|
||||
t.Fatalf("seed = %d, %#v !Json Equal %#v", seed, msg, p)
|
||||
}
|
||||
}
|
||||
func TestSigningValidatorJSON(t *testing.T) {
|
||||
func TestValidatorUpdateJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedSigningValidator(popr, true)
|
||||
p := NewPopulatedValidatorUpdate(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 := &SigningValidator{}
|
||||
msg := &ValidatorUpdate{}
|
||||
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 TestVoteInfoJSON(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVoteInfo(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 := &VoteInfo{}
|
||||
err = github_com_gogo_protobuf_jsonpb.UnmarshalString(jsondata, msg)
|
||||
if err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
@@ -3368,12 +3591,12 @@ func TestBlockSizeProtoCompactText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxSizeProtoText(t *testing.T) {
|
||||
func TestEvidenceParamsProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedTxSize(popr, true)
|
||||
p := NewPopulatedEvidenceParams(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &TxSize{}
|
||||
msg := &EvidenceParams{}
|
||||
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -3382,40 +3605,12 @@ func TestTxSizeProtoText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxSizeProtoCompactText(t *testing.T) {
|
||||
func TestEvidenceParamsProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedTxSize(popr, true)
|
||||
p := NewPopulatedEvidenceParams(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &TxSize{}
|
||||
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 TestBlockGossipProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockGossip(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &BlockGossip{}
|
||||
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 TestBlockGossipProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockGossip(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &BlockGossip{}
|
||||
msg := &EvidenceParams{}
|
||||
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -3480,6 +3675,90 @@ func TestHeaderProtoCompactText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVersion(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &Version{}
|
||||
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 TestVersionProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVersion(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &Version{}
|
||||
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 TestBlockIDProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockID(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &BlockID{}
|
||||
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 TestBlockIDProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockID(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &BlockID{}
|
||||
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 TestPartSetHeaderProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedPartSetHeader(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &PartSetHeader{}
|
||||
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 TestPartSetHeaderProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedPartSetHeader(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &PartSetHeader{}
|
||||
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 TestValidatorProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
@@ -3508,12 +3787,12 @@ func TestValidatorProtoCompactText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigningValidatorProtoText(t *testing.T) {
|
||||
func TestValidatorUpdateProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedSigningValidator(popr, true)
|
||||
p := NewPopulatedValidatorUpdate(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &SigningValidator{}
|
||||
msg := &ValidatorUpdate{}
|
||||
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -3522,12 +3801,40 @@ func TestSigningValidatorProtoText(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigningValidatorProtoCompactText(t *testing.T) {
|
||||
func TestValidatorUpdateProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedSigningValidator(popr, true)
|
||||
p := NewPopulatedValidatorUpdate(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &SigningValidator{}
|
||||
msg := &ValidatorUpdate{}
|
||||
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 TestVoteInfoProtoText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVoteInfo(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.MarshalTextString(p)
|
||||
msg := &VoteInfo{}
|
||||
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 TestVoteInfoProtoCompactText(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVoteInfo(popr, true)
|
||||
dAtA := github_com_gogo_protobuf_proto.CompactTextString(p)
|
||||
msg := &VoteInfo{}
|
||||
if err := github_com_gogo_protobuf_proto.UnmarshalText(dAtA, msg); err != nil {
|
||||
t.Fatalf("seed = %d, err = %v", seed, err)
|
||||
}
|
||||
@@ -4186,32 +4493,10 @@ func TestBlockSizeSize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTxSizeSize(t *testing.T) {
|
||||
func TestEvidenceParamsSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedTxSize(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 TestBlockGossipSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockGossip(popr, true)
|
||||
p := NewPopulatedEvidenceParams(popr, true)
|
||||
size2 := github_com_gogo_protobuf_proto.Size(p)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
@@ -4274,6 +4559,72 @@ func TestHeaderSize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVersionSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVersion(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 TestBlockIDSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedBlockID(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 TestPartSetHeaderSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedPartSetHeader(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 TestValidatorSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
@@ -4296,10 +4647,32 @@ func TestValidatorSize(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigningValidatorSize(t *testing.T) {
|
||||
func TestValidatorUpdateSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedSigningValidator(popr, true)
|
||||
p := NewPopulatedValidatorUpdate(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 TestVoteInfoSize(t *testing.T) {
|
||||
seed := time.Now().UnixNano()
|
||||
popr := math_rand.New(math_rand.NewSource(seed))
|
||||
p := NewPopulatedVoteInfo(popr, true)
|
||||
size2 := github_com_gogo_protobuf_proto.Size(p)
|
||||
dAtA, err := github_com_gogo_protobuf_proto.Marshal(p)
|
||||
if err != nil {
|
||||
|
||||
@@ -2,58 +2,33 @@ package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"sort"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Validators is a list of validators that implements the Sort interface
|
||||
type Validators []Validator
|
||||
// ValidatorUpdates is a list of validators that implements the Sort interface
|
||||
type ValidatorUpdates []ValidatorUpdate
|
||||
|
||||
var _ sort.Interface = (Validators)(nil)
|
||||
var _ sort.Interface = (ValidatorUpdates)(nil)
|
||||
|
||||
// All these methods for Validators:
|
||||
// All these methods for ValidatorUpdates:
|
||||
// Len, Less and Swap
|
||||
// are for Validators to implement sort.Interface
|
||||
// are for ValidatorUpdates to implement sort.Interface
|
||||
// which will be used by the sort package.
|
||||
// See Issue https://github.com/tendermint/abci/issues/212
|
||||
|
||||
func (v Validators) Len() int {
|
||||
func (v ValidatorUpdates) Len() int {
|
||||
return len(v)
|
||||
}
|
||||
|
||||
// XXX: doesn't distinguish same validator with different power
|
||||
func (v Validators) Less(i, j int) bool {
|
||||
func (v ValidatorUpdates) Less(i, j int) bool {
|
||||
return bytes.Compare(v[i].PubKey.Data, v[j].PubKey.Data) <= 0
|
||||
}
|
||||
|
||||
func (v Validators) Swap(i, j int) {
|
||||
func (v ValidatorUpdates) Swap(i, j int) {
|
||||
v1 := v[i]
|
||||
v[i] = v[j]
|
||||
v[j] = v1
|
||||
}
|
||||
|
||||
func ValidatorsString(vs Validators) string {
|
||||
s := make([]validatorPretty, len(vs))
|
||||
for i, v := range vs {
|
||||
s[i] = validatorPretty{
|
||||
Address: v.Address,
|
||||
PubKey: v.PubKey.Data,
|
||||
Power: v.Power,
|
||||
}
|
||||
}
|
||||
b, err := json.Marshal(s)
|
||||
if err != nil {
|
||||
panic(err.Error())
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
type validatorPretty struct {
|
||||
Address cmn.HexBytes `json:"address"`
|
||||
PubKey []byte `json:"pub_key"`
|
||||
Power int64 `json:"power"`
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package version
|
||||
|
||||
// NOTE: we should probably be versioning the ABCI and the abci-cli separately
|
||||
import (
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
const Maj = "0"
|
||||
const Min = "12"
|
||||
const Fix = "0"
|
||||
// TODO: eliminate this after some version refactor
|
||||
|
||||
const Version = "0.12.0"
|
||||
const Version = version.ABCIVersion
|
||||
|
||||
@@ -12,20 +12,28 @@ import (
|
||||
ctypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
)
|
||||
|
||||
func testNodeInfo(id p2p.ID) p2p.DefaultNodeInfo {
|
||||
return p2p.DefaultNodeInfo{
|
||||
ProtocolVersion: p2p.ProtocolVersion{1, 2, 3},
|
||||
ID_: id,
|
||||
Moniker: "SOMENAME",
|
||||
Network: "SOMENAME",
|
||||
ListenAddr: "SOMEADDR",
|
||||
Version: "SOMEVER",
|
||||
Other: p2p.DefaultNodeInfoOther{
|
||||
TxIndex: "on",
|
||||
RPCAddress: "0.0.0.0:26657",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEncodeStatusWire(b *testing.B) {
|
||||
b.StopTimer()
|
||||
cdc := amino.NewCodec()
|
||||
ctypes.RegisterAmino(cdc)
|
||||
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
|
||||
status := &ctypes.ResultStatus{
|
||||
NodeInfo: p2p.NodeInfo{
|
||||
ID: nodeKey.ID(),
|
||||
Moniker: "SOMENAME",
|
||||
Network: "SOMENAME",
|
||||
ListenAddr: "SOMEADDR",
|
||||
Version: "SOMEVER",
|
||||
Other: []string{"SOMESTRING", "OTHERSTRING"},
|
||||
},
|
||||
NodeInfo: testNodeInfo(nodeKey.ID()),
|
||||
SyncInfo: ctypes.SyncInfo{
|
||||
LatestBlockHash: []byte("SOMEBYTES"),
|
||||
LatestBlockHeight: 123,
|
||||
@@ -53,14 +61,7 @@ func BenchmarkEncodeNodeInfoWire(b *testing.B) {
|
||||
cdc := amino.NewCodec()
|
||||
ctypes.RegisterAmino(cdc)
|
||||
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
|
||||
nodeInfo := p2p.NodeInfo{
|
||||
ID: nodeKey.ID(),
|
||||
Moniker: "SOMENAME",
|
||||
Network: "SOMENAME",
|
||||
ListenAddr: "SOMEADDR",
|
||||
Version: "SOMEVER",
|
||||
Other: []string{"SOMESTRING", "OTHERSTRING"},
|
||||
}
|
||||
nodeInfo := testNodeInfo(nodeKey.ID())
|
||||
b.StartTimer()
|
||||
|
||||
counter := 0
|
||||
@@ -78,14 +79,7 @@ func BenchmarkEncodeNodeInfoBinary(b *testing.B) {
|
||||
cdc := amino.NewCodec()
|
||||
ctypes.RegisterAmino(cdc)
|
||||
nodeKey := p2p.NodeKey{PrivKey: ed25519.GenPrivKey()}
|
||||
nodeInfo := p2p.NodeInfo{
|
||||
ID: nodeKey.ID(),
|
||||
Moniker: "SOMENAME",
|
||||
Network: "SOMENAME",
|
||||
ListenAddr: "SOMEADDR",
|
||||
Version: "SOMEVER",
|
||||
Other: []string{"SOMESTRING", "OTHERSTRING"},
|
||||
}
|
||||
nodeInfo := testNodeInfo(nodeKey.ID())
|
||||
b.StartTimer()
|
||||
|
||||
counter := 0
|
||||
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/lib/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -365,10 +365,10 @@ func (pool *BlockPool) debug() string {
|
||||
nextHeight := pool.height + pool.requestersLen()
|
||||
for h := pool.height; h < nextHeight; h++ {
|
||||
if pool.requesters[h] == nil {
|
||||
str += cmn.Fmt("H(%v):X ", h)
|
||||
str += fmt.Sprintf("H(%v):X ", h)
|
||||
} else {
|
||||
str += cmn.Fmt("H(%v):", h)
|
||||
str += cmn.Fmt("B?(%v) ", pool.requesters[h].block != nil)
|
||||
str += fmt.Sprintf("H(%v):", h)
|
||||
str += fmt.Sprintf("B?(%v) ", pool.requesters[h].block != nil)
|
||||
}
|
||||
}
|
||||
return str
|
||||
|
||||
@@ -201,7 +201,7 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
// Got a peer status. Unverified.
|
||||
bcR.pool.SetPeerHeight(src.ID(), msg.Height)
|
||||
default:
|
||||
bcR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
bcR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -290,7 +290,7 @@ FOR_LOOP:
|
||||
didProcessCh <- struct{}{}
|
||||
}
|
||||
|
||||
firstParts := first.MakePartSet(state.ConsensusParams.BlockPartSizeBytes)
|
||||
firstParts := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
firstPartsHeader := firstParts.Header()
|
||||
firstID := types.BlockID{first.Hash(), firstPartsHeader}
|
||||
// Finally, verify the first block using the second's commit
|
||||
@@ -321,7 +321,7 @@ FOR_LOOP:
|
||||
state, err = bcR.blockExec.ApplyBlock(state, firstID, first)
|
||||
if err != nil {
|
||||
// TODO This is bad, are we zombie?
|
||||
cmn.PanicQ(cmn.Fmt("Failed to process committed block (%d:%X): %v",
|
||||
cmn.PanicQ(fmt.Sprintf("Failed to process committed block (%d:%X): %v",
|
||||
first.Height, first.Hash(), err))
|
||||
}
|
||||
blocksSynced++
|
||||
@@ -356,11 +356,11 @@ type BlockchainMessage interface{}
|
||||
|
||||
func RegisterBlockchainMessages(cdc *amino.Codec) {
|
||||
cdc.RegisterInterface((*BlockchainMessage)(nil), nil)
|
||||
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/mempool/BlockRequest", nil)
|
||||
cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/mempool/BlockResponse", nil)
|
||||
cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/mempool/NoBlockResponse", nil)
|
||||
cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/mempool/StatusResponse", nil)
|
||||
cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/mempool/StatusRequest", nil)
|
||||
cdc.RegisterConcrete(&bcBlockRequestMessage{}, "tendermint/blockchain/BlockRequest", nil)
|
||||
cdc.RegisterConcrete(&bcBlockResponseMessage{}, "tendermint/blockchain/BlockResponse", nil)
|
||||
cdc.RegisterConcrete(&bcNoBlockResponseMessage{}, "tendermint/blockchain/NoBlockResponse", nil)
|
||||
cdc.RegisterConcrete(&bcStatusResponseMessage{}, "tendermint/blockchain/StatusResponse", nil)
|
||||
cdc.RegisterConcrete(&bcStatusRequestMessage{}, "tendermint/blockchain/StatusRequest", nil)
|
||||
}
|
||||
|
||||
func decodeMsg(bz []byte) (msg BlockchainMessage, err error) {
|
||||
@@ -378,7 +378,7 @@ type bcBlockRequestMessage struct {
|
||||
}
|
||||
|
||||
func (m *bcBlockRequestMessage) String() string {
|
||||
return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
|
||||
return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
|
||||
}
|
||||
|
||||
type bcNoBlockResponseMessage struct {
|
||||
@@ -386,7 +386,7 @@ type bcNoBlockResponseMessage struct {
|
||||
}
|
||||
|
||||
func (brm *bcNoBlockResponseMessage) String() string {
|
||||
return cmn.Fmt("[bcNoBlockResponseMessage %d]", brm.Height)
|
||||
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", brm.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -396,7 +396,7 @@ type bcBlockResponseMessage struct {
|
||||
}
|
||||
|
||||
func (m *bcBlockResponseMessage) String() string {
|
||||
return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
|
||||
return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -406,7 +406,7 @@ type bcStatusRequestMessage struct {
|
||||
}
|
||||
|
||||
func (m *bcStatusRequestMessage) String() string {
|
||||
return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
|
||||
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -416,5 +416,5 @@ type bcStatusResponseMessage struct {
|
||||
}
|
||||
|
||||
func (m *bcStatusResponseMessage) String() string {
|
||||
return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
|
||||
return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
|
||||
}
|
||||
|
||||
@@ -42,13 +42,13 @@ func newBlockchainReactor(logger log.Logger, maxBlockHeight int64) *BlockchainRe
|
||||
bcReactor.SetLogger(logger.With("module", "blockchain"))
|
||||
|
||||
// Next: we need to set a switch in order for peers to be added in
|
||||
bcReactor.Switch = p2p.NewSwitch(cfg.DefaultP2PConfig())
|
||||
bcReactor.Switch = p2p.NewSwitch(cfg.DefaultP2PConfig(), nil)
|
||||
|
||||
// Lastly: let's add some blocks in
|
||||
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
|
||||
firstBlock := makeBlock(blockHeight, state)
|
||||
secondBlock := makeBlock(blockHeight+1, state)
|
||||
firstParts := firstBlock.MakePartSet(state.ConsensusParams.BlockGossip.BlockPartSizeBytes)
|
||||
firstParts := firstBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit)
|
||||
}
|
||||
|
||||
@@ -156,7 +156,7 @@ func makeTxs(height int64) (txs []types.Tx) {
|
||||
}
|
||||
|
||||
func makeBlock(height int64, state sm.State) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil)
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil, state.Validators.GetProposer().Address)
|
||||
return block
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
|
||||
}
|
||||
|
||||
func (tp *bcrTestPeer) Send(chID byte, msgBytes []byte) bool { return tp.TrySend(chID, msgBytes) }
|
||||
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.NodeInfo{} }
|
||||
func (tp *bcrTestPeer) NodeInfo() p2p.NodeInfo { return p2p.DefaultNodeInfo{} }
|
||||
func (tp *bcrTestPeer) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
|
||||
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }
|
||||
func (tp *bcrTestPeer) IsOutbound() bool { return false }
|
||||
|
||||
@@ -63,7 +63,7 @@ func (bs *BlockStore) LoadBlock(height int64) *types.Block {
|
||||
part := bs.LoadBlockPart(height, i)
|
||||
buf = append(buf, part.Bytes...)
|
||||
}
|
||||
err := cdc.UnmarshalBinary(buf, block)
|
||||
err := cdc.UnmarshalBinaryLengthPrefixed(buf, block)
|
||||
if err != nil {
|
||||
// NOTE: The existence of meta should imply the existence of the
|
||||
// block. So, make sure meta is only saved after blocks are saved.
|
||||
@@ -148,10 +148,10 @@ func (bs *BlockStore) SaveBlock(block *types.Block, blockParts *types.PartSet, s
|
||||
}
|
||||
height := block.Height
|
||||
if g, w := height, bs.Height()+1; g != w {
|
||||
cmn.PanicSanity(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
|
||||
cmn.PanicSanity(fmt.Sprintf("BlockStore can only save contiguous blocks. Wanted %v, got %v", w, g))
|
||||
}
|
||||
if !blockParts.IsComplete() {
|
||||
cmn.PanicSanity(cmn.Fmt("BlockStore can only save complete block part sets"))
|
||||
cmn.PanicSanity(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(cmn.Fmt("BlockStore can only save contiguous blocks. Wanted %v, got %v", bs.Height()+1, height))
|
||||
cmn.PanicSanity(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(cmn.Fmt("Could not marshal state bytes: %v", err))
|
||||
cmn.PanicSanity(fmt.Sprintf("Could not marshal state bytes: %v", err))
|
||||
}
|
||||
db.SetSync(blockStoreKey, bytes)
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -14,6 +13,7 @@ import (
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
func TestLoadBlockStoreStateJSON(t *testing.T) {
|
||||
@@ -49,7 +49,7 @@ func TestNewBlockStore(t *testing.T) {
|
||||
return nil, nil
|
||||
})
|
||||
require.NotNil(t, panicErr, "#%d panicCauser: %q expected a panic", i, tt.data)
|
||||
assert.Contains(t, panicErr.Error(), tt.wantErr, "#%d data: %q", i, tt.data)
|
||||
assert.Contains(t, fmt.Sprintf("%#v", panicErr), tt.wantErr, "#%d data: %q", i, tt.data)
|
||||
}
|
||||
|
||||
db.Set(blockStoreKey, nil)
|
||||
@@ -70,7 +70,7 @@ var (
|
||||
part1 = partSet.GetPart(0)
|
||||
part2 = partSet.GetPart(1)
|
||||
seenCommit1 = &types.Commit{Precommits: []*types.Vote{{Height: 10,
|
||||
Timestamp: time.Now().UTC()}}}
|
||||
Timestamp: tmtime.Now()}}}
|
||||
)
|
||||
|
||||
// TODO: This test should be simplified ...
|
||||
@@ -91,7 +91,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
block := makeBlock(bs.Height()+1, state)
|
||||
validPartSet := block.MakePartSet(2)
|
||||
seenCommit := &types.Commit{Precommits: []*types.Vote{{Height: 10,
|
||||
Timestamp: time.Now().UTC()}}}
|
||||
Timestamp: tmtime.Now()}}}
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
|
||||
|
||||
@@ -103,7 +103,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
Height: 1,
|
||||
NumTxs: 100,
|
||||
ChainID: "block_test",
|
||||
Time: time.Now(),
|
||||
Time: tmtime.Now(),
|
||||
}
|
||||
header2 := header1
|
||||
header2.Height = 4
|
||||
@@ -111,7 +111,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
// End of setup, test data
|
||||
|
||||
commitAtH10 := &types.Commit{Precommits: []*types.Vote{{Height: 10,
|
||||
Timestamp: time.Now().UTC()}}}
|
||||
Timestamp: tmtime.Now()}}}
|
||||
tuples := []struct {
|
||||
block *types.Block
|
||||
parts *types.PartSet
|
||||
@@ -238,7 +238,7 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
if subStr := tuple.wantPanic; subStr != "" {
|
||||
if panicErr == nil {
|
||||
t.Errorf("#%d: want a non-nil panic", i)
|
||||
} else if got := panicErr.Error(); !strings.Contains(got, subStr) {
|
||||
} else if got := fmt.Sprintf("%#v", panicErr); !strings.Contains(got, subStr) {
|
||||
t.Errorf("#%d:\n\tgotErr: %q\nwant substring: %q", i, got, subStr)
|
||||
}
|
||||
continue
|
||||
@@ -335,7 +335,7 @@ func TestBlockFetchAtHeight(t *testing.T) {
|
||||
|
||||
partSet := block.MakePartSet(2)
|
||||
seenCommit := &types.Commit{Precommits: []*types.Vote{{Height: 10,
|
||||
Timestamp: time.Now().UTC()}}}
|
||||
Timestamp: tmtime.Now()}}}
|
||||
|
||||
bs.SaveBlock(block, partSet, seenCommit)
|
||||
require.Equal(t, bs.Height(), block.Header.Height, "expecting the new height to be changed")
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
)
|
||||
|
||||
// GenNodeKeyCmd allows the generation of a node key. It prints node's ID to
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"time"
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
// InitFilesCmd initialises a fresh Tendermint Core instance.
|
||||
@@ -52,13 +53,14 @@ func initFilesWithConfig(config *cfg.Config) error {
|
||||
logger.Info("Found genesis file", "path", genFile)
|
||||
} else {
|
||||
genDoc := types.GenesisDoc{
|
||||
ChainID: cmn.Fmt("test-chain-%v", cmn.RandStr(6)),
|
||||
GenesisTime: time.Now(),
|
||||
ChainID: fmt.Sprintf("test-chain-%v", cmn.RandStr(6)),
|
||||
GenesisTime: tmtime.Now(),
|
||||
ConsensusParams: types.DefaultConsensusParams(),
|
||||
}
|
||||
genDoc.Validators = []types.GenesisValidator{{
|
||||
PubKey: pv.GetPubKey(),
|
||||
Power: 10,
|
||||
Address: pv.GetPubKey().Address(),
|
||||
PubKey: pv.GetPubKey(),
|
||||
Power: 10,
|
||||
}}
|
||||
|
||||
if err := genDoc.SaveAs(genFile); err != nil {
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
"github.com/tendermint/tendermint/lite/proxy"
|
||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
||||
)
|
||||
@@ -27,10 +26,12 @@ just with added trust and running locally.`,
|
||||
}
|
||||
|
||||
var (
|
||||
listenAddr string
|
||||
nodeAddr string
|
||||
chainID string
|
||||
home string
|
||||
listenAddr string
|
||||
nodeAddr string
|
||||
chainID string
|
||||
home string
|
||||
maxOpenConnections int
|
||||
cacheSize int
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -38,6 +39,8 @@ func init() {
|
||||
LiteCmd.Flags().StringVar(&nodeAddr, "node", "tcp://localhost:26657", "Connect to a Tendermint node at this address")
|
||||
LiteCmd.Flags().StringVar(&chainID, "chain-id", "tendermint", "Specify the Tendermint chain ID")
|
||||
LiteCmd.Flags().StringVar(&home, "home-dir", ".tendermint-lite", "Specify the home directory")
|
||||
LiteCmd.Flags().IntVar(&maxOpenConnections, "max-open-connections", 900, "Maximum number of simultaneous connections (including WebSocket).")
|
||||
LiteCmd.Flags().IntVar(&cacheSize, "cache-size", 10, "Specify the memory trust store cache size")
|
||||
}
|
||||
|
||||
func ensureAddrHasSchemeOrDefaultToTCP(addr string) (string, error) {
|
||||
@@ -66,17 +69,21 @@ func runProxy(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// First, connect a client
|
||||
logger.Info("Connecting to source HTTP client...")
|
||||
node := rpcclient.NewHTTP(nodeAddr, "/websocket")
|
||||
|
||||
cert, err := proxy.GetCertifier(chainID, home, nodeAddr)
|
||||
logger.Info("Constructing Verifier...")
|
||||
cert, err := proxy.NewVerifier(chainID, home, node, logger, cacheSize)
|
||||
if err != nil {
|
||||
return err
|
||||
return cmn.ErrorWrap(err, "constructing Verifier")
|
||||
}
|
||||
cert.SetLogger(logger)
|
||||
sc := proxy.SecureClient(node, cert)
|
||||
|
||||
err = proxy.StartProxy(sc, listenAddr, logger)
|
||||
logger.Info("Starting proxy...")
|
||||
err = proxy.StartProxy(sc, listenAddr, logger, maxOpenConnections)
|
||||
if err != nil {
|
||||
return err
|
||||
return cmn.ErrorWrap(err, "starting proxy")
|
||||
}
|
||||
|
||||
cmn.TrapSignal(func() {
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
|
||||
// ResetAllCmd removes the database of this Tendermint core
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -35,6 +36,9 @@ func ParseConfig() (*cfg.Config, error) {
|
||||
}
|
||||
conf.SetRoot(conf.RootDir)
|
||||
cfg.EnsureRoot(conf.RootDir)
|
||||
if err = conf.ValidateBasic(); err != nil {
|
||||
return nil, fmt.Errorf("Error in config file: %v", err)
|
||||
}
|
||||
return conf, err
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -49,19 +52,31 @@ func NewRunNodeCmd(nodeProvider nm.NodeProvider) *cobra.Command {
|
||||
Use: "node",
|
||||
Short: "Run the tendermint node",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
// Create & start node
|
||||
n, err := nodeProvider(config, logger)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
}()
|
||||
|
||||
if err := n.Start(); err != nil {
|
||||
return fmt.Errorf("Failed to start node: %v", err)
|
||||
}
|
||||
logger.Info("Started node", "nodeInfo", n.Switch().NodeInfo())
|
||||
|
||||
// Trap signal, run forever.
|
||||
n.RunForever()
|
||||
// Run forever
|
||||
select {}
|
||||
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -6,15 +6,15 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -76,7 +76,7 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
genVals := make([]types.GenesisValidator, nValidators)
|
||||
|
||||
for i := 0; i < nValidators; i++ {
|
||||
nodeDirName := cmn.Fmt("%s%d", nodeDirPrefix, i)
|
||||
nodeDirName := fmt.Sprintf("%s%d", nodeDirPrefix, i)
|
||||
nodeDir := filepath.Join(outputDir, nodeDirName)
|
||||
config.SetRoot(nodeDir)
|
||||
|
||||
@@ -91,14 +91,15 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
||||
pv := privval.LoadFilePV(pvFile)
|
||||
genVals[i] = types.GenesisValidator{
|
||||
PubKey: pv.GetPubKey(),
|
||||
Power: 1,
|
||||
Name: nodeDirName,
|
||||
Address: pv.GetPubKey().Address(),
|
||||
PubKey: pv.GetPubKey(),
|
||||
Power: 1,
|
||||
Name: nodeDirName,
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i+nValidators))
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i+nValidators))
|
||||
config.SetRoot(nodeDir)
|
||||
|
||||
err := os.MkdirAll(filepath.Join(nodeDir, "config"), nodeDirPerm)
|
||||
@@ -112,14 +113,14 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Generate genesis doc from generated validators
|
||||
genDoc := &types.GenesisDoc{
|
||||
GenesisTime: time.Now(),
|
||||
GenesisTime: tmtime.Now(),
|
||||
ChainID: "chain-" + cmn.RandStr(6),
|
||||
Validators: genVals,
|
||||
}
|
||||
|
||||
// Write genesis file.
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
if err := genDoc.SaveAs(filepath.Join(nodeDir, config.BaseConfig.Genesis)); err != nil {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
@@ -159,7 +160,7 @@ func hostnameOrIP(i int) string {
|
||||
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
|
||||
persistentPeers := make([]string, nValidators+nNonValidators)
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
nodeKey, err := p2p.LoadNodeKey(config.NodeKeyFile())
|
||||
if err != nil {
|
||||
@@ -170,7 +171,7 @@ func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
|
||||
persistentPeersList := strings.Join(persistentPeers, ",")
|
||||
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
config.P2P.PersistentPeers = persistentPeersList
|
||||
config.P2P.AddrBookStrict = false
|
||||
|
||||
359
config/config.go
359
config/config.go
@@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -19,7 +21,7 @@ const (
|
||||
// generate the config.toml. Please reflect any changes
|
||||
// made here in the defaultConfigTemplate constant in
|
||||
// config/toml.go
|
||||
// NOTE: tmlibs/cli must know to look in the config dir!
|
||||
// NOTE: libs/cli must know to look in the config dir!
|
||||
var (
|
||||
DefaultTendermintDir = ".tendermint"
|
||||
defaultConfigDir = "config"
|
||||
@@ -89,12 +91,32 @@ func (cfg *Config) SetRoot(root string) *Config {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *Config) ValidateBasic() error {
|
||||
if err := cfg.RPC.ValidateBasic(); err != nil {
|
||||
return errors.Wrap(err, "Error in [rpc] section")
|
||||
}
|
||||
if err := cfg.P2P.ValidateBasic(); err != nil {
|
||||
return errors.Wrap(err, "Error in [p2p] section")
|
||||
}
|
||||
if err := cfg.Mempool.ValidateBasic(); err != nil {
|
||||
return errors.Wrap(err, "Error in [mempool] section")
|
||||
}
|
||||
if err := cfg.Consensus.ValidateBasic(); err != nil {
|
||||
return errors.Wrap(err, "Error in [consensus] section")
|
||||
}
|
||||
return errors.Wrap(
|
||||
cfg.Instrumentation.ValidateBasic(),
|
||||
"Error in [instrumentation] section",
|
||||
)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// BaseConfig
|
||||
|
||||
// BaseConfig defines the base configuration for a Tendermint node
|
||||
type BaseConfig struct {
|
||||
|
||||
// chainID is unexposed and immutable but here for convenience
|
||||
chainID string
|
||||
|
||||
@@ -102,49 +124,49 @@ type BaseConfig struct {
|
||||
// This should be set in viper so it can unmarshal into this struct
|
||||
RootDir string `mapstructure:"home"`
|
||||
|
||||
// Path to the JSON file containing the initial validator set and other meta data
|
||||
Genesis string `mapstructure:"genesis_file"`
|
||||
|
||||
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||
PrivValidator string `mapstructure:"priv_validator_file"`
|
||||
|
||||
// A JSON file containing the private key to use for p2p authenticated encryption
|
||||
NodeKey string `mapstructure:"node_key_file"`
|
||||
|
||||
// A custom human readable name for this node
|
||||
Moniker string `mapstructure:"moniker"`
|
||||
|
||||
// TCP or UNIX socket address for Tendermint to listen on for
|
||||
// connections from an external PrivValidator process
|
||||
PrivValidatorListenAddr string `mapstructure:"priv_validator_laddr"`
|
||||
|
||||
// TCP or UNIX socket address of the ABCI application,
|
||||
// or the name of an ABCI application compiled in with the Tendermint binary
|
||||
ProxyApp string `mapstructure:"proxy_app"`
|
||||
|
||||
// Mechanism to connect to the ABCI application: socket | grpc
|
||||
ABCI string `mapstructure:"abci"`
|
||||
|
||||
// Output level for logging
|
||||
LogLevel string `mapstructure:"log_level"`
|
||||
|
||||
// TCP or UNIX socket address for the profiling server to listen on
|
||||
ProfListenAddress string `mapstructure:"prof_laddr"`
|
||||
// A custom human readable name for this node
|
||||
Moniker string `mapstructure:"moniker"`
|
||||
|
||||
// If this node is many blocks behind the tip of the chain, FastSync
|
||||
// allows them to catchup quickly by downloading blocks in parallel
|
||||
// and verifying their commits
|
||||
FastSync bool `mapstructure:"fast_sync"`
|
||||
|
||||
// If true, query the ABCI app on connecting to a new peer
|
||||
// so the app can decide if we should keep the connection or not
|
||||
FilterPeers bool `mapstructure:"filter_peers"` // false
|
||||
|
||||
// Database backend: leveldb | memdb
|
||||
// Database backend: leveldb | memdb | cleveldb
|
||||
DBBackend string `mapstructure:"db_backend"`
|
||||
|
||||
// Database directory
|
||||
DBPath string `mapstructure:"db_dir"`
|
||||
|
||||
// Output level for logging
|
||||
LogLevel string `mapstructure:"log_level"`
|
||||
|
||||
// Path to the JSON file containing the initial validator set and other meta data
|
||||
Genesis string `mapstructure:"genesis_file"`
|
||||
|
||||
// Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||
PrivValidator string `mapstructure:"priv_validator_file"`
|
||||
|
||||
// TCP or UNIX socket address for Tendermint to listen on for
|
||||
// connections from an external PrivValidator process
|
||||
PrivValidatorListenAddr string `mapstructure:"priv_validator_laddr"`
|
||||
|
||||
// A JSON file containing the private key to use for p2p authenticated encryption
|
||||
NodeKey string `mapstructure:"node_key_file"`
|
||||
|
||||
// Mechanism to connect to the ABCI application: socket | grpc
|
||||
ABCI string `mapstructure:"abci"`
|
||||
|
||||
// TCP or UNIX socket address for the profiling server to listen on
|
||||
ProfListenAddress string `mapstructure:"prof_laddr"`
|
||||
|
||||
// If true, query the ABCI app on connecting to a new peer
|
||||
// so the app can decide if we should keep the connection or not
|
||||
FilterPeers bool `mapstructure:"filter_peers"` // false
|
||||
}
|
||||
|
||||
// DefaultBaseConfig returns a default base configuration for a Tendermint node
|
||||
@@ -239,6 +261,8 @@ type RPCConfig struct {
|
||||
// If you want to accept more significant number than the default, make sure
|
||||
// you increase your OS limits.
|
||||
// 0 - unlimited.
|
||||
// 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"`
|
||||
}
|
||||
|
||||
@@ -248,11 +272,9 @@ func DefaultRPCConfig() *RPCConfig {
|
||||
ListenAddress: "tcp://0.0.0.0:26657",
|
||||
|
||||
GRPCListenAddress: "",
|
||||
GRPCMaxOpenConnections: 900, // no ipv4
|
||||
GRPCMaxOpenConnections: 900,
|
||||
|
||||
Unsafe: false,
|
||||
// should be < {ulimit -Sn} - {MaxNumPeers} - {N of wal, db and other open files}
|
||||
// 1024 - 50 - 50 = 924 = ~900
|
||||
Unsafe: false,
|
||||
MaxOpenConnections: 900,
|
||||
}
|
||||
}
|
||||
@@ -266,6 +288,18 @@ func TestRPCConfig() *RPCConfig {
|
||||
return cfg
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *RPCConfig) ValidateBasic() error {
|
||||
if cfg.GRPCMaxOpenConnections < 0 {
|
||||
return errors.New("grpc_max_open_connections can't be negative")
|
||||
}
|
||||
if cfg.MaxOpenConnections < 0 {
|
||||
return errors.New("max_open_connections can't be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// P2PConfig
|
||||
|
||||
@@ -293,13 +327,17 @@ type P2PConfig struct {
|
||||
AddrBook string `mapstructure:"addr_book_file"`
|
||||
|
||||
// Set true for strict address routability rules
|
||||
// Set false for private or local networks
|
||||
AddrBookStrict bool `mapstructure:"addr_book_strict"`
|
||||
|
||||
// Maximum number of peers to connect to
|
||||
MaxNumPeers int `mapstructure:"max_num_peers"`
|
||||
// Maximum number of inbound peers
|
||||
MaxNumInboundPeers int `mapstructure:"max_num_inbound_peers"`
|
||||
|
||||
// Time to wait before flushing messages out on the connection, in ms
|
||||
FlushThrottleTimeout int `mapstructure:"flush_throttle_timeout"`
|
||||
// Maximum number of outbound peers to connect to, excluding persistent peers
|
||||
MaxNumOutboundPeers int `mapstructure:"max_num_outbound_peers"`
|
||||
|
||||
// Time to wait before flushing messages out on the connection
|
||||
FlushThrottleTimeout time.Duration `mapstructure:"flush_throttle_timeout"`
|
||||
|
||||
// Maximum size of a message packet payload, in bytes
|
||||
MaxPacketMsgPayloadSize int `mapstructure:"max_packet_msg_payload_size"`
|
||||
@@ -346,8 +384,9 @@ func DefaultP2PConfig() *P2PConfig {
|
||||
UPNP: false,
|
||||
AddrBook: defaultAddrBookPath,
|
||||
AddrBookStrict: true,
|
||||
MaxNumPeers: 50,
|
||||
FlushThrottleTimeout: 100,
|
||||
MaxNumInboundPeers: 40,
|
||||
MaxNumOutboundPeers: 10,
|
||||
FlushThrottleTimeout: 100 * time.Millisecond,
|
||||
MaxPacketMsgPayloadSize: 1024, // 1 kB
|
||||
SendRate: 5120000, // 5 mB/s
|
||||
RecvRate: 5120000, // 5 mB/s
|
||||
@@ -366,7 +405,7 @@ func DefaultP2PConfig() *P2PConfig {
|
||||
func TestP2PConfig() *P2PConfig {
|
||||
cfg := DefaultP2PConfig()
|
||||
cfg.ListenAddress = "tcp://0.0.0.0:36656"
|
||||
cfg.FlushThrottleTimeout = 10
|
||||
cfg.FlushThrottleTimeout = 10 * time.Millisecond
|
||||
cfg.AllowDuplicateIP = true
|
||||
return cfg
|
||||
}
|
||||
@@ -376,6 +415,30 @@ func (cfg *P2PConfig) AddrBookFile() string {
|
||||
return rootify(cfg.AddrBook, cfg.RootDir)
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *P2PConfig) ValidateBasic() error {
|
||||
if cfg.MaxNumInboundPeers < 0 {
|
||||
return errors.New("max_num_inbound_peers can't be negative")
|
||||
}
|
||||
if cfg.MaxNumOutboundPeers < 0 {
|
||||
return errors.New("max_num_outbound_peers can't be negative")
|
||||
}
|
||||
if cfg.FlushThrottleTimeout < 0 {
|
||||
return errors.New("flush_throttle_timeout can't be negative")
|
||||
}
|
||||
if cfg.MaxPacketMsgPayloadSize < 0 {
|
||||
return errors.New("max_packet_msg_payload_size can't be negative")
|
||||
}
|
||||
if cfg.SendRate < 0 {
|
||||
return errors.New("send_rate can't be negative")
|
||||
}
|
||||
if cfg.RecvRate < 0 {
|
||||
return errors.New("recv_rate can't be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FuzzConnConfig is a FuzzedConnection configuration.
|
||||
type FuzzConnConfig struct {
|
||||
Mode int
|
||||
@@ -401,24 +464,24 @@ func DefaultFuzzConnConfig() *FuzzConnConfig {
|
||||
|
||||
// MempoolConfig defines the configuration options for the Tendermint mempool
|
||||
type MempoolConfig struct {
|
||||
RootDir string `mapstructure:"home"`
|
||||
Recheck bool `mapstructure:"recheck"`
|
||||
RecheckEmpty bool `mapstructure:"recheck_empty"`
|
||||
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"`
|
||||
CacheSize int `mapstructure:"cache_size"`
|
||||
}
|
||||
|
||||
// DefaultMempoolConfig returns a default configuration for the Tendermint mempool
|
||||
func DefaultMempoolConfig() *MempoolConfig {
|
||||
return &MempoolConfig{
|
||||
Recheck: true,
|
||||
RecheckEmpty: true,
|
||||
Broadcast: true,
|
||||
WalPath: filepath.Join(defaultDataDir, "mempool.wal"),
|
||||
Size: 100000,
|
||||
CacheSize: 100000,
|
||||
Recheck: true,
|
||||
Broadcast: true,
|
||||
WalPath: "",
|
||||
// Each signature verification takes .5ms, size reduced until we implement
|
||||
// ABCI Recheck
|
||||
Size: 5000,
|
||||
CacheSize: 10000,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -434,6 +497,18 @@ func (cfg *MempoolConfig) WalDir() string {
|
||||
return rootify(cfg.WalPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *MempoolConfig) ValidateBasic() error {
|
||||
if cfg.Size < 0 {
|
||||
return errors.New("size can't be negative")
|
||||
}
|
||||
if cfg.CacheSize < 0 {
|
||||
return errors.New("cache_size can't be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ConsensusConfig
|
||||
|
||||
@@ -444,100 +519,101 @@ type ConsensusConfig struct {
|
||||
WalPath string `mapstructure:"wal_file"`
|
||||
walFile string // overrides WalPath if set
|
||||
|
||||
// All timeouts are in milliseconds
|
||||
TimeoutPropose int `mapstructure:"timeout_propose"`
|
||||
TimeoutProposeDelta int `mapstructure:"timeout_propose_delta"`
|
||||
TimeoutPrevote int `mapstructure:"timeout_prevote"`
|
||||
TimeoutPrevoteDelta int `mapstructure:"timeout_prevote_delta"`
|
||||
TimeoutPrecommit int `mapstructure:"timeout_precommit"`
|
||||
TimeoutPrecommitDelta int `mapstructure:"timeout_precommit_delta"`
|
||||
TimeoutCommit int `mapstructure:"timeout_commit"`
|
||||
TimeoutPropose time.Duration `mapstructure:"timeout_propose"`
|
||||
TimeoutProposeDelta time.Duration `mapstructure:"timeout_propose_delta"`
|
||||
TimeoutPrevote time.Duration `mapstructure:"timeout_prevote"`
|
||||
TimeoutPrevoteDelta time.Duration `mapstructure:"timeout_prevote_delta"`
|
||||
TimeoutPrecommit time.Duration `mapstructure:"timeout_precommit"`
|
||||
TimeoutPrecommitDelta time.Duration `mapstructure:"timeout_precommit_delta"`
|
||||
TimeoutCommit time.Duration `mapstructure:"timeout_commit"`
|
||||
|
||||
// Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
|
||||
SkipTimeoutCommit bool `mapstructure:"skip_timeout_commit"`
|
||||
|
||||
// EmptyBlocks mode and possible interval between empty blocks in seconds
|
||||
CreateEmptyBlocks bool `mapstructure:"create_empty_blocks"`
|
||||
CreateEmptyBlocksInterval int `mapstructure:"create_empty_blocks_interval"`
|
||||
// EmptyBlocks mode and possible interval between empty blocks
|
||||
CreateEmptyBlocks bool `mapstructure:"create_empty_blocks"`
|
||||
CreateEmptyBlocksInterval time.Duration `mapstructure:"create_empty_blocks_interval"`
|
||||
|
||||
// Reactor sleep duration parameters are in milliseconds
|
||||
PeerGossipSleepDuration int `mapstructure:"peer_gossip_sleep_duration"`
|
||||
PeerQueryMaj23SleepDuration int `mapstructure:"peer_query_maj23_sleep_duration"`
|
||||
// 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
|
||||
func DefaultConsensusConfig() *ConsensusConfig {
|
||||
return &ConsensusConfig{
|
||||
WalPath: filepath.Join(defaultDataDir, "cs.wal", "wal"),
|
||||
TimeoutPropose: 3000,
|
||||
TimeoutProposeDelta: 500,
|
||||
TimeoutPrevote: 1000,
|
||||
TimeoutPrevoteDelta: 500,
|
||||
TimeoutPrecommit: 1000,
|
||||
TimeoutPrecommitDelta: 500,
|
||||
TimeoutCommit: 1000,
|
||||
TimeoutPropose: 3000 * time.Millisecond,
|
||||
TimeoutProposeDelta: 500 * time.Millisecond,
|
||||
TimeoutPrevote: 1000 * time.Millisecond,
|
||||
TimeoutPrevoteDelta: 500 * time.Millisecond,
|
||||
TimeoutPrecommit: 1000 * time.Millisecond,
|
||||
TimeoutPrecommitDelta: 500 * time.Millisecond,
|
||||
TimeoutCommit: 1000 * time.Millisecond,
|
||||
SkipTimeoutCommit: false,
|
||||
CreateEmptyBlocks: true,
|
||||
CreateEmptyBlocksInterval: 0,
|
||||
PeerGossipSleepDuration: 100,
|
||||
PeerQueryMaj23SleepDuration: 2000,
|
||||
CreateEmptyBlocksInterval: 0 * time.Second,
|
||||
PeerGossipSleepDuration: 100 * time.Millisecond,
|
||||
PeerQueryMaj23SleepDuration: 2000 * time.Millisecond,
|
||||
BlockTimeIota: 1000 * time.Millisecond,
|
||||
}
|
||||
}
|
||||
|
||||
// TestConsensusConfig returns a configuration for testing the consensus service
|
||||
func TestConsensusConfig() *ConsensusConfig {
|
||||
cfg := DefaultConsensusConfig()
|
||||
cfg.TimeoutPropose = 100
|
||||
cfg.TimeoutProposeDelta = 1
|
||||
cfg.TimeoutPrevote = 10
|
||||
cfg.TimeoutPrevoteDelta = 1
|
||||
cfg.TimeoutPrecommit = 10
|
||||
cfg.TimeoutPrecommitDelta = 1
|
||||
cfg.TimeoutCommit = 10
|
||||
cfg.TimeoutPropose = 40 * time.Millisecond
|
||||
cfg.TimeoutProposeDelta = 1 * time.Millisecond
|
||||
cfg.TimeoutPrevote = 10 * time.Millisecond
|
||||
cfg.TimeoutPrevoteDelta = 1 * time.Millisecond
|
||||
cfg.TimeoutPrecommit = 10 * time.Millisecond
|
||||
cfg.TimeoutPrecommitDelta = 1 * time.Millisecond
|
||||
cfg.TimeoutCommit = 10 * time.Millisecond
|
||||
cfg.SkipTimeoutCommit = true
|
||||
cfg.PeerGossipSleepDuration = 5
|
||||
cfg.PeerQueryMaj23SleepDuration = 250
|
||||
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
|
||||
}
|
||||
|
||||
// EmptyBlocks returns the amount of time to wait before proposing an empty block or starting the propose timer if there are no txs available
|
||||
func (cfg *ConsensusConfig) EmptyBlocksInterval() time.Duration {
|
||||
return time.Duration(cfg.CreateEmptyBlocksInterval) * time.Second
|
||||
}
|
||||
|
||||
// Propose returns the amount of time to wait for a proposal
|
||||
func (cfg *ConsensusConfig) Propose(round int) time.Duration {
|
||||
return time.Duration(cfg.TimeoutPropose+cfg.TimeoutProposeDelta*round) * time.Millisecond
|
||||
return time.Duration(
|
||||
cfg.TimeoutPropose.Nanoseconds()+cfg.TimeoutProposeDelta.Nanoseconds()*int64(round),
|
||||
) * time.Nanosecond
|
||||
}
|
||||
|
||||
// Prevote returns the amount of time to wait for straggler votes after receiving any +2/3 prevotes
|
||||
func (cfg *ConsensusConfig) Prevote(round int) time.Duration {
|
||||
return time.Duration(cfg.TimeoutPrevote+cfg.TimeoutPrevoteDelta*round) * time.Millisecond
|
||||
return time.Duration(
|
||||
cfg.TimeoutPrevote.Nanoseconds()+cfg.TimeoutPrevoteDelta.Nanoseconds()*int64(round),
|
||||
) * time.Nanosecond
|
||||
}
|
||||
|
||||
// Precommit returns the amount of time to wait for straggler votes after receiving any +2/3 precommits
|
||||
func (cfg *ConsensusConfig) Precommit(round int) time.Duration {
|
||||
return time.Duration(cfg.TimeoutPrecommit+cfg.TimeoutPrecommitDelta*round) * time.Millisecond
|
||||
return time.Duration(
|
||||
cfg.TimeoutPrecommit.Nanoseconds()+cfg.TimeoutPrecommitDelta.Nanoseconds()*int64(round),
|
||||
) * time.Nanosecond
|
||||
}
|
||||
|
||||
// Commit returns the amount of time to wait for straggler votes after receiving +2/3 precommits for a single block (ie. a commit).
|
||||
func (cfg *ConsensusConfig) Commit(t time.Time) time.Time {
|
||||
return t.Add(time.Duration(cfg.TimeoutCommit) * time.Millisecond)
|
||||
}
|
||||
|
||||
// PeerGossipSleep returns the amount of time to sleep if there is nothing to send from the ConsensusReactor
|
||||
func (cfg *ConsensusConfig) PeerGossipSleep() time.Duration {
|
||||
return time.Duration(cfg.PeerGossipSleepDuration) * time.Millisecond
|
||||
}
|
||||
|
||||
// PeerQueryMaj23Sleep returns the amount of time to sleep after each VoteSetMaj23Message is sent in the ConsensusReactor
|
||||
func (cfg *ConsensusConfig) PeerQueryMaj23Sleep() time.Duration {
|
||||
return time.Duration(cfg.PeerQueryMaj23SleepDuration) * time.Millisecond
|
||||
return t.Add(cfg.TimeoutCommit)
|
||||
}
|
||||
|
||||
// WalFile returns the full path to the write-ahead log file
|
||||
@@ -553,11 +629,50 @@ func (cfg *ConsensusConfig) SetWalFile(walFile string) {
|
||||
cfg.walFile = walFile
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *ConsensusConfig) ValidateBasic() error {
|
||||
if cfg.TimeoutPropose < 0 {
|
||||
return errors.New("timeout_propose can't be negative")
|
||||
}
|
||||
if cfg.TimeoutProposeDelta < 0 {
|
||||
return errors.New("timeout_propose_delta can't be negative")
|
||||
}
|
||||
if cfg.TimeoutPrevote < 0 {
|
||||
return errors.New("timeout_prevote can't be negative")
|
||||
}
|
||||
if cfg.TimeoutPrevoteDelta < 0 {
|
||||
return errors.New("timeout_prevote_delta can't be negative")
|
||||
}
|
||||
if cfg.TimeoutPrecommit < 0 {
|
||||
return errors.New("timeout_precommit can't be negative")
|
||||
}
|
||||
if cfg.TimeoutPrecommitDelta < 0 {
|
||||
return errors.New("timeout_precommit_delta can't be negative")
|
||||
}
|
||||
if cfg.TimeoutCommit < 0 {
|
||||
return errors.New("timeout_commit can't be negative")
|
||||
}
|
||||
if cfg.CreateEmptyBlocksInterval < 0 {
|
||||
return errors.New("create_empty_blocks_interval can't be negative")
|
||||
}
|
||||
if cfg.PeerGossipSleepDuration < 0 {
|
||||
return errors.New("peer_gossip_sleep_duration can't be negative")
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// TxIndexConfig
|
||||
|
||||
// TxIndexConfig defines the configuration for the transaction
|
||||
// indexer, including tags to index.
|
||||
// TxIndexConfig defines the configuration for the transaction indexer,
|
||||
// including tags to index.
|
||||
type TxIndexConfig struct {
|
||||
// What indexer to use for transactions
|
||||
//
|
||||
@@ -566,16 +681,21 @@ type TxIndexConfig struct {
|
||||
// 2) "kv" (default) - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
||||
Indexer string `mapstructure:"indexer"`
|
||||
|
||||
// Comma-separated list of tags to index (by default the only tag is tx hash)
|
||||
// Comma-separated list of tags to index (by default the only tag is "tx.hash")
|
||||
//
|
||||
// You can also index transactions by height by adding "tx.height" tag here.
|
||||
//
|
||||
// It's recommended to index only a subset of tags due to possible memory
|
||||
// bloat. This is, of course, depends on the indexer's DB and the volume of
|
||||
// transactions.
|
||||
IndexTags string `mapstructure:"index_tags"`
|
||||
|
||||
// When set to true, tells indexer to index all tags. Note this may be not
|
||||
// desirable (see the comment above). IndexTags has a precedence over
|
||||
// IndexAllTags (i.e. when given both, IndexTags will be indexed).
|
||||
// When set to true, tells indexer to index all tags (predefined tags:
|
||||
// "tx.hash", "tx.height" and all tags from DeliverTx responses).
|
||||
//
|
||||
// Note this may be not desirable (see the comment above). IndexTags has a
|
||||
// precedence over IndexAllTags (i.e. when given both, IndexTags will be
|
||||
// indexed).
|
||||
IndexAllTags bool `mapstructure:"index_all_tags"`
|
||||
}
|
||||
|
||||
@@ -611,6 +731,9 @@ type InstrumentationConfig struct {
|
||||
// you increase your OS limits.
|
||||
// 0 - unlimited.
|
||||
MaxOpenConnections int `mapstructure:"max_open_connections"`
|
||||
|
||||
// Tendermint instrumentation namespace.
|
||||
Namespace string `mapstructure:"namespace"`
|
||||
}
|
||||
|
||||
// DefaultInstrumentationConfig returns a default configuration for metrics
|
||||
@@ -620,6 +743,7 @@ func DefaultInstrumentationConfig() *InstrumentationConfig {
|
||||
Prometheus: false,
|
||||
PrometheusListenAddr: ":26660",
|
||||
MaxOpenConnections: 3,
|
||||
Namespace: "tendermint",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,6 +753,15 @@ func TestInstrumentationConfig() *InstrumentationConfig {
|
||||
return DefaultInstrumentationConfig()
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg *InstrumentationConfig) ValidateBasic() error {
|
||||
if cfg.MaxOpenConnections < 0 {
|
||||
return errors.New("max_open_connections can't be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Utils
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -26,3 +27,12 @@ func TestDefaultConfig(t *testing.T) {
|
||||
assert.Equal("/foo/wal/mem", cfg.Mempool.WalDir())
|
||||
|
||||
}
|
||||
|
||||
func TestConfigValidateBasic(t *testing.T) {
|
||||
cfg := DefaultConfig()
|
||||
assert.NoError(t, cfg.ValidateBasic())
|
||||
|
||||
// tamper with timeout_propose
|
||||
cfg.Consensus.TimeoutPropose = -10 * time.Second
|
||||
assert.Error(t, cfg.ValidateBasic())
|
||||
}
|
||||
|
||||
@@ -77,11 +77,11 @@ moniker = "{{ .BaseConfig.Moniker }}"
|
||||
# and verifying their commits
|
||||
fast_sync = {{ .BaseConfig.FastSync }}
|
||||
|
||||
# Database backend: leveldb | memdb
|
||||
# Database backend: leveldb | memdb | cleveldb
|
||||
db_backend = "{{ .BaseConfig.DBBackend }}"
|
||||
|
||||
# Database directory
|
||||
db_path = "{{ js .BaseConfig.DBPath }}"
|
||||
db_dir = "{{ js .BaseConfig.DBPath }}"
|
||||
|
||||
# Output level for logging, including package level options
|
||||
log_level = "{{ .BaseConfig.LogLevel }}"
|
||||
@@ -94,8 +94,12 @@ genesis_file = "{{ js .BaseConfig.Genesis }}"
|
||||
# Path to the JSON file containing the private key to use as a validator in the consensus protocol
|
||||
priv_validator_file = "{{ js .BaseConfig.PrivValidator }}"
|
||||
|
||||
# TCP or UNIX socket address for Tendermint to listen on for
|
||||
# connections from an external PrivValidator process
|
||||
priv_validator_laddr = "{{ .BaseConfig.PrivValidatorListenAddr }}"
|
||||
|
||||
# Path to the JSON file containing the private key to use for node authentication in the p2p protocol
|
||||
node_key_file = "{{ js .BaseConfig.NodeKey}}"
|
||||
node_key_file = "{{ js .BaseConfig.NodeKey }}"
|
||||
|
||||
# Mechanism to connect to the ABCI application: socket | grpc
|
||||
abci = "{{ .BaseConfig.ABCI }}"
|
||||
@@ -124,6 +128,8 @@ grpc_laddr = "{{ .RPC.GRPCListenAddress }}"
|
||||
# If you want to accept more significant number than the default, make sure
|
||||
# you increase your OS limits.
|
||||
# 0 - unlimited.
|
||||
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||
# 1024 - 40 - 10 - 50 = 924 = ~900
|
||||
grpc_max_open_connections = {{ .RPC.GRPCMaxOpenConnections }}
|
||||
|
||||
# Activate unsafe RPC commands like /dial_seeds and /unsafe_flush_mempool
|
||||
@@ -134,6 +140,8 @@ unsafe = {{ .RPC.Unsafe }}
|
||||
# If you want to accept more significant number than the default, make sure
|
||||
# you increase your OS limits.
|
||||
# 0 - unlimited.
|
||||
# Should be < {ulimit -Sn} - {MaxNumInboundPeers} - {MaxNumOutboundPeers} - {N of wal, db and other open files}
|
||||
# 1024 - 40 - 10 - 50 = 924 = ~900
|
||||
max_open_connections = {{ .RPC.MaxOpenConnections }}
|
||||
|
||||
##### peer to peer configuration options #####
|
||||
@@ -161,13 +169,17 @@ upnp = {{ .P2P.UPNP }}
|
||||
addr_book_file = "{{ js .P2P.AddrBook }}"
|
||||
|
||||
# Set true for strict address routability rules
|
||||
# Set false for private or local networks
|
||||
addr_book_strict = {{ .P2P.AddrBookStrict }}
|
||||
|
||||
# Time to wait before flushing messages out on the connection, in ms
|
||||
flush_throttle_timeout = {{ .P2P.FlushThrottleTimeout }}
|
||||
# Maximum number of inbound peers
|
||||
max_num_inbound_peers = {{ .P2P.MaxNumInboundPeers }}
|
||||
|
||||
# Maximum number of peers to connect to
|
||||
max_num_peers = {{ .P2P.MaxNumPeers }}
|
||||
# Maximum number of outbound peers to connect to, excluding persistent peers
|
||||
max_num_outbound_peers = {{ .P2P.MaxNumOutboundPeers }}
|
||||
|
||||
# Time to wait before flushing messages out on the connection
|
||||
flush_throttle_timeout = "{{ .P2P.FlushThrottleTimeout }}"
|
||||
|
||||
# Maximum size of a message packet payload, in bytes
|
||||
max_packet_msg_payload_size = {{ .P2P.MaxPacketMsgPayloadSize }}
|
||||
@@ -190,11 +202,17 @@ seed_mode = {{ .P2P.SeedMode }}
|
||||
# Comma separated list of peer IDs to keep private (will not be gossiped to other peers)
|
||||
private_peer_ids = "{{ .P2P.PrivatePeerIDs }}"
|
||||
|
||||
# Toggle to disable guard against peers connecting from the same ip.
|
||||
allow_duplicate_ip = {{ .P2P.AllowDuplicateIP }}
|
||||
|
||||
# Peer connection configuration.
|
||||
handshake_timeout = "{{ .P2P.HandshakeTimeout }}"
|
||||
dial_timeout = "{{ .P2P.DialTimeout }}"
|
||||
|
||||
##### mempool configuration options #####
|
||||
[mempool]
|
||||
|
||||
recheck = {{ .Mempool.Recheck }}
|
||||
recheck_empty = {{ .Mempool.RecheckEmpty }}
|
||||
broadcast = {{ .Mempool.Broadcast }}
|
||||
wal_dir = "{{ js .Mempool.WalPath }}"
|
||||
|
||||
@@ -209,25 +227,24 @@ cache_size = {{ .Mempool.CacheSize }}
|
||||
|
||||
wal_file = "{{ js .Consensus.WalPath }}"
|
||||
|
||||
# All timeouts are in milliseconds
|
||||
timeout_propose = {{ .Consensus.TimeoutPropose }}
|
||||
timeout_propose_delta = {{ .Consensus.TimeoutProposeDelta }}
|
||||
timeout_prevote = {{ .Consensus.TimeoutPrevote }}
|
||||
timeout_prevote_delta = {{ .Consensus.TimeoutPrevoteDelta }}
|
||||
timeout_precommit = {{ .Consensus.TimeoutPrecommit }}
|
||||
timeout_precommit_delta = {{ .Consensus.TimeoutPrecommitDelta }}
|
||||
timeout_commit = {{ .Consensus.TimeoutCommit }}
|
||||
timeout_propose = "{{ .Consensus.TimeoutPropose }}"
|
||||
timeout_propose_delta = "{{ .Consensus.TimeoutProposeDelta }}"
|
||||
timeout_prevote = "{{ .Consensus.TimeoutPrevote }}"
|
||||
timeout_prevote_delta = "{{ .Consensus.TimeoutPrevoteDelta }}"
|
||||
timeout_precommit = "{{ .Consensus.TimeoutPrecommit }}"
|
||||
timeout_precommit_delta = "{{ .Consensus.TimeoutPrecommitDelta }}"
|
||||
timeout_commit = "{{ .Consensus.TimeoutCommit }}"
|
||||
|
||||
# Make progress as soon as we have all the precommits (as if TimeoutCommit = 0)
|
||||
skip_timeout_commit = {{ .Consensus.SkipTimeoutCommit }}
|
||||
|
||||
# EmptyBlocks mode and possible interval between empty blocks in seconds
|
||||
# EmptyBlocks mode and possible interval between empty blocks
|
||||
create_empty_blocks = {{ .Consensus.CreateEmptyBlocks }}
|
||||
create_empty_blocks_interval = {{ .Consensus.CreateEmptyBlocksInterval }}
|
||||
create_empty_blocks_interval = "{{ .Consensus.CreateEmptyBlocksInterval }}"
|
||||
|
||||
# Reactor sleep duration parameters are in milliseconds
|
||||
peer_gossip_sleep_duration = {{ .Consensus.PeerGossipSleepDuration }}
|
||||
peer_query_maj23_sleep_duration = {{ .Consensus.PeerQueryMaj23SleepDuration }}
|
||||
# Reactor sleep duration parameters
|
||||
peer_gossip_sleep_duration = "{{ .Consensus.PeerGossipSleepDuration }}"
|
||||
peer_query_maj23_sleep_duration = "{{ .Consensus.PeerQueryMaj23SleepDuration }}"
|
||||
|
||||
##### transactions indexer configuration options #####
|
||||
[tx_index]
|
||||
@@ -239,16 +256,21 @@ peer_query_maj23_sleep_duration = {{ .Consensus.PeerQueryMaj23SleepDuration }}
|
||||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
||||
indexer = "{{ .TxIndex.Indexer }}"
|
||||
|
||||
# Comma-separated list of tags to index (by default the only tag is tx hash)
|
||||
# Comma-separated list of tags to index (by default the only tag is "tx.hash")
|
||||
#
|
||||
# You can also index transactions by height by adding "tx.height" tag here.
|
||||
#
|
||||
# It's recommended to index only a subset of tags due to possible memory
|
||||
# bloat. This is, of course, depends on the indexer's DB and the volume of
|
||||
# transactions.
|
||||
index_tags = "{{ .TxIndex.IndexTags }}"
|
||||
|
||||
# When set to true, tells indexer to index all tags. Note this may be not
|
||||
# desirable (see the comment above). IndexTags has a precedence over
|
||||
# IndexAllTags (i.e. when given both, IndexTags will be indexed).
|
||||
# When set to true, tells indexer to index all tags (predefined tags:
|
||||
# "tx.hash", "tx.height" and all tags from DeliverTx responses).
|
||||
#
|
||||
# Note this may be not desirable (see the comment above). IndexTags has a
|
||||
# precedence over IndexAllTags (i.e. when given both, IndexTags will be
|
||||
# indexed).
|
||||
index_all_tags = {{ .TxIndex.IndexAllTags }}
|
||||
|
||||
##### instrumentation configuration options #####
|
||||
@@ -267,6 +289,9 @@ prometheus_listen_addr = "{{ .Instrumentation.PrometheusListenAddr }}"
|
||||
# you increase your OS limits.
|
||||
# 0 - unlimited.
|
||||
max_open_connections = {{ .Instrumentation.MaxOpenConnections }}
|
||||
|
||||
# Instrumentation namespace
|
||||
namespace = "{{ .Instrumentation.Namespace }}"
|
||||
`
|
||||
|
||||
/****** these are for test settings ***********/
|
||||
@@ -317,7 +342,7 @@ func ResetTestRoot(testName string) *Config {
|
||||
}
|
||||
|
||||
var testGenesis = `{
|
||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
||||
"genesis_time": "2017-10-10T08:20:13.695936996Z",
|
||||
"chain_id": "tendermint_test",
|
||||
"validators": [
|
||||
{
|
||||
|
||||
@@ -2,14 +2,15 @@ package consensus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -38,7 +39,13 @@ func TestByzantine(t *testing.T) {
|
||||
switches := make([]*p2p.Switch, N)
|
||||
p2pLogger := logger.With("module", "p2p")
|
||||
for i := 0; i < N; i++ {
|
||||
switches[i] = p2p.NewSwitch(config.P2P)
|
||||
switches[i] = p2p.MakeSwitch(
|
||||
config.P2P,
|
||||
i,
|
||||
"foo", "1.0.0",
|
||||
func(i int, sw *p2p.Switch) *p2p.Switch {
|
||||
return sw
|
||||
})
|
||||
switches[i].SetLogger(p2pLogger.With("validator", i))
|
||||
}
|
||||
|
||||
@@ -156,8 +163,8 @@ func TestByzantine(t *testing.T) {
|
||||
case <-done:
|
||||
case <-tick.C:
|
||||
for i, reactor := range reactors {
|
||||
t.Log(cmn.Fmt("Consensus Reactor %v", i))
|
||||
t.Log(cmn.Fmt("%v", reactor))
|
||||
t.Log(fmt.Sprintf("Consensus Reactor %v", i))
|
||||
t.Log(fmt.Sprintf("%v", reactor))
|
||||
}
|
||||
t.Fatalf("Timed out waiting for all validators to commit first block")
|
||||
}
|
||||
@@ -219,8 +226,8 @@ func sendProposalAndParts(height int64, round int, cs *ConsensusState, peer p2p.
|
||||
|
||||
// votes
|
||||
cs.mtx.Lock()
|
||||
prevote, _ := cs.signVote(types.VoteTypePrevote, blockHash, parts.Header())
|
||||
precommit, _ := cs.signVote(types.VoteTypePrecommit, blockHash, parts.Header())
|
||||
prevote, _ := cs.signVote(types.PrevoteType, blockHash, parts.Header())
|
||||
precommit, _ := cs.signVote(types.PrecommitType, blockHash, parts.Header())
|
||||
cs.mtx.Unlock()
|
||||
|
||||
peer.Send(VoteChannel, cdc.MustMarshalBinaryBare(&VoteMessage{prevote}))
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/counter"
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
@@ -37,8 +39,8 @@ const (
|
||||
)
|
||||
|
||||
// genesis, chain_id, priv_val
|
||||
var config *cfg.Config // NOTE: must be reset for each _test.go file
|
||||
var ensureTimeout = time.Second * 1 // must be in seconds because CreateEmptyBlocksInterval is
|
||||
var config *cfg.Config // NOTE: must be reset for each _test.go file
|
||||
var ensureTimeout = time.Millisecond * 100
|
||||
|
||||
func ensureDir(dir string, mode os.FileMode) {
|
||||
if err := cmn.EnsureDir(dir, mode); err != nil {
|
||||
@@ -69,13 +71,13 @@ func NewValidatorStub(privValidator types.PrivValidator, valIndex int) *validato
|
||||
}
|
||||
}
|
||||
|
||||
func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
func (vs *validatorStub) signVote(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
vote := &types.Vote{
|
||||
ValidatorIndex: vs.Index,
|
||||
ValidatorAddress: vs.PrivValidator.GetAddress(),
|
||||
Height: vs.Height,
|
||||
Round: vs.Round,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: voteType,
|
||||
BlockID: types.BlockID{hash, header},
|
||||
}
|
||||
@@ -84,7 +86,7 @@ func (vs *validatorStub) signVote(voteType byte, hash []byte, header types.PartS
|
||||
}
|
||||
|
||||
// Sign vote for type/hash/header
|
||||
func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||
func signVote(vs *validatorStub, voteType types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||
v, err := vs.signVote(voteType, hash, header)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to sign vote: %v", err))
|
||||
@@ -92,7 +94,7 @@ func signVote(vs *validatorStub, voteType byte, hash []byte, header types.PartSe
|
||||
return v
|
||||
}
|
||||
|
||||
func signVotes(voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
|
||||
func signVotes(voteType types.SignedMsgType, hash []byte, header types.PartSetHeader, vss ...*validatorStub) []*types.Vote {
|
||||
votes := make([]*types.Vote, len(vss))
|
||||
for i, vs := range vss {
|
||||
votes[i] = signVote(vs, voteType, hash, header)
|
||||
@@ -142,7 +144,7 @@ func addVotes(to *ConsensusState, votes ...*types.Vote) {
|
||||
}
|
||||
}
|
||||
|
||||
func signAddVotes(to *ConsensusState, voteType byte, hash []byte, header types.PartSetHeader, vss ...*validatorStub) {
|
||||
func signAddVotes(to *ConsensusState, voteType types.SignedMsgType, hash []byte, header types.PartSetHeader, vss ...*validatorStub) {
|
||||
votes := signVotes(voteType, hash, header, vss...)
|
||||
addVotes(to, votes...)
|
||||
}
|
||||
@@ -305,23 +307,199 @@ func randConsensusState(nValidators int) (*ConsensusState, []*validatorStub) {
|
||||
|
||||
//-------------------------------------------------------------------------------
|
||||
|
||||
func ensureNoNewStep(stepCh <-chan interface{}) {
|
||||
timer := time.NewTimer(ensureTimeout)
|
||||
func ensureNoNewEvent(ch <-chan interface{}, timeout time.Duration,
|
||||
errorMessage string) {
|
||||
select {
|
||||
case <-timer.C:
|
||||
case <-time.After(timeout):
|
||||
break
|
||||
case <-stepCh:
|
||||
panic("We should be stuck waiting, not moving to the next step")
|
||||
case <-ch:
|
||||
panic(errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureNewStep(stepCh <-chan interface{}) {
|
||||
timer := time.NewTimer(ensureTimeout)
|
||||
func ensureNoNewEventOnChannel(ch <-chan interface{}) {
|
||||
ensureNoNewEvent(
|
||||
ch,
|
||||
ensureTimeout,
|
||||
"We should be stuck waiting, not receiving new event on the channel")
|
||||
}
|
||||
|
||||
func ensureNoNewRoundStep(stepCh <-chan interface{}) {
|
||||
ensureNoNewEvent(
|
||||
stepCh,
|
||||
ensureTimeout,
|
||||
"We should be stuck waiting, not receiving NewRoundStep event")
|
||||
}
|
||||
|
||||
func ensureNoNewUnlock(unlockCh <-chan interface{}) {
|
||||
ensureNoNewEvent(
|
||||
unlockCh,
|
||||
ensureTimeout,
|
||||
"We should be stuck waiting, not receiving Unlock event")
|
||||
}
|
||||
|
||||
func ensureNoNewTimeout(stepCh <-chan interface{}, timeout int64) {
|
||||
timeoutDuration := time.Duration(timeout*5) * time.Nanosecond
|
||||
ensureNoNewEvent(
|
||||
stepCh,
|
||||
timeoutDuration,
|
||||
"We should be stuck waiting, not receiving NewTimeout event")
|
||||
}
|
||||
|
||||
func ensureNewEvent(
|
||||
ch <-chan interface{},
|
||||
height int64,
|
||||
round int,
|
||||
timeout time.Duration,
|
||||
errorMessage string) {
|
||||
|
||||
select {
|
||||
case <-timer.C:
|
||||
panic("We shouldnt be stuck waiting")
|
||||
case <-stepCh:
|
||||
case <-time.After(timeout):
|
||||
panic(errorMessage)
|
||||
case ev := <-ch:
|
||||
rs, ok := ev.(types.EventDataRoundState)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataRoundState, 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))
|
||||
}
|
||||
// TODO: We could check also for a step at this point!
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
ensureNewEvent(roundCh, height, round, ensureTimeout,
|
||||
"Timeout expired while waiting for NewRound event")
|
||||
}
|
||||
|
||||
func ensureNewTimeout(timeoutCh <-chan interface{}, height int64, round int, timeout int64) {
|
||||
timeoutDuration := time.Duration(timeout*3) * time.Nanosecond
|
||||
ensureNewEvent(timeoutCh, height, round, timeoutDuration,
|
||||
"Timeout expired while waiting for NewTimeout event")
|
||||
}
|
||||
|
||||
func ensureNewProposal(proposalCh <-chan interface{}, height int64, round int) {
|
||||
ensureNewEvent(proposalCh, height, round, ensureTimeout,
|
||||
"Timeout expired while waiting for NewProposal event")
|
||||
}
|
||||
|
||||
func ensureNewBlock(blockCh <-chan interface{}, height int64) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewBlock event")
|
||||
case ev := <-blockCh:
|
||||
block, ok := ev.(types.EventDataNewBlock)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("expected a *types.EventDataNewBlock, "+
|
||||
"got %v. wrong subscription channel?",
|
||||
reflect.TypeOf(block)))
|
||||
}
|
||||
if block.Block.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, block.Block.Height))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureNewBlockHeader(blockCh <-chan interface{}, 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)
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("expected a *types.EventDataNewBlockHeader, "+
|
||||
"got %v. wrong subscription channel?",
|
||||
reflect.TypeOf(blockHeader)))
|
||||
}
|
||||
if blockHeader.Header.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, blockHeader.Header.Height))
|
||||
}
|
||||
if !bytes.Equal(blockHeader.Header.Hash(), blockHash) {
|
||||
panic(fmt.Sprintf("expected header %X, got %X", blockHash, blockHeader.Header.Hash()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureNewUnlock(unlockCh <-chan interface{}, 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,
|
||||
voteType types.SignedMsgType) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewVote event")
|
||||
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))
|
||||
}
|
||||
if vote.Type != voteType {
|
||||
panic(fmt.Sprintf("expected type %v, got %v", voteType, vote.Type))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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{}) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for new activity on the channel")
|
||||
case <-ch:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,13 +526,13 @@ func randConsensusNet(nValidators int, testName string, tickerFunc func() Timeou
|
||||
for i := 0; i < nValidators; i++ {
|
||||
stateDB := dbm.NewMemDB() // each state needs its own db
|
||||
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
|
||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||
for _, opt := range configOpts {
|
||||
opt(thisConfig)
|
||||
}
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
app := appFunc()
|
||||
vals := types.TM2PB.Validators(state.Validators)
|
||||
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
app.InitChain(abci.RequestInitChain{Validators: vals})
|
||||
|
||||
css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], app)
|
||||
@@ -372,7 +550,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
||||
for i := 0; i < nPeers; i++ {
|
||||
stateDB := dbm.NewMemDB() // each state needs its own db
|
||||
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
|
||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
var privVal types.PrivValidator
|
||||
if i < nValidators {
|
||||
@@ -386,7 +564,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
||||
}
|
||||
|
||||
app := appFunc()
|
||||
vals := types.TM2PB.Validators(state.Validators)
|
||||
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
app.InitChain(abci.RequestInitChain{Validators: vals})
|
||||
|
||||
css[i] = newConsensusStateWithConfig(thisConfig, state, privVal, app)
|
||||
@@ -398,7 +576,7 @@ func randConsensusNetWithPeers(nValidators, nPeers int, testName string, tickerF
|
||||
|
||||
func getSwitchIndex(switches []*p2p.Switch, peer p2p.Peer) int {
|
||||
for i, s := range switches {
|
||||
if peer.NodeInfo().ID == s.NodeInfo().ID {
|
||||
if peer.NodeInfo().ID() == s.NodeInfo().ID() {
|
||||
return i
|
||||
}
|
||||
}
|
||||
@@ -423,7 +601,7 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
|
||||
sort.Sort(types.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return &types.GenesisDoc{
|
||||
GenesisTime: time.Now(),
|
||||
GenesisTime: tmtime.Now(),
|
||||
ChainID: config.ChainID(),
|
||||
Validators: validators,
|
||||
}, privValidators
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/code"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
@@ -29,17 +28,17 @@ func TestMempoolNoProgressUntilTxsAvailable(t *testing.T) {
|
||||
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
||||
startTestRound(cs, height, round)
|
||||
|
||||
ensureNewStep(newBlockCh) // first block gets committed
|
||||
ensureNoNewStep(newBlockCh)
|
||||
ensureNewEventOnChannel(newBlockCh) // first block gets committed
|
||||
ensureNoNewEventOnChannel(newBlockCh)
|
||||
deliverTxsRange(cs, 0, 1)
|
||||
ensureNewStep(newBlockCh) // commit txs
|
||||
ensureNewStep(newBlockCh) // commit updated app hash
|
||||
ensureNoNewStep(newBlockCh)
|
||||
ensureNewEventOnChannel(newBlockCh) // commit txs
|
||||
ensureNewEventOnChannel(newBlockCh) // commit updated app hash
|
||||
ensureNoNewEventOnChannel(newBlockCh)
|
||||
}
|
||||
|
||||
func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
|
||||
config := ResetConfig("consensus_mempool_txs_available_test")
|
||||
config.Consensus.CreateEmptyBlocksInterval = int(ensureTimeout.Seconds())
|
||||
config.Consensus.CreateEmptyBlocksInterval = ensureTimeout
|
||||
state, privVals := randGenesisState(1, false, 10)
|
||||
cs := newConsensusStateWithConfig(config, state, privVals[0], NewCounterApplication())
|
||||
cs.mempool.EnableTxsAvailable()
|
||||
@@ -47,9 +46,9 @@ func TestMempoolProgressAfterCreateEmptyBlocksInterval(t *testing.T) {
|
||||
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
||||
startTestRound(cs, height, round)
|
||||
|
||||
ensureNewStep(newBlockCh) // first block gets committed
|
||||
ensureNoNewStep(newBlockCh) // then we dont make a block ...
|
||||
ensureNewStep(newBlockCh) // until the CreateEmptyBlocksInterval has passed
|
||||
ensureNewEventOnChannel(newBlockCh) // first block gets committed
|
||||
ensureNoNewEventOnChannel(newBlockCh) // then we dont make a block ...
|
||||
ensureNewEventOnChannel(newBlockCh) // until the CreateEmptyBlocksInterval has passed
|
||||
}
|
||||
|
||||
func TestMempoolProgressInHigherRound(t *testing.T) {
|
||||
@@ -73,13 +72,19 @@ func TestMempoolProgressInHigherRound(t *testing.T) {
|
||||
}
|
||||
startTestRound(cs, height, round)
|
||||
|
||||
ensureNewStep(newRoundCh) // first round at first height
|
||||
ensureNewStep(newBlockCh) // first block gets committed
|
||||
ensureNewStep(newRoundCh) // first round at next height
|
||||
deliverTxsRange(cs, 0, 1) // we deliver txs, but dont set a proposal so we get the next round
|
||||
<-timeoutCh
|
||||
ensureNewStep(newRoundCh) // wait for the next round
|
||||
ensureNewStep(newBlockCh) // now we can commit the block
|
||||
ensureNewRoundStep(newRoundCh, height, round) // first round at first height
|
||||
ensureNewEventOnChannel(newBlockCh) // first block gets committed
|
||||
|
||||
height = height + 1 // moving to the next height
|
||||
round = 0
|
||||
|
||||
ensureNewRoundStep(newRoundCh, height, round) // first round at next height
|
||||
deliverTxsRange(cs, 0, 1) // we deliver txs, but dont set a proposal so we get the next round
|
||||
ensureNewTimeout(timeoutCh, height, round, cs.config.TimeoutPropose.Nanoseconds())
|
||||
|
||||
round = round + 1 // moving to the next round
|
||||
ensureNewRoundStep(newRoundCh, height, round) // wait for the next round
|
||||
ensureNewEventOnChannel(newBlockCh) // now we can commit the block
|
||||
}
|
||||
|
||||
func deliverTxsRange(cs *ConsensusState, start, end int) {
|
||||
@@ -89,7 +94,7 @@ func deliverTxsRange(cs *ConsensusState, start, end int) {
|
||||
binary.BigEndian.PutUint64(txBytes, uint64(i))
|
||||
err := cs.mempool.CheckTx(txBytes, nil)
|
||||
if err != nil {
|
||||
panic(cmn.Fmt("Error after CheckTx: %v", err))
|
||||
panic(fmt.Sprintf("Error after CheckTx: %v", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +105,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) {
|
||||
height, round := cs.Height, cs.Round
|
||||
newBlockCh := subscribe(cs.eventBus, types.EventQueryNewBlock)
|
||||
|
||||
NTxs := 10000
|
||||
NTxs := 3000
|
||||
go deliverTxsRange(cs, 0, NTxs)
|
||||
|
||||
startTestRound(cs, height, round)
|
||||
@@ -126,7 +131,7 @@ func TestMempoolRmBadTx(t *testing.T) {
|
||||
binary.BigEndian.PutUint64(txBytes, uint64(0))
|
||||
|
||||
resDeliver := app.DeliverTx(txBytes)
|
||||
assert.False(t, resDeliver.IsErr(), cmn.Fmt("expected no error. got %v", resDeliver))
|
||||
assert.False(t, resDeliver.IsErr(), fmt.Sprintf("expected no error. got %v", resDeliver))
|
||||
|
||||
resCommit := app.Commit()
|
||||
assert.True(t, len(resCommit.Data) > 0)
|
||||
@@ -149,7 +154,7 @@ func TestMempoolRmBadTx(t *testing.T) {
|
||||
|
||||
// check for the tx
|
||||
for {
|
||||
txs := cs.mempool.Reap(1)
|
||||
txs := cs.mempool.ReapMaxBytesMaxGas(int64(len(txBytes)), -1)
|
||||
if len(txs) == 0 {
|
||||
emptyMempoolCh <- struct{}{}
|
||||
return
|
||||
@@ -190,7 +195,7 @@ func NewCounterApplication() *CounterApplication {
|
||||
}
|
||||
|
||||
func (app *CounterApplication) Info(req abci.RequestInfo) abci.ResponseInfo {
|
||||
return abci.ResponseInfo{Data: cmn.Fmt("txs:%v", app.txCount)}
|
||||
return abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}
|
||||
}
|
||||
|
||||
func (app *CounterApplication) DeliverTx(tx []byte) abci.ResponseDeliverTx {
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
stdprometheus "github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const MetricsSubsystem = "consensus"
|
||||
|
||||
// Metrics contains metrics exposed by this package.
|
||||
type Metrics struct {
|
||||
// Height of the chain.
|
||||
@@ -30,7 +32,7 @@ type Metrics struct {
|
||||
ByzantineValidatorsPower metrics.Gauge
|
||||
|
||||
// Time between this and the last block.
|
||||
BlockIntervalSeconds metrics.Histogram
|
||||
BlockIntervalSeconds metrics.Gauge
|
||||
|
||||
// Number of transactions.
|
||||
NumTxs metrics.Gauge
|
||||
@@ -38,75 +40,111 @@ type Metrics struct {
|
||||
BlockSizeBytes metrics.Gauge
|
||||
// Total number of transactions.
|
||||
TotalTxs metrics.Gauge
|
||||
// The latest block height.
|
||||
CommittedHeight metrics.Gauge
|
||||
// Whether or not a node is fast syncing. 1 if yes, 0 if no.
|
||||
FastSyncing metrics.Gauge
|
||||
|
||||
// Number of blockparts transmitted by peer.
|
||||
BlockParts metrics.Counter
|
||||
}
|
||||
|
||||
// PrometheusMetrics returns Metrics build using Prometheus client library.
|
||||
func PrometheusMetrics() *Metrics {
|
||||
func PrometheusMetrics(namespace string) *Metrics {
|
||||
return &Metrics{
|
||||
Height: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "height",
|
||||
Help: "Height of the chain.",
|
||||
}, []string{}),
|
||||
Rounds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "rounds",
|
||||
Help: "Number of rounds.",
|
||||
}, []string{}),
|
||||
|
||||
Validators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validators",
|
||||
Help: "Number of validators.",
|
||||
}, []string{}),
|
||||
ValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "validators_power",
|
||||
Help: "Total power of all validators.",
|
||||
}, []string{}),
|
||||
MissingValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "missing_validators",
|
||||
Help: "Number of validators who did not sign.",
|
||||
}, []string{}),
|
||||
MissingValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "missing_validators_power",
|
||||
Help: "Total power of the missing validators.",
|
||||
}, []string{}),
|
||||
ByzantineValidators: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "byzantine_validators",
|
||||
Help: "Number of validators who tried to double sign.",
|
||||
}, []string{}),
|
||||
ByzantineValidatorsPower: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "byzantine_validators_power",
|
||||
Help: "Total power of the byzantine validators.",
|
||||
}, []string{}),
|
||||
|
||||
BlockIntervalSeconds: prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{
|
||||
Subsystem: "consensus",
|
||||
BlockIntervalSeconds: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_interval_seconds",
|
||||
Help: "Time between this and the last block.",
|
||||
Buckets: []float64{1, 2.5, 5, 10, 60},
|
||||
}, []string{}),
|
||||
|
||||
NumTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "num_txs",
|
||||
Help: "Number of transactions.",
|
||||
}, []string{}),
|
||||
BlockSizeBytes: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_size_bytes",
|
||||
Help: "Size of the block.",
|
||||
}, []string{}),
|
||||
TotalTxs: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Subsystem: "consensus",
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "total_txs",
|
||||
Help: "Total number of transactions.",
|
||||
}, []string{}),
|
||||
CommittedHeight: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "latest_block_height",
|
||||
Help: "The latest block height.",
|
||||
}, []string{}),
|
||||
FastSyncing: prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "fast_syncing",
|
||||
Help: "Whether or not a node is fast syncing. 1 if yes, 0 if no.",
|
||||
}, []string{}),
|
||||
BlockParts: prometheus.NewCounterFrom(stdprometheus.CounterOpts{
|
||||
Namespace: namespace,
|
||||
Subsystem: MetricsSubsystem,
|
||||
Name: "block_parts",
|
||||
Help: "Number of blockparts transmitted by peer.",
|
||||
}, []string{"peer_id"}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,10 +162,13 @@ func NopMetrics() *Metrics {
|
||||
ByzantineValidators: discard.NewGauge(),
|
||||
ByzantineValidatorsPower: discard.NewGauge(),
|
||||
|
||||
BlockIntervalSeconds: discard.NewHistogram(),
|
||||
BlockIntervalSeconds: discard.NewGauge(),
|
||||
|
||||
NumTxs: discard.NewGauge(),
|
||||
BlockSizeBytes: discard.NewGauge(),
|
||||
TotalTxs: discard.NewGauge(),
|
||||
NumTxs: discard.NewGauge(),
|
||||
BlockSizeBytes: discard.NewGauge(),
|
||||
TotalTxs: discard.NewGauge(),
|
||||
CommittedHeight: discard.NewGauge(),
|
||||
FastSyncing: discard.NewGauge(),
|
||||
BlockParts: discard.NewCounter(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/go-amino"
|
||||
|
||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -28,6 +29,7 @@ const (
|
||||
maxMsgSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
|
||||
|
||||
blocksToContributeToBecomeGoodPeer = 10000
|
||||
votesToContributeToBecomeGoodPeer = 10000
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -41,16 +43,27 @@ type ConsensusReactor struct {
|
||||
mtx sync.RWMutex
|
||||
fastSync bool
|
||||
eventBus *types.EventBus
|
||||
|
||||
metrics *Metrics
|
||||
}
|
||||
|
||||
type ReactorOption func(*ConsensusReactor)
|
||||
|
||||
// NewConsensusReactor returns a new ConsensusReactor with the given
|
||||
// consensusState.
|
||||
func NewConsensusReactor(consensusState *ConsensusState, fastSync bool) *ConsensusReactor {
|
||||
func NewConsensusReactor(consensusState *ConsensusState, fastSync bool, options ...ReactorOption) *ConsensusReactor {
|
||||
conR := &ConsensusReactor{
|
||||
conS: consensusState,
|
||||
fastSync: fastSync,
|
||||
metrics: NopMetrics(),
|
||||
}
|
||||
conR.updateFastSyncingMetric()
|
||||
conR.BaseReactor = *p2p.NewBaseReactor("ConsensusReactor", conR)
|
||||
|
||||
for _, option := range options {
|
||||
option(conR)
|
||||
}
|
||||
|
||||
return conR
|
||||
}
|
||||
|
||||
@@ -59,6 +72,9 @@ func NewConsensusReactor(consensusState *ConsensusState, fastSync bool) *Consens
|
||||
func (conR *ConsensusReactor) OnStart() error {
|
||||
conR.Logger.Info("ConsensusReactor ", "fastSync", conR.FastSync())
|
||||
|
||||
// start routine that computes peer statistics for evaluating peer quality
|
||||
go conR.peerStatsRoutine()
|
||||
|
||||
conR.subscribeToBroadcastEvents()
|
||||
|
||||
if !conR.FastSync() {
|
||||
@@ -93,6 +109,7 @@ func (conR *ConsensusReactor) SwitchToConsensus(state sm.State, blocksSynced int
|
||||
conR.mtx.Lock()
|
||||
conR.fastSync = false
|
||||
conR.mtx.Unlock()
|
||||
conR.metrics.FastSyncing.Set(0)
|
||||
|
||||
if blocksSynced > 0 {
|
||||
// dont bother with the WAL if we fast synced
|
||||
@@ -220,9 +237,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
// (and consequently shows which we don't have)
|
||||
var ourVotes *cmn.BitArray
|
||||
switch msg.Type {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
|
||||
default:
|
||||
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
|
||||
@@ -241,7 +258,7 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
"height", hb.Height, "round", hb.Round, "sequence", hb.Sequence,
|
||||
"valIdx", hb.ValidatorIndex, "valAddr", hb.ValidatorAddress)
|
||||
default:
|
||||
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
|
||||
case DataChannel:
|
||||
@@ -257,12 +274,10 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
ps.ApplyProposalPOLMessage(msg)
|
||||
case *BlockPartMessage:
|
||||
ps.SetHasProposalBlockPart(msg.Height, msg.Round, msg.Part.Index)
|
||||
if numBlocks := ps.RecordBlockPart(msg); numBlocks%blocksToContributeToBecomeGoodPeer == 0 {
|
||||
conR.Switch.MarkPeerAsGood(src)
|
||||
}
|
||||
conR.metrics.BlockParts.With("peer_id", string(src.ID())).Add(1)
|
||||
conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
|
||||
default:
|
||||
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
|
||||
case VoteChannel:
|
||||
@@ -279,15 +294,12 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
ps.EnsureVoteBitArrays(height, valSize)
|
||||
ps.EnsureVoteBitArrays(height-1, lastCommitSize)
|
||||
ps.SetHasVote(msg.Vote)
|
||||
if blocks := ps.RecordVote(msg.Vote); blocks%blocksToContributeToBecomeGoodPeer == 0 {
|
||||
conR.Switch.MarkPeerAsGood(src)
|
||||
}
|
||||
|
||||
cs.peerMsgQueue <- msgInfo{msg, src.ID()}
|
||||
|
||||
default:
|
||||
// don't punish (leave room for soft upgrades)
|
||||
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
|
||||
case VoteSetBitsChannel:
|
||||
@@ -305,9 +317,9 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
if height == msg.Height {
|
||||
var ourVotes *cmn.BitArray
|
||||
switch msg.Type {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
|
||||
default:
|
||||
conR.Logger.Error("Bad VoteSetBitsMessage field Type")
|
||||
@@ -319,11 +331,11 @@ func (conR *ConsensusReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
}
|
||||
default:
|
||||
// don't punish (leave room for soft upgrades)
|
||||
conR.Logger.Error(cmn.Fmt("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
|
||||
}
|
||||
|
||||
default:
|
||||
conR.Logger.Error(cmn.Fmt("Unknown chId %X", chID))
|
||||
conR.Logger.Error(fmt.Sprintf("Unknown chId %X", chID))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -482,7 +494,7 @@ OUTER_LOOP:
|
||||
if prs.ProposalBlockParts == nil {
|
||||
blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height)
|
||||
if blockMeta == nil {
|
||||
cmn.PanicCrisis(cmn.Fmt("Failed to load block %d when blockStore is at %d",
|
||||
cmn.PanicCrisis(fmt.Sprintf("Failed to load block %d when blockStore is at %d",
|
||||
prs.Height, conR.conS.blockStore.Height()))
|
||||
}
|
||||
ps.InitProposalBlockParts(blockMeta.BlockID.PartsHeader)
|
||||
@@ -496,7 +508,7 @@ OUTER_LOOP:
|
||||
// If height and round don't match, sleep.
|
||||
if (rs.Height != prs.Height) || (rs.Round != prs.Round) {
|
||||
//logger.Info("Peer Height|Round mismatch, sleeping", "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer)
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
continue OUTER_LOOP
|
||||
}
|
||||
|
||||
@@ -532,7 +544,7 @@ OUTER_LOOP:
|
||||
}
|
||||
|
||||
// Nothing to do. Sleep.
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
continue OUTER_LOOP
|
||||
}
|
||||
}
|
||||
@@ -546,12 +558,12 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
|
||||
if blockMeta == nil {
|
||||
logger.Error("Failed to load block meta",
|
||||
"ourHeight", rs.Height, "blockstoreHeight", conR.conS.blockStore.Height())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
return
|
||||
} else if !blockMeta.BlockID.PartsHeader.Equals(prs.ProposalBlockPartsHeader) {
|
||||
logger.Info("Peer ProposalBlockPartsHeader mismatch, sleeping",
|
||||
"blockPartsHeader", blockMeta.BlockID.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader)
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
return
|
||||
}
|
||||
// Load the part
|
||||
@@ -559,7 +571,7 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
|
||||
if part == nil {
|
||||
logger.Error("Could not load part", "index", index,
|
||||
"blockPartsHeader", blockMeta.BlockID.PartsHeader, "peerBlockPartsHeader", prs.ProposalBlockPartsHeader)
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
return
|
||||
}
|
||||
// Send the part
|
||||
@@ -577,7 +589,7 @@ func (conR *ConsensusReactor) gossipDataForCatchup(logger log.Logger, rs *cstype
|
||||
return
|
||||
}
|
||||
//logger.Info("No parts to send in catch-up, sleeping")
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
}
|
||||
|
||||
func (conR *ConsensusReactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) {
|
||||
@@ -646,7 +658,7 @@ OUTER_LOOP:
|
||||
sleeping = 1
|
||||
}
|
||||
|
||||
time.Sleep(conR.conS.config.PeerGossipSleep())
|
||||
time.Sleep(conR.conS.config.PeerGossipSleepDuration)
|
||||
continue OUTER_LOOP
|
||||
}
|
||||
}
|
||||
@@ -727,10 +739,10 @@ OUTER_LOOP:
|
||||
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||
Height: prs.Height,
|
||||
Round: prs.Round,
|
||||
Type: types.VoteTypePrevote,
|
||||
Type: types.PrevoteType,
|
||||
BlockID: maj23,
|
||||
}))
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -744,10 +756,10 @@ OUTER_LOOP:
|
||||
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||
Height: prs.Height,
|
||||
Round: prs.Round,
|
||||
Type: types.VoteTypePrecommit,
|
||||
Type: types.PrecommitType,
|
||||
BlockID: maj23,
|
||||
}))
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,10 +773,10 @@ OUTER_LOOP:
|
||||
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||
Height: prs.Height,
|
||||
Round: prs.ProposalPOLRound,
|
||||
Type: types.VoteTypePrevote,
|
||||
Type: types.PrevoteType,
|
||||
BlockID: maj23,
|
||||
}))
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -780,19 +792,56 @@ OUTER_LOOP:
|
||||
peer.TrySend(StateChannel, cdc.MustMarshalBinaryBare(&VoteSetMaj23Message{
|
||||
Height: prs.Height,
|
||||
Round: commit.Round(),
|
||||
Type: types.VoteTypePrecommit,
|
||||
Type: types.PrecommitType,
|
||||
BlockID: commit.BlockID,
|
||||
}))
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23Sleep())
|
||||
time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
|
||||
|
||||
continue OUTER_LOOP
|
||||
}
|
||||
}
|
||||
|
||||
func (conR *ConsensusReactor) peerStatsRoutine() {
|
||||
for {
|
||||
if !conR.IsRunning() {
|
||||
conR.Logger.Info("Stopping peerStatsRoutine")
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case msg := <-conR.conS.statsMsgQueue:
|
||||
// Get peer
|
||||
peer := conR.Switch.Peers().Get(msg.PeerID)
|
||||
if peer == nil {
|
||||
conR.Logger.Debug("Attempt to update stats for non-existent peer",
|
||||
"peer", msg.PeerID)
|
||||
continue
|
||||
}
|
||||
// Get peer state
|
||||
ps := peer.Get(types.PeerStateKey).(*PeerState)
|
||||
switch msg.Msg.(type) {
|
||||
case *VoteMessage:
|
||||
if numVotes := ps.RecordVote(); numVotes%votesToContributeToBecomeGoodPeer == 0 {
|
||||
conR.Switch.MarkPeerAsGood(peer)
|
||||
}
|
||||
case *BlockPartMessage:
|
||||
if numParts := ps.RecordBlockPart(); numParts%blocksToContributeToBecomeGoodPeer == 0 {
|
||||
conR.Switch.MarkPeerAsGood(peer)
|
||||
}
|
||||
}
|
||||
case <-conR.conS.Quit():
|
||||
return
|
||||
|
||||
case <-conR.Quit():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of the ConsensusReactor.
|
||||
// NOTE: For now, it is just a hard-coded string to avoid accessing unprotected shared variables.
|
||||
// TODO: improve!
|
||||
@@ -813,6 +862,21 @@ func (conR *ConsensusReactor) StringIndented(indent string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
func (conR *ConsensusReactor) updateFastSyncingMetric() {
|
||||
var fastSyncing float64
|
||||
if conR.fastSync {
|
||||
fastSyncing = 1
|
||||
} else {
|
||||
fastSyncing = 0
|
||||
}
|
||||
conR.metrics.FastSyncing.Set(fastSyncing)
|
||||
}
|
||||
|
||||
// ReactorMetrics sets the metrics
|
||||
func ReactorMetrics(metrics *Metrics) ReactorOption {
|
||||
return func(conR *ConsensusReactor) { conR.metrics = metrics }
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
var (
|
||||
@@ -835,15 +899,13 @@ type PeerState struct {
|
||||
|
||||
// peerStateStats holds internal statistics for a peer.
|
||||
type peerStateStats struct {
|
||||
LastVoteHeight int64 `json:"last_vote_height"`
|
||||
Votes int `json:"votes"`
|
||||
LastBlockPartHeight int64 `json:"last_block_part_height"`
|
||||
BlockParts int `json:"block_parts"`
|
||||
Votes int `json:"votes"`
|
||||
BlockParts int `json:"block_parts"`
|
||||
}
|
||||
|
||||
func (pss peerStateStats) String() string {
|
||||
return fmt.Sprintf("peerStateStats{lvh: %d, votes: %d, lbph: %d, blockParts: %d}",
|
||||
pss.LastVoteHeight, pss.Votes, pss.LastBlockPartHeight, pss.BlockParts)
|
||||
return fmt.Sprintf("peerStateStats{votes: %d, blockParts: %d}",
|
||||
pss.Votes, pss.BlockParts)
|
||||
}
|
||||
|
||||
// NewPeerState returns a new PeerState for the given Peer
|
||||
@@ -960,7 +1022,7 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote
|
||||
return nil, false
|
||||
}
|
||||
|
||||
height, round, type_, size := votes.Height(), votes.Round(), votes.Type(), votes.Size()
|
||||
height, round, type_, size := votes.Height(), votes.Round(), types.SignedMsgType(votes.Type()), votes.Size()
|
||||
|
||||
// Lazily set data using 'votes'.
|
||||
if votes.IsCommit() {
|
||||
@@ -979,7 +1041,7 @@ func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.BitArray {
|
||||
func (ps *PeerState) getVoteBitArray(height int64, round int, type_ types.SignedMsgType) *cmn.BitArray {
|
||||
if !types.IsVoteTypeValid(type_) {
|
||||
return nil
|
||||
}
|
||||
@@ -987,25 +1049,25 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
|
||||
if ps.PRS.Height == height {
|
||||
if ps.PRS.Round == round {
|
||||
switch type_ {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
return ps.PRS.Prevotes
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
return ps.PRS.Precommits
|
||||
}
|
||||
}
|
||||
if ps.PRS.CatchupCommitRound == round {
|
||||
switch type_ {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
return nil
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
return ps.PRS.CatchupCommit
|
||||
}
|
||||
}
|
||||
if ps.PRS.ProposalPOLRound == round {
|
||||
switch type_ {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
return ps.PRS.ProposalPOL
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1014,9 +1076,9 @@ func (ps *PeerState) getVoteBitArray(height int64, round int, type_ byte) *cmn.B
|
||||
if ps.PRS.Height == height+1 {
|
||||
if ps.PRS.LastCommitRound == round {
|
||||
switch type_ {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
return nil
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
return ps.PRS.LastCommit
|
||||
}
|
||||
}
|
||||
@@ -1034,7 +1096,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(cmn.Fmt("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
|
||||
cmn.PanicSanity(fmt.Sprintf("Conflicting CatchupCommitRound. Height: %v, Orig: %v, New: %v", height, ps.CatchupCommitRound, round))
|
||||
}
|
||||
*/
|
||||
if ps.PRS.CatchupCommitRound == round {
|
||||
@@ -1079,18 +1141,14 @@ func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
|
||||
}
|
||||
}
|
||||
|
||||
// RecordVote updates internal statistics for this peer by recording the vote.
|
||||
// It returns the total number of votes (1 per block). This essentially means
|
||||
// the number of blocks for which peer has been sending us votes.
|
||||
func (ps *PeerState) RecordVote(vote *types.Vote) int {
|
||||
// RecordVote increments internal votes related statistics for this peer.
|
||||
// It returns the total number of added votes.
|
||||
func (ps *PeerState) RecordVote() int {
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
|
||||
if ps.Stats.LastVoteHeight >= vote.Height {
|
||||
return ps.Stats.Votes
|
||||
}
|
||||
ps.Stats.LastVoteHeight = vote.Height
|
||||
ps.Stats.Votes++
|
||||
|
||||
return ps.Stats.Votes
|
||||
}
|
||||
|
||||
@@ -1103,25 +1161,17 @@ func (ps *PeerState) VotesSent() int {
|
||||
return ps.Stats.Votes
|
||||
}
|
||||
|
||||
// RecordBlockPart updates internal statistics for this peer by recording the
|
||||
// block part. It returns the total number of block parts (1 per block). This
|
||||
// essentially means the number of blocks for which peer has been sending us
|
||||
// block parts.
|
||||
func (ps *PeerState) RecordBlockPart(bp *BlockPartMessage) int {
|
||||
// RecordBlockPart increments internal block part related statistics for this peer.
|
||||
// It returns the total number of added block parts.
|
||||
func (ps *PeerState) RecordBlockPart() int {
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
|
||||
if ps.Stats.LastBlockPartHeight >= bp.Height {
|
||||
return ps.Stats.BlockParts
|
||||
}
|
||||
|
||||
ps.Stats.LastBlockPartHeight = bp.Height
|
||||
ps.Stats.BlockParts++
|
||||
return ps.Stats.BlockParts
|
||||
}
|
||||
|
||||
// BlockPartsSent returns the number of blocks for which peer has been sending
|
||||
// us block parts.
|
||||
// BlockPartsSent returns the number of useful block parts the peer has sent us.
|
||||
func (ps *PeerState) BlockPartsSent() int {
|
||||
ps.mtx.Lock()
|
||||
defer ps.mtx.Unlock()
|
||||
@@ -1137,8 +1187,8 @@ func (ps *PeerState) SetHasVote(vote *types.Vote) {
|
||||
ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex)
|
||||
}
|
||||
|
||||
func (ps *PeerState) setHasVote(height int64, round int, type_ byte, index int) {
|
||||
logger := ps.logger.With("peerH/R", cmn.Fmt("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", cmn.Fmt("%d/%d", height, round))
|
||||
func (ps *PeerState) setHasVote(height int64, round int, type_ types.SignedMsgType, index int) {
|
||||
logger := ps.logger.With("peerH/R", fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round), "H/R", fmt.Sprintf("%d/%d", height, round))
|
||||
logger.Debug("setHasVote", "type", type_, "index", index)
|
||||
|
||||
// NOTE: some may be nil BitArrays -> no side effects.
|
||||
@@ -1165,7 +1215,7 @@ func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
|
||||
psCatchupCommitRound := ps.PRS.CatchupCommitRound
|
||||
psCatchupCommit := ps.PRS.CatchupCommit
|
||||
|
||||
startTime := time.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
|
||||
startTime := tmtime.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
|
||||
ps.PRS.Height = msg.Height
|
||||
ps.PRS.Round = msg.Round
|
||||
ps.PRS.Step = msg.Step
|
||||
@@ -1403,7 +1453,7 @@ func (m *VoteMessage) String() string {
|
||||
type HasVoteMessage struct {
|
||||
Height int64
|
||||
Round int
|
||||
Type byte
|
||||
Type types.SignedMsgType
|
||||
Index int
|
||||
}
|
||||
|
||||
@@ -1418,7 +1468,7 @@ func (m *HasVoteMessage) String() string {
|
||||
type VoteSetMaj23Message struct {
|
||||
Height int64
|
||||
Round int
|
||||
Type byte
|
||||
Type types.SignedMsgType
|
||||
BlockID types.BlockID
|
||||
}
|
||||
|
||||
@@ -1433,7 +1483,7 @@ func (m *VoteSetMaj23Message) String() string {
|
||||
type VoteSetBitsMessage struct {
|
||||
Height int64
|
||||
Round int
|
||||
Type byte
|
||||
Type types.SignedMsgType
|
||||
BlockID types.BlockID
|
||||
Votes *cmn.BitArray
|
||||
}
|
||||
|
||||
@@ -11,23 +11,20 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"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"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
p2pdummy "github.com/tendermint/tendermint/p2p/dummy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -100,6 +97,9 @@ func TestReactorBasic(t *testing.T) {
|
||||
|
||||
// Ensure we can process blocks with evidence
|
||||
func TestReactorWithEvidence(t *testing.T) {
|
||||
types.RegisterMockEvidences(cdc)
|
||||
types.RegisterMockEvidences(types.GetCodec())
|
||||
|
||||
nValidators := 4
|
||||
testName := "consensus_reactor_test"
|
||||
tickerFunc := newMockTickerFunc(true)
|
||||
@@ -115,10 +115,10 @@ func TestReactorWithEvidence(t *testing.T) {
|
||||
for i := 0; i < nValidators; i++ {
|
||||
stateDB := dbm.NewMemDB() // each state needs its own db
|
||||
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
thisConfig := ResetConfig(cmn.Fmt("%s_%d", testName, i))
|
||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
app := appFunc()
|
||||
vals := types.TM2PB.Validators(state.Validators)
|
||||
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
app.InitChain(abci.RequestInitChain{Validators: vals})
|
||||
|
||||
pv := privVals[i]
|
||||
@@ -194,7 +194,8 @@ func newMockEvidencePool(val []byte) *mockEvidencePool {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mockEvidencePool) PendingEvidence() []types.Evidence {
|
||||
// NOTE: maxBytes is ignored
|
||||
func (m *mockEvidencePool) PendingEvidence(maxBytes int64) []types.Evidence {
|
||||
if m.height > 0 {
|
||||
return m.ev
|
||||
}
|
||||
@@ -207,7 +208,7 @@ func (m *mockEvidencePool) Update(block *types.Block, state sm.State) {
|
||||
panic("block has no evidence")
|
||||
}
|
||||
}
|
||||
m.height += 1
|
||||
m.height++
|
||||
}
|
||||
|
||||
//------------------------------------
|
||||
@@ -244,110 +245,25 @@ func TestReactorProposalHeartbeats(t *testing.T) {
|
||||
}, css)
|
||||
}
|
||||
|
||||
// Test we record block parts from other peers
|
||||
func TestReactorRecordsBlockParts(t *testing.T) {
|
||||
// create dummy peer
|
||||
peer := p2pdummy.NewPeer()
|
||||
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
|
||||
peer.Set(types.PeerStateKey, ps)
|
||||
// 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)
|
||||
reactors, eventChans, eventBuses := startConsensusNet(t, css, N)
|
||||
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
||||
|
||||
// create reactor
|
||||
css := randConsensusNet(1, "consensus_reactor_records_block_parts_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
|
||||
reactor.SetEventBus(css[0].eventBus)
|
||||
reactor.SetLogger(log.TestingLogger())
|
||||
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
|
||||
reactor.SetSwitch(sw)
|
||||
err := reactor.Start()
|
||||
require.NoError(t, err)
|
||||
defer reactor.Stop()
|
||||
// wait till everyone makes the first new block
|
||||
timeoutWaitGroup(t, N, func(j int) {
|
||||
<-eventChans[j]
|
||||
}, css)
|
||||
|
||||
// 1) new block part
|
||||
parts := types.NewPartSetFromData(cmn.RandBytes(100), 10)
|
||||
msg := &BlockPartMessage{
|
||||
Height: 2,
|
||||
Round: 0,
|
||||
Part: parts.GetPart(0),
|
||||
}
|
||||
bz, err := cdc.MarshalBinaryBare(msg)
|
||||
require.NoError(t, err)
|
||||
// Get peer
|
||||
peer := reactors[1].Switch.Peers().List()[0]
|
||||
// Get peer state
|
||||
ps := peer.Get(types.PeerStateKey).(*PeerState)
|
||||
|
||||
reactor.Receive(DataChannel, peer, bz)
|
||||
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should have increased by 1")
|
||||
|
||||
// 2) block part with the same height, but different round
|
||||
msg.Round = 1
|
||||
|
||||
bz, err = cdc.MarshalBinaryBare(msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
reactor.Receive(DataChannel, peer, bz)
|
||||
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
|
||||
|
||||
// 3) block part from earlier height
|
||||
msg.Height = 1
|
||||
msg.Round = 0
|
||||
|
||||
bz, err = cdc.MarshalBinaryBare(msg)
|
||||
require.NoError(t, err)
|
||||
|
||||
reactor.Receive(DataChannel, peer, bz)
|
||||
require.Equal(t, 1, ps.BlockPartsSent(), "number of block parts sent should stay the same")
|
||||
}
|
||||
|
||||
// Test we record votes from other peers
|
||||
func TestReactorRecordsVotes(t *testing.T) {
|
||||
// create dummy peer
|
||||
peer := p2pdummy.NewPeer()
|
||||
ps := NewPeerState(peer).SetLogger(log.TestingLogger())
|
||||
peer.Set(types.PeerStateKey, ps)
|
||||
|
||||
// create reactor
|
||||
css := randConsensusNet(1, "consensus_reactor_records_votes_test", newMockTickerFunc(true), newPersistentKVStore)
|
||||
reactor := NewConsensusReactor(css[0], false) // so we dont start the consensus states
|
||||
reactor.SetEventBus(css[0].eventBus)
|
||||
reactor.SetLogger(log.TestingLogger())
|
||||
sw := p2p.MakeSwitch(cfg.DefaultP2PConfig(), 1, "testing", "123.123.123", func(i int, sw *p2p.Switch) *p2p.Switch { return sw })
|
||||
reactor.SetSwitch(sw)
|
||||
err := reactor.Start()
|
||||
require.NoError(t, err)
|
||||
defer reactor.Stop()
|
||||
_, val := css[0].state.Validators.GetByIndex(0)
|
||||
|
||||
// 1) new vote
|
||||
vote := &types.Vote{
|
||||
ValidatorIndex: 0,
|
||||
ValidatorAddress: val.Address,
|
||||
Height: 2,
|
||||
Round: 0,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Type: types.VoteTypePrevote,
|
||||
BlockID: types.BlockID{},
|
||||
}
|
||||
bz, err := cdc.MarshalBinaryBare(&VoteMessage{vote})
|
||||
require.NoError(t, err)
|
||||
|
||||
reactor.Receive(VoteChannel, peer, bz)
|
||||
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should have increased by 1")
|
||||
|
||||
// 2) vote with the same height, but different round
|
||||
vote.Round = 1
|
||||
|
||||
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
|
||||
require.NoError(t, err)
|
||||
|
||||
reactor.Receive(VoteChannel, peer, bz)
|
||||
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should stay the same")
|
||||
|
||||
// 3) vote from earlier height
|
||||
vote.Height = 1
|
||||
vote.Round = 0
|
||||
|
||||
bz, err = cdc.MarshalBinaryBare(&VoteMessage{vote})
|
||||
require.NoError(t, err)
|
||||
|
||||
reactor.Receive(VoteChannel, peer, bz)
|
||||
assert.Equal(t, 1, ps.VotesSent(), "number of votes sent should stay the same")
|
||||
assert.Equal(t, true, ps.VotesSent() > 0, "number of votes sent should have increased")
|
||||
assert.Equal(t, true, ps.BlockPartsSent() > 0, "number of votes sent should have increased")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
@@ -540,7 +456,7 @@ func waitForAndValidateBlock(t *testing.T, n int, activeVals map[string]struct{}
|
||||
err := validateBlock(newBlock, activeVals)
|
||||
assert.Nil(t, err)
|
||||
for _, tx := range txs {
|
||||
css[j].mempool.CheckTx(tx, nil)
|
||||
err := css[j].mempool.CheckTx(tx, nil)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
}, css)
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"time"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
//auto "github.com/tendermint/tendermint/libs/autofile"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
@@ -19,7 +20,6 @@ import (
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
"github.com/tendermint/tendermint/version"
|
||||
)
|
||||
|
||||
var crc32c = crc32.MakeTable(crc32.Castagnoli)
|
||||
@@ -227,7 +227,7 @@ func (h *Handshaker) NBlocks() int {
|
||||
func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
||||
|
||||
// Handshake is done via ABCI Info on the query conn.
|
||||
res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: version.Version})
|
||||
res, err := proxyApp.Query().InfoSync(proxy.RequestInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error calling Info: %v", err)
|
||||
}
|
||||
@@ -238,9 +238,15 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
||||
}
|
||||
appHash := res.LastBlockAppHash
|
||||
|
||||
h.logger.Info("ABCI Handshake", "appHeight", blockHeight, "appHash", fmt.Sprintf("%X", appHash))
|
||||
h.logger.Info("ABCI Handshake App Info",
|
||||
"height", blockHeight,
|
||||
"hash", fmt.Sprintf("%X", appHash),
|
||||
"software-version", res.Version,
|
||||
"protocol-version", res.AppVersion,
|
||||
)
|
||||
|
||||
// TODO: check app version.
|
||||
// Set AppVersion on the state.
|
||||
h.initialState.Version.Consensus.App = version.Protocol(res.AppVersion)
|
||||
|
||||
// Replay blocks up to the latest in the blockstore.
|
||||
_, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp)
|
||||
@@ -264,15 +270,15 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
stateBlockHeight := state.LastBlockHeight
|
||||
h.logger.Info("ABCI Replay Blocks", "appHeight", appBlockHeight, "storeHeight", storeBlockHeight, "stateHeight", stateBlockHeight)
|
||||
|
||||
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain
|
||||
// If appBlockHeight == 0 it means that we are at genesis and hence should send InitChain.
|
||||
if appBlockHeight == 0 {
|
||||
validators := types.TM2PB.Validators(state.Validators)
|
||||
nextVals := types.TM2PB.ValidatorUpdates(state.NextValidators) // state.Validators would work too.
|
||||
csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams)
|
||||
req := abci.RequestInitChain{
|
||||
Time: h.genDoc.GenesisTime,
|
||||
ChainId: h.genDoc.ChainID,
|
||||
ConsensusParams: csParams,
|
||||
Validators: validators,
|
||||
Validators: nextVals,
|
||||
AppStateBytes: h.genDoc.AppState,
|
||||
}
|
||||
res, err := proxyApp.Consensus().InitChainSync(req)
|
||||
@@ -280,15 +286,14 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// if the app returned validators
|
||||
// or consensus params, update the state
|
||||
// with the them
|
||||
// If the app returned validators or consensus params, update the state.
|
||||
if len(res.Validators) > 0 {
|
||||
vals, err := types.PB2TM.Validators(res.Validators)
|
||||
vals, err := types.PB2TM.ValidatorUpdates(res.Validators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Validators = types.NewValidatorSet(vals)
|
||||
state.NextValidators = types.NewValidatorSet(vals)
|
||||
}
|
||||
if res.ConsensusParams != nil {
|
||||
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
|
||||
@@ -296,7 +301,7 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
sm.SaveState(h.stateDB, state)
|
||||
}
|
||||
|
||||
// First handle edge cases and constraints on the storeBlockHeight
|
||||
// First handle edge cases and constraints on the storeBlockHeight.
|
||||
if storeBlockHeight == 0 {
|
||||
return appHash, checkAppHash(state, appHash)
|
||||
|
||||
@@ -306,11 +311,11 @@ func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight
|
||||
|
||||
} else if storeBlockHeight < stateBlockHeight {
|
||||
// the state should never be ahead of the store (this is under tendermint's control)
|
||||
cmn.PanicSanity(cmn.Fmt("StateBlockHeight (%d) > StoreBlockHeight (%d)", stateBlockHeight, storeBlockHeight))
|
||||
cmn.PanicSanity(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(cmn.Fmt("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1))
|
||||
cmn.PanicSanity(fmt.Sprintf("StoreBlockHeight (%d) > StateBlockHeight + 1 (%d)", storeBlockHeight, stateBlockHeight+1))
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
@@ -13,12 +13,12 @@ import (
|
||||
|
||||
bc "github.com/tendermint/tendermint/blockchain"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +34,7 @@ func RunReplayFile(config cfg.BaseConfig, csConfig *cfg.ConsensusConfig, console
|
||||
consensusState := newConsensusStateForReplay(config, csConfig)
|
||||
|
||||
if err := consensusState.ReplayFile(csConfig.WalFile(), console); err != nil {
|
||||
cmn.Exit(cmn.Fmt("Error during consensus replay: %v", err))
|
||||
cmn.Exit(fmt.Sprintf("Error during consensus replay: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,16 +298,21 @@ func newConsensusStateForReplay(config cfg.BaseConfig, csConfig *cfg.ConsensusCo
|
||||
|
||||
// Create proxyAppConn connection (consensus, mempool, query)
|
||||
clientCreator := proxy.DefaultClientCreator(config.ProxyApp, config.ABCI, config.DBDir())
|
||||
proxyApp := proxy.NewAppConns(clientCreator,
|
||||
NewHandshaker(stateDB, state, blockStore, gdoc))
|
||||
proxyApp := proxy.NewAppConns(clientCreator)
|
||||
err = proxyApp.Start()
|
||||
if err != nil {
|
||||
cmn.Exit(cmn.Fmt("Error starting proxy app conns: %v", err))
|
||||
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(cmn.Fmt("Failed to start event bus: %v", err))
|
||||
cmn.Exit(fmt.Sprintf("Failed to start event bus: %v", err))
|
||||
}
|
||||
|
||||
mempool, evpool := sm.MockMempool{}, sm.MockEvidencePool{}
|
||||
|
||||
@@ -3,7 +3,6 @@ package consensus
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -20,15 +19,15 @@ import (
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/tendermint/crypto"
|
||||
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/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/libs/log"
|
||||
)
|
||||
|
||||
var consensusReplayConfig *cfg.Config
|
||||
@@ -104,14 +103,6 @@ func TestWALCrash(t *testing.T) {
|
||||
{"empty block",
|
||||
func(stateDB dbm.DB, cs *ConsensusState, ctx context.Context) {},
|
||||
1},
|
||||
{"block with a smaller part size",
|
||||
func(stateDB dbm.DB, cs *ConsensusState, ctx context.Context) {
|
||||
// XXX: is there a better way to change BlockPartSizeBytes?
|
||||
cs.state.ConsensusParams.BlockPartSizeBytes = 512
|
||||
sm.SaveState(stateDB, cs.state)
|
||||
go sendTxs(cs, ctx)
|
||||
},
|
||||
1},
|
||||
{"many non-empty blocks",
|
||||
func(stateDB dbm.DB, cs *ConsensusState, ctx context.Context) {
|
||||
go sendTxs(cs, ctx)
|
||||
@@ -347,7 +338,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey())
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
|
||||
store.chain = chain
|
||||
store.commits = commits
|
||||
|
||||
@@ -361,19 +352,22 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
if nBlocks > 0 {
|
||||
// run nBlocks against a new client to build up the app state.
|
||||
// use a throwaway tendermint state
|
||||
proxyApp := proxy.NewAppConns(clientCreator2, nil)
|
||||
stateDB, state, _ := stateAndStore(config, privVal.GetPubKey())
|
||||
proxyApp := proxy.NewAppConns(clientCreator2)
|
||||
stateDB, state, _ := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
|
||||
buildAppStateFromChain(proxyApp, stateDB, state, 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, handshaker)
|
||||
proxyApp := proxy.NewAppConns(clientCreator2)
|
||||
if err := proxyApp.Start(); err != nil {
|
||||
t.Fatalf("Error starting proxy app connections: %v", err)
|
||||
}
|
||||
defer proxyApp.Stop()
|
||||
if err := handshaker.Handshake(proxyApp); err != nil {
|
||||
t.Fatalf("Error on abci handshake: %v", err)
|
||||
}
|
||||
|
||||
// get the latest app hash from the app
|
||||
res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: ""})
|
||||
@@ -399,7 +393,7 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
}
|
||||
|
||||
func applyBlock(stateDB dbm.DB, st sm.State, blk *types.Block, proxyApp proxy.AppConns) sm.State {
|
||||
testPartSize := st.ConsensusParams.BlockPartSizeBytes
|
||||
testPartSize := types.BlockPartSizeBytes
|
||||
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyApp.Consensus(), mempool, evpool)
|
||||
|
||||
blkID := types.BlockID{blk.Hash(), blk.MakePartSet(testPartSize).Header()}
|
||||
@@ -418,7 +412,7 @@ func buildAppStateFromChain(proxyApp proxy.AppConns, stateDB dbm.DB,
|
||||
}
|
||||
defer proxyApp.Stop()
|
||||
|
||||
validators := types.TM2PB.Validators(state.Validators)
|
||||
validators := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
|
||||
Validators: validators,
|
||||
}); err != nil {
|
||||
@@ -449,13 +443,13 @@ 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, nil) // sm.NewHandshaker(config, state, store, ReplayLastBlock))
|
||||
proxyApp := proxy.NewAppConns(clientCreator)
|
||||
if err := proxyApp.Start(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer proxyApp.Stop()
|
||||
|
||||
validators := types.TM2PB.Validators(state.Validators)
|
||||
validators := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
if _, err := proxyApp.Consensus().InitChainSync(abci.RequestInitChain{
|
||||
Validators: validators,
|
||||
}); err != nil {
|
||||
@@ -494,7 +488,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !found {
|
||||
return nil, nil, errors.New(cmn.Fmt("WAL does not contain height %d.", 1))
|
||||
return nil, nil, fmt.Errorf("WAL does not contain height %d.", 1)
|
||||
}
|
||||
defer gr.Close() // nolint: errcheck
|
||||
|
||||
@@ -526,16 +520,16 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
||||
// if its not the first one, we have a full block
|
||||
if thisBlockParts != nil {
|
||||
var block = new(types.Block)
|
||||
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
|
||||
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(thisBlockParts.GetReader(), block, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if block.Height != height+1 {
|
||||
panic(cmn.Fmt("read bad block from wal. got height %d, expected %d", block.Height, height+1))
|
||||
panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1))
|
||||
}
|
||||
commitHeight := thisBlockCommit.Precommits[0].Height
|
||||
if commitHeight != height+1 {
|
||||
panic(cmn.Fmt("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
|
||||
panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
commits = append(commits, thisBlockCommit)
|
||||
@@ -549,7 +543,7 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
case *types.Vote:
|
||||
if p.Type == types.VoteTypePrecommit {
|
||||
if p.Type == types.PrecommitType {
|
||||
thisBlockCommit = &types.Commit{
|
||||
BlockID: p.BlockID,
|
||||
Precommits: []*types.Vote{p},
|
||||
@@ -559,16 +553,16 @@ func makeBlockchainFromWAL(wal WAL) ([]*types.Block, []*types.Commit, error) {
|
||||
}
|
||||
// grab the last block too
|
||||
var block = new(types.Block)
|
||||
_, err = cdc.UnmarshalBinaryReader(thisBlockParts.GetReader(), block, 0)
|
||||
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(thisBlockParts.GetReader(), block, 0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if block.Height != height+1 {
|
||||
panic(cmn.Fmt("read bad block from wal. got height %d, expected %d", block.Height, height+1))
|
||||
panic(fmt.Sprintf("read bad block from wal. got height %d, expected %d", block.Height, height+1))
|
||||
}
|
||||
commitHeight := thisBlockCommit.Precommits[0].Height
|
||||
if commitHeight != height+1 {
|
||||
panic(cmn.Fmt("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
|
||||
panic(fmt.Sprintf("commit doesnt match. got height %d, expected %d", commitHeight, height+1))
|
||||
}
|
||||
blocks = append(blocks, block)
|
||||
commits = append(commits, thisBlockCommit)
|
||||
@@ -595,9 +589,10 @@ func readPieceFromWAL(msg *TimedWALMessage) interface{} {
|
||||
}
|
||||
|
||||
// fresh state and mock store
|
||||
func stateAndStore(config *cfg.Config, pubKey crypto.PubKey) (dbm.DB, sm.State, *mockBlockStore) {
|
||||
func stateAndStore(config *cfg.Config, pubKey crypto.PubKey, appVersion version.Protocol) (dbm.DB, sm.State, *mockBlockStore) {
|
||||
stateDB := dbm.NewMemDB()
|
||||
state, _ := sm.MakeGenesisStateFromFile(config.GenesisFile())
|
||||
state.Version.Consensus.App = appVersion
|
||||
store := NewMockBlockStore(config, state.ConsensusParams)
|
||||
return stateDB, state, store
|
||||
}
|
||||
@@ -622,7 +617,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(bs.params.BlockPartSizeBytes).Header()},
|
||||
BlockID: types.BlockID{block.Hash(), block.MakePartSet(types.BlockPartSizeBytes).Header()},
|
||||
Header: block.Header,
|
||||
}
|
||||
}
|
||||
@@ -641,23 +636,26 @@ func (bs *mockBlockStore) LoadSeenCommit(height int64) *types.Commit {
|
||||
func TestInitChainUpdateValidators(t *testing.T) {
|
||||
val, _ := types.RandValidator(true, 10)
|
||||
vals := types.NewValidatorSet([]*types.Validator{val})
|
||||
app := &initChainApp{vals: types.TM2PB.Validators(vals)}
|
||||
app := &initChainApp{vals: types.TM2PB.ValidatorUpdates(vals)}
|
||||
clientCreator := proxy.NewLocalClientCreator(app)
|
||||
|
||||
config := ResetConfig("proxy_test_")
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey())
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), 0x0)
|
||||
|
||||
oldValAddr := state.Validators.Validators[0].Address
|
||||
|
||||
// now start the app using the handshake - it should sync
|
||||
genDoc, _ := sm.MakeGenesisDocFromFile(config.GenesisFile())
|
||||
handshaker := NewHandshaker(stateDB, state, store, genDoc)
|
||||
proxyApp := proxy.NewAppConns(clientCreator, handshaker)
|
||||
proxyApp := proxy.NewAppConns(clientCreator)
|
||||
if err := proxyApp.Start(); err != nil {
|
||||
t.Fatalf("Error starting proxy app connections: %v", err)
|
||||
}
|
||||
defer proxyApp.Stop()
|
||||
if err := handshaker.Handshake(proxyApp); err != nil {
|
||||
t.Fatalf("Error on abci handshake: %v", err)
|
||||
}
|
||||
|
||||
// reload the state, check the validator set was updated
|
||||
state = sm.LoadState(stateDB)
|
||||
@@ -668,7 +666,7 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
||||
assert.Equal(t, newValAddr, expectValAddr)
|
||||
}
|
||||
|
||||
func newInitChainApp(vals []abci.Validator) *initChainApp {
|
||||
func newInitChainApp(vals []abci.ValidatorUpdate) *initChainApp {
|
||||
return &initChainApp{
|
||||
vals: vals,
|
||||
}
|
||||
@@ -677,7 +675,7 @@ func newInitChainApp(vals []abci.Validator) *initChainApp {
|
||||
// returns the vals on InitChain
|
||||
type initChainApp struct {
|
||||
abci.BaseApplication
|
||||
vals []abci.Validator
|
||||
vals []abci.ValidatorUpdate
|
||||
}
|
||||
|
||||
func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
fail "github.com/ebuchman/fail-test"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||
@@ -74,7 +75,6 @@ type ConsensusState struct {
|
||||
privValidator types.PrivValidator // for signing votes
|
||||
|
||||
// services for creating and executing blocks
|
||||
// TODO: encapsulate all of this in one "BlockManager"
|
||||
blockExec *sm.BlockExecutor
|
||||
blockStore sm.BlockStore
|
||||
mempool sm.Mempool
|
||||
@@ -83,7 +83,8 @@ type ConsensusState struct {
|
||||
// internal state
|
||||
mtx sync.RWMutex
|
||||
cstypes.RoundState
|
||||
state sm.State // State until height-1.
|
||||
triggeredTimeoutPrecommit bool
|
||||
state sm.State // State until height-1.
|
||||
|
||||
// state changes may be triggered by: msgs from peers,
|
||||
// msgs from ourself, or by timeouts
|
||||
@@ -91,6 +92,10 @@ type ConsensusState struct {
|
||||
internalMsgQueue chan msgInfo
|
||||
timeoutTicker TimeoutTicker
|
||||
|
||||
// information about about added votes and block parts are written on this channel
|
||||
// so statistics can be computed by reactor
|
||||
statsMsgQueue chan msgInfo
|
||||
|
||||
// we use eventBus to trigger msg broadcasts in the reactor,
|
||||
// and to notify external subscribers, eg. through a websocket
|
||||
eventBus *types.EventBus
|
||||
@@ -120,8 +125,8 @@ type ConsensusState struct {
|
||||
metrics *Metrics
|
||||
}
|
||||
|
||||
// CSOption sets an optional parameter on the ConsensusState.
|
||||
type CSOption func(*ConsensusState)
|
||||
// StateOption sets an optional parameter on the ConsensusState.
|
||||
type StateOption func(*ConsensusState)
|
||||
|
||||
// NewConsensusState returns a new ConsensusState.
|
||||
func NewConsensusState(
|
||||
@@ -131,7 +136,7 @@ func NewConsensusState(
|
||||
blockStore sm.BlockStore,
|
||||
mempool sm.Mempool,
|
||||
evpool sm.EvidencePool,
|
||||
options ...CSOption,
|
||||
options ...StateOption,
|
||||
) *ConsensusState {
|
||||
cs := &ConsensusState{
|
||||
config: config,
|
||||
@@ -141,6 +146,7 @@ func NewConsensusState(
|
||||
peerMsgQueue: make(chan msgInfo, msgQueueSize),
|
||||
internalMsgQueue: make(chan msgInfo, msgQueueSize),
|
||||
timeoutTicker: NewTimeoutTicker(),
|
||||
statsMsgQueue: make(chan msgInfo, msgQueueSize),
|
||||
done: make(chan struct{}),
|
||||
doWALCatchup: true,
|
||||
wal: nilWAL{},
|
||||
@@ -154,6 +160,7 @@ func NewConsensusState(
|
||||
cs.setProposal = cs.defaultSetProposal
|
||||
|
||||
cs.updateToState(state)
|
||||
|
||||
// Don't call scheduleRound0 yet.
|
||||
// We do that upon Start().
|
||||
cs.reconstructLastCommit(state)
|
||||
@@ -179,15 +186,15 @@ func (cs *ConsensusState) SetEventBus(b *types.EventBus) {
|
||||
cs.blockExec.SetEventBus(b)
|
||||
}
|
||||
|
||||
// WithMetrics sets the metrics.
|
||||
func WithMetrics(metrics *Metrics) CSOption {
|
||||
// StateMetrics sets the metrics.
|
||||
func StateMetrics(metrics *Metrics) StateOption {
|
||||
return func(cs *ConsensusState) { cs.metrics = metrics }
|
||||
}
|
||||
|
||||
// String returns a string.
|
||||
func (cs *ConsensusState) String() string {
|
||||
// better not to access shared variables
|
||||
return cmn.Fmt("ConsensusState") //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
|
||||
return fmt.Sprintf("ConsensusState") //(H:%v R:%v S:%v", cs.Height, cs.Round, cs.Step)
|
||||
}
|
||||
|
||||
// GetState returns a copy of the chain state.
|
||||
@@ -197,6 +204,15 @@ func (cs *ConsensusState) GetState() sm.State {
|
||||
return cs.state.Copy()
|
||||
}
|
||||
|
||||
// GetLastHeight returns the last height committed.
|
||||
// If there were no blocks, returns 0.
|
||||
func (cs *ConsensusState) GetLastHeight() int64 {
|
||||
cs.mtx.Lock()
|
||||
defer cs.mtx.Unlock()
|
||||
|
||||
return cs.RoundState.Height - 1
|
||||
}
|
||||
|
||||
// GetRoundState returns a shallow copy of the internal consensus state.
|
||||
func (cs *ConsensusState) GetRoundState() *cstypes.RoundState {
|
||||
cs.mtx.RLock()
|
||||
@@ -413,8 +429,8 @@ 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", time.Now(), "startTime", cs.StartTime)
|
||||
sleepDuration := rs.StartTime.Sub(time.Now()) // nolint: gotype, gosimple
|
||||
//cs.Logger.Info("scheduleRound0", "now", tmtime.Now(), "startTime", cs.StartTime)
|
||||
sleepDuration := rs.StartTime.Sub(tmtime.Now()) // nolint: gotype, gosimple
|
||||
cs.scheduleTimeout(sleepDuration, rs.Height, 0, cstypes.RoundStepNewHeight)
|
||||
}
|
||||
|
||||
@@ -444,14 +460,14 @@ func (cs *ConsensusState) reconstructLastCommit(state sm.State) {
|
||||
return
|
||||
}
|
||||
seenCommit := cs.blockStore.LoadSeenCommit(state.LastBlockHeight)
|
||||
lastPrecommits := types.NewVoteSet(state.ChainID, state.LastBlockHeight, seenCommit.Round(), types.VoteTypePrecommit, state.LastValidators)
|
||||
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(precommit)
|
||||
if !added || err != nil {
|
||||
cmn.PanicCrisis(cmn.Fmt("Failed to reconstruct LastCommit: %v", err))
|
||||
cmn.PanicCrisis(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
|
||||
}
|
||||
}
|
||||
if !lastPrecommits.HasTwoThirdsMajority() {
|
||||
@@ -464,13 +480,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(cmn.Fmt("updateToState() expected state height of %v but found %v",
|
||||
cmn.PanicSanity(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(cmn.Fmt("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
|
||||
cmn.PanicSanity(fmt.Sprintf("Inconsistent cs.state.LastBlockHeight+1 %v vs cs.Height %v",
|
||||
cs.state.LastBlockHeight+1, cs.Height))
|
||||
}
|
||||
|
||||
@@ -507,7 +523,7 @@ func (cs *ConsensusState) updateToState(state sm.State) {
|
||||
// to be gathered for the first block.
|
||||
// And alternative solution that relies on clocks:
|
||||
// cs.StartTime = state.LastBlockTime.Add(timeoutCommit)
|
||||
cs.StartTime = cs.config.Commit(time.Now())
|
||||
cs.StartTime = cs.config.Commit(tmtime.Now())
|
||||
} else {
|
||||
cs.StartTime = cs.config.Commit(cs.CommitTime)
|
||||
}
|
||||
@@ -516,10 +532,10 @@ func (cs *ConsensusState) updateToState(state sm.State) {
|
||||
cs.Proposal = nil
|
||||
cs.ProposalBlock = nil
|
||||
cs.ProposalBlockParts = nil
|
||||
cs.LockedRound = 0
|
||||
cs.LockedRound = -1
|
||||
cs.LockedBlock = nil
|
||||
cs.LockedBlockParts = nil
|
||||
cs.ValidRound = 0
|
||||
cs.ValidRound = -1
|
||||
cs.ValidBlock = nil
|
||||
cs.ValidBlockParts = nil
|
||||
cs.Votes = cstypes.NewHeightVoteSet(state.ChainID, height, validators)
|
||||
@@ -629,7 +645,11 @@ func (cs *ConsensusState) handleMsg(mi msgInfo) {
|
||||
err = cs.setProposal(msg.Proposal)
|
||||
case *BlockPartMessage:
|
||||
// if the proposal is complete, we'll enterPrevote or tryFinalizeCommit
|
||||
_, err = cs.addProposalBlockPart(msg, peerID)
|
||||
added, err := cs.addProposalBlockPart(msg, peerID)
|
||||
if added {
|
||||
cs.statsMsgQueue <- mi
|
||||
}
|
||||
|
||||
if err != nil && msg.Round != cs.Round {
|
||||
cs.Logger.Debug("Received block part from wrong round", "height", cs.Height, "csRound", cs.Round, "blockRound", msg.Round)
|
||||
err = nil
|
||||
@@ -637,7 +657,11 @@ 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
|
||||
err := cs.tryAddVote(msg.Vote, peerID)
|
||||
added, err := cs.tryAddVote(msg.Vote, peerID)
|
||||
if added {
|
||||
cs.statsMsgQueue <- mi
|
||||
}
|
||||
|
||||
if err == ErrAddingVote {
|
||||
// TODO: punish peer
|
||||
// We probably don't want to stop the peer here. The vote does not
|
||||
@@ -688,9 +712,10 @@ func (cs *ConsensusState) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) {
|
||||
cs.enterPrecommit(ti.Height, ti.Round)
|
||||
case cstypes.RoundStepPrecommitWait:
|
||||
cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent())
|
||||
cs.enterPrecommit(ti.Height, ti.Round)
|
||||
cs.enterNewRound(ti.Height, ti.Round+1)
|
||||
default:
|
||||
panic(cmn.Fmt("Invalid timeout step: %v", ti.Step))
|
||||
panic(fmt.Sprintf("Invalid timeout step: %v", ti.Step))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -716,15 +741,15 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) {
|
||||
logger.Debug(cmn.Fmt("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Debug(fmt.Sprintf("enterNewRound(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
|
||||
if now := time.Now(); cs.StartTime.After(now) {
|
||||
if now := tmtime.Now(); cs.StartTime.After(now) {
|
||||
logger.Info("Need to set a buffer and log message here for sanity.", "startTime", cs.StartTime, "now", now)
|
||||
}
|
||||
|
||||
logger.Info(cmn.Fmt("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Info(fmt.Sprintf("enterNewRound(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
// Increment validators if necessary
|
||||
validators := cs.Validators
|
||||
@@ -749,6 +774,7 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
|
||||
cs.ProposalBlockParts = nil
|
||||
}
|
||||
cs.Votes.SetRound(round + 1) // also track next round (round+1) to allow round-skipping
|
||||
cs.triggeredTimeoutPrecommit = false
|
||||
|
||||
cs.eventBus.PublishEventNewRound(cs.RoundStateEvent())
|
||||
cs.metrics.Rounds.Set(float64(round))
|
||||
@@ -759,7 +785,8 @@ func (cs *ConsensusState) enterNewRound(height int64, round int) {
|
||||
waitForTxs := cs.config.WaitForTxs() && round == 0 && !cs.needProofBlock(height)
|
||||
if waitForTxs {
|
||||
if cs.config.CreateEmptyBlocksInterval > 0 {
|
||||
cs.scheduleTimeout(cs.config.EmptyBlocksInterval(), height, round, cstypes.RoundStepNewRound)
|
||||
cs.scheduleTimeout(cs.config.CreateEmptyBlocksInterval, height, round,
|
||||
cstypes.RoundStepNewRound)
|
||||
}
|
||||
go cs.proposalHeartbeat(height, round)
|
||||
} else {
|
||||
@@ -811,10 +838,10 @@ func (cs *ConsensusState) enterPropose(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPropose <= cs.Step) {
|
||||
logger.Debug(cmn.Fmt("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Debug(fmt.Sprintf("enterPropose(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
logger.Info(cmn.Fmt("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Info(fmt.Sprintf("enterPropose(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
defer func() {
|
||||
// Done enterPropose:
|
||||
@@ -862,10 +889,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
|
||||
var blockParts *types.PartSet
|
||||
|
||||
// Decide on block
|
||||
if cs.LockedBlock != nil {
|
||||
// If we're locked onto a block, just choose that.
|
||||
block, blockParts = cs.LockedBlock, cs.LockedBlockParts
|
||||
} else if cs.ValidBlock != nil {
|
||||
if cs.ValidBlock != nil {
|
||||
// If there is valid block, choose that.
|
||||
block, blockParts = cs.ValidBlock, cs.ValidBlockParts
|
||||
} else {
|
||||
@@ -894,7 +918,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
|
||||
cs.sendInternalMessage(msgInfo{&BlockPartMessage{cs.Height, cs.Round, part}, ""})
|
||||
}
|
||||
cs.Logger.Info("Signed proposal", "height", height, "round", round, "proposal", proposal)
|
||||
cs.Logger.Debug(cmn.Fmt("Signed proposal block: %v", block))
|
||||
cs.Logger.Debug(fmt.Sprintf("Signed proposal block: %v", block))
|
||||
} else {
|
||||
if !cs.replayMode {
|
||||
cs.Logger.Error("enterPropose: Error signing proposal", "height", height, "round", round, "err", err)
|
||||
@@ -938,21 +962,29 @@ func (cs *ConsensusState) createProposalBlock() (block *types.Block, blockParts
|
||||
return
|
||||
}
|
||||
|
||||
maxBytes := cs.state.ConsensusParams.BlockSize.MaxBytes
|
||||
maxGas := cs.state.ConsensusParams.BlockSize.MaxGas
|
||||
// bound evidence to 1/10th of the block
|
||||
evidence := cs.evpool.PendingEvidence(types.MaxEvidenceBytesPerBlock(maxBytes))
|
||||
// Mempool validated transactions
|
||||
txs := cs.mempool.Reap(cs.state.ConsensusParams.BlockSize.MaxTxs)
|
||||
evidence := cs.evpool.PendingEvidence()
|
||||
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence)
|
||||
txs := cs.mempool.ReapMaxBytesMaxGas(types.MaxDataBytes(
|
||||
maxBytes,
|
||||
cs.state.Validators.Size(),
|
||||
len(evidence),
|
||||
), maxGas)
|
||||
proposerAddr := cs.privValidator.GetAddress()
|
||||
block, parts := cs.state.MakeBlock(cs.Height, txs, commit, evidence, proposerAddr)
|
||||
|
||||
return block, parts
|
||||
}
|
||||
|
||||
// Enter: `timeoutPropose` after entering Propose.
|
||||
// Enter: proposal block and POL is ready.
|
||||
// Enter: any +2/3 prevotes for future round.
|
||||
// Prevote for LockedBlock if we're locked, or ProposalBlock if valid.
|
||||
// Otherwise vote nil.
|
||||
func (cs *ConsensusState) enterPrevote(height int64, round int) {
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevote <= cs.Step) {
|
||||
cs.Logger.Debug(cmn.Fmt("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
cs.Logger.Debug(fmt.Sprintf("enterPrevote(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -970,7 +1002,7 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
|
||||
// TODO: catchup event?
|
||||
}
|
||||
|
||||
cs.Logger.Info(cmn.Fmt("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
cs.Logger.Info(fmt.Sprintf("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
// Sign and broadcast vote as necessary
|
||||
cs.doPrevote(height, round)
|
||||
@@ -981,17 +1013,18 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
|
||||
|
||||
func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
|
||||
// If a block is locked, prevote that.
|
||||
if cs.LockedBlock != nil {
|
||||
logger.Info("enterPrevote: Block was locked")
|
||||
cs.signAddVote(types.VoteTypePrevote, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header())
|
||||
cs.signAddVote(types.PrevoteType, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header())
|
||||
return
|
||||
}
|
||||
|
||||
// If ProposalBlock is nil, prevote nil.
|
||||
if cs.ProposalBlock == nil {
|
||||
logger.Info("enterPrevote: ProposalBlock is nil")
|
||||
cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{})
|
||||
cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1000,7 +1033,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
|
||||
if err != nil {
|
||||
// ProposalBlock is invalid, prevote nil.
|
||||
logger.Error("enterPrevote: ProposalBlock is invalid", "err", err)
|
||||
cs.signAddVote(types.VoteTypePrevote, nil, types.PartSetHeader{})
|
||||
cs.signAddVote(types.PrevoteType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1008,7 +1041,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
|
||||
// NOTE: the proposal signature is validated when it is received,
|
||||
// and the proposal block parts are validated as they are received (against the merkle hash in the proposal)
|
||||
logger.Info("enterPrevote: ProposalBlock is valid")
|
||||
cs.signAddVote(types.VoteTypePrevote, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
|
||||
cs.signAddVote(types.PrevoteType, cs.ProposalBlock.Hash(), cs.ProposalBlockParts.Header())
|
||||
}
|
||||
|
||||
// Enter: any +2/3 prevotes at next round.
|
||||
@@ -1016,13 +1049,13 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) {
|
||||
logger.Debug(cmn.Fmt("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Debug(fmt.Sprintf("enterPrevoteWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
if !cs.Votes.Prevotes(round).HasTwoThirdsAny() {
|
||||
cmn.PanicSanity(cmn.Fmt("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round))
|
||||
cmn.PanicSanity(fmt.Sprintf("enterPrevoteWait(%v/%v), but Prevotes does not have any +2/3 votes", height, round))
|
||||
}
|
||||
logger.Info(cmn.Fmt("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Info(fmt.Sprintf("enterPrevoteWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
defer func() {
|
||||
// Done enterPrevoteWait:
|
||||
@@ -1035,8 +1068,8 @@ func (cs *ConsensusState) enterPrevoteWait(height int64, round int) {
|
||||
}
|
||||
|
||||
// Enter: `timeoutPrevote` after any +2/3 prevotes.
|
||||
// Enter: `timeoutPrecommit` after any +2/3 precommits.
|
||||
// Enter: +2/3 precomits for block or nil.
|
||||
// Enter: any +2/3 precommits for next round.
|
||||
// Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round)
|
||||
// else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil,
|
||||
// else, precommit nil otherwise.
|
||||
@@ -1044,11 +1077,11 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommit <= cs.Step) {
|
||||
logger.Debug(cmn.Fmt("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Debug(fmt.Sprintf("enterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info(cmn.Fmt("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Info(fmt.Sprintf("enterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
defer func() {
|
||||
// Done enterPrecommit:
|
||||
@@ -1066,7 +1099,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
|
||||
} else {
|
||||
logger.Info("enterPrecommit: No +2/3 prevotes during enterPrecommit. Precommitting nil.")
|
||||
}
|
||||
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{})
|
||||
cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1076,7 +1109,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(cmn.Fmt("This POLRound should be %v but got %", round, polRound))
|
||||
cmn.PanicSanity(fmt.Sprintf("This POLRound should be %v but got %v", round, polRound))
|
||||
}
|
||||
|
||||
// +2/3 prevoted nil. Unlock and precommit nil.
|
||||
@@ -1085,12 +1118,12 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
|
||||
logger.Info("enterPrecommit: +2/3 prevoted for nil.")
|
||||
} else {
|
||||
logger.Info("enterPrecommit: +2/3 prevoted for nil. Unlocking")
|
||||
cs.LockedRound = 0
|
||||
cs.LockedRound = -1
|
||||
cs.LockedBlock = nil
|
||||
cs.LockedBlockParts = nil
|
||||
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
|
||||
}
|
||||
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{})
|
||||
cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1101,7 +1134,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
|
||||
logger.Info("enterPrecommit: +2/3 prevoted locked block. Relocking")
|
||||
cs.LockedRound = round
|
||||
cs.eventBus.PublishEventRelock(cs.RoundStateEvent())
|
||||
cs.signAddVote(types.VoteTypePrecommit, blockID.Hash, blockID.PartsHeader)
|
||||
cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1110,13 +1143,13 @@ 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(cmn.Fmt("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
|
||||
cmn.PanicConsensus(fmt.Sprintf("enterPrecommit: +2/3 prevoted for an invalid block: %v", err))
|
||||
}
|
||||
cs.LockedRound = round
|
||||
cs.LockedBlock = cs.ProposalBlock
|
||||
cs.LockedBlockParts = cs.ProposalBlockParts
|
||||
cs.eventBus.PublishEventLock(cs.RoundStateEvent())
|
||||
cs.signAddVote(types.VoteTypePrecommit, blockID.Hash, blockID.PartsHeader)
|
||||
cs.signAddVote(types.PrecommitType, blockID.Hash, blockID.PartsHeader)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1124,7 +1157,7 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
|
||||
// Fetch that block, unlock, and precommit nil.
|
||||
// The +2/3 prevotes for this round is the POL for our unlock.
|
||||
// TODO: In the future save the POL prevotes for justification.
|
||||
cs.LockedRound = 0
|
||||
cs.LockedRound = -1
|
||||
cs.LockedBlock = nil
|
||||
cs.LockedBlockParts = nil
|
||||
if !cs.ProposalBlockParts.HasHeader(blockID.PartsHeader) {
|
||||
@@ -1132,25 +1165,29 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
|
||||
cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartsHeader)
|
||||
}
|
||||
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
|
||||
cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{})
|
||||
cs.signAddVote(types.PrecommitType, nil, types.PartSetHeader{})
|
||||
}
|
||||
|
||||
// Enter: any +2/3 precommits for next round.
|
||||
func (cs *ConsensusState) enterPrecommitWait(height int64, round int) {
|
||||
logger := cs.Logger.With("height", height, "round", round)
|
||||
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommitWait <= cs.Step) {
|
||||
logger.Debug(cmn.Fmt("enterPrecommitWait(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.triggeredTimeoutPrecommit) {
|
||||
logger.Debug(
|
||||
fmt.Sprintf(
|
||||
"enterPrecommitWait(%v/%v): Invalid args. "+
|
||||
"Current state is Height/Round: %v/%v/, triggeredTimeoutPrecommit:%v",
|
||||
height, round, cs.Height, cs.Round, cs.triggeredTimeoutPrecommit))
|
||||
return
|
||||
}
|
||||
if !cs.Votes.Precommits(round).HasTwoThirdsAny() {
|
||||
cmn.PanicSanity(cmn.Fmt("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round))
|
||||
cmn.PanicSanity(fmt.Sprintf("enterPrecommitWait(%v/%v), but Precommits does not have any +2/3 votes", height, round))
|
||||
}
|
||||
logger.Info(cmn.Fmt("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
logger.Info(fmt.Sprintf("enterPrecommitWait(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
defer func() {
|
||||
// Done enterPrecommitWait:
|
||||
cs.updateRoundStep(round, cstypes.RoundStepPrecommitWait)
|
||||
cs.triggeredTimeoutPrecommit = true
|
||||
cs.newStep()
|
||||
}()
|
||||
|
||||
@@ -1164,17 +1201,17 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
|
||||
logger := cs.Logger.With("height", height, "commitRound", commitRound)
|
||||
|
||||
if cs.Height != height || cstypes.RoundStepCommit <= cs.Step {
|
||||
logger.Debug(cmn.Fmt("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
|
||||
logger.Debug(fmt.Sprintf("enterCommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
logger.Info(cmn.Fmt("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
|
||||
logger.Info(fmt.Sprintf("enterCommit(%v/%v). Current: %v/%v/%v", height, commitRound, cs.Height, cs.Round, cs.Step))
|
||||
|
||||
defer func() {
|
||||
// Done enterCommit:
|
||||
// keep cs.Round the same, commitRound points to the right Precommits set.
|
||||
cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit)
|
||||
cs.CommitRound = commitRound
|
||||
cs.CommitTime = time.Now()
|
||||
cs.CommitTime = tmtime.Now()
|
||||
cs.newStep()
|
||||
|
||||
// Maybe finalize immediately.
|
||||
@@ -1214,7 +1251,7 @@ func (cs *ConsensusState) tryFinalizeCommit(height int64) {
|
||||
logger := cs.Logger.With("height", height)
|
||||
|
||||
if cs.Height != height {
|
||||
cmn.PanicSanity(cmn.Fmt("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
|
||||
cmn.PanicSanity(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
|
||||
}
|
||||
|
||||
blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
|
||||
@@ -1236,7 +1273,7 @@ func (cs *ConsensusState) tryFinalizeCommit(height int64) {
|
||||
// Increment height and goto cstypes.RoundStepNewHeight
|
||||
func (cs *ConsensusState) finalizeCommit(height int64) {
|
||||
if cs.Height != height || cs.Step != cstypes.RoundStepCommit {
|
||||
cs.Logger.Debug(cmn.Fmt("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step))
|
||||
cs.Logger.Debug(fmt.Sprintf("finalizeCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1244,21 +1281,21 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
|
||||
block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts
|
||||
|
||||
if !ok {
|
||||
cmn.PanicSanity(cmn.Fmt("Cannot finalizeCommit, commit does not have two thirds majority"))
|
||||
cmn.PanicSanity(fmt.Sprintf("Cannot finalizeCommit, commit does not have two thirds majority"))
|
||||
}
|
||||
if !blockParts.HasHeader(blockID.PartsHeader) {
|
||||
cmn.PanicSanity(cmn.Fmt("Expected ProposalBlockParts header to be commit header"))
|
||||
cmn.PanicSanity(fmt.Sprintf("Expected ProposalBlockParts header to be commit header"))
|
||||
}
|
||||
if !block.HashesTo(blockID.Hash) {
|
||||
cmn.PanicSanity(cmn.Fmt("Cannot finalizeCommit, ProposalBlock does not hash to commit hash"))
|
||||
cmn.PanicSanity(fmt.Sprintf("Cannot finalizeCommit, ProposalBlock does not hash to commit hash"))
|
||||
}
|
||||
if err := cs.blockExec.ValidateBlock(cs.state, block); err != nil {
|
||||
cmn.PanicConsensus(cmn.Fmt("+2/3 committed an invalid block: %v", err))
|
||||
cmn.PanicConsensus(fmt.Sprintf("+2/3 committed an invalid block: %v", err))
|
||||
}
|
||||
|
||||
cs.Logger.Info(cmn.Fmt("Finalizing commit of block with %d txs", block.NumTxs),
|
||||
cs.Logger.Info(fmt.Sprintf("Finalizing commit of block with %d txs", block.NumTxs),
|
||||
"height", block.Height, "hash", block.Hash(), "root", block.AppHash)
|
||||
cs.Logger.Info(cmn.Fmt("%v", block))
|
||||
cs.Logger.Info(fmt.Sprintf("%v", block))
|
||||
|
||||
fail.Fail() // XXX
|
||||
|
||||
@@ -1357,7 +1394,7 @@ func (cs *ConsensusState) recordMetrics(height int64, block *types.Block) {
|
||||
|
||||
if height > 1 {
|
||||
lastBlockMeta := cs.blockStore.LoadBlockMeta(height - 1)
|
||||
cs.metrics.BlockIntervalSeconds.Observe(
|
||||
cs.metrics.BlockIntervalSeconds.Set(
|
||||
block.Time.Sub(lastBlockMeta.Header.Time).Seconds(),
|
||||
)
|
||||
}
|
||||
@@ -1365,6 +1402,8 @@ func (cs *ConsensusState) recordMetrics(height int64, block *types.Block) {
|
||||
cs.metrics.NumTxs.Set(float64(block.NumTxs))
|
||||
cs.metrics.BlockSizeBytes.Set(float64(block.Size()))
|
||||
cs.metrics.TotalTxs.Set(float64(block.TotalTxs))
|
||||
cs.metrics.CommittedHeight.Set(float64(block.Height))
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1429,9 +1468,13 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
|
||||
}
|
||||
if added && cs.ProposalBlockParts.IsComplete() {
|
||||
// Added and completed!
|
||||
_, err = cdc.UnmarshalBinaryReader(cs.ProposalBlockParts.GetReader(), &cs.ProposalBlock, int64(cs.state.ConsensusParams.BlockSize.MaxBytes))
|
||||
_, err = cdc.UnmarshalBinaryLengthPrefixedReader(
|
||||
cs.ProposalBlockParts.GetReader(),
|
||||
&cs.ProposalBlock,
|
||||
int64(cs.state.ConsensusParams.BlockSize.MaxBytes),
|
||||
)
|
||||
if err != nil {
|
||||
return true, err
|
||||
return added, err
|
||||
}
|
||||
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
|
||||
cs.Logger.Info("Received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash())
|
||||
@@ -1457,39 +1500,42 @@ func (cs *ConsensusState) addProposalBlockPart(msg *BlockPartMessage, peerID p2p
|
||||
if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() {
|
||||
// Move onto the next step
|
||||
cs.enterPrevote(height, cs.Round)
|
||||
if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added
|
||||
cs.enterPrecommit(height, cs.Round)
|
||||
}
|
||||
} else if cs.Step == cstypes.RoundStepCommit {
|
||||
// If we're waiting on the proposal block...
|
||||
cs.tryFinalizeCommit(height)
|
||||
}
|
||||
return true, nil
|
||||
return added, nil
|
||||
}
|
||||
return added, nil
|
||||
}
|
||||
|
||||
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
|
||||
func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerID p2p.ID) error {
|
||||
_, err := cs.addVote(vote, peerID)
|
||||
func (cs *ConsensusState) tryAddVote(vote *types.Vote, peerID p2p.ID) (bool, error) {
|
||||
added, err := cs.addVote(vote, peerID)
|
||||
if err != nil {
|
||||
// If the vote height is off, we'll just ignore it,
|
||||
// But if it's a conflicting sig, add it to the cs.evpool.
|
||||
// If it's otherwise invalid, punish peer.
|
||||
if err == ErrVoteHeightMismatch {
|
||||
return err
|
||||
return added, err
|
||||
} else if voteErr, ok := err.(*types.ErrVoteConflictingVotes); ok {
|
||||
if bytes.Equal(vote.ValidatorAddress, cs.privValidator.GetAddress()) {
|
||||
cs.Logger.Error("Found conflicting vote from ourselves. Did you unsafe_reset a validator?", "height", vote.Height, "round", vote.Round, "type", vote.Type)
|
||||
return err
|
||||
return added, err
|
||||
}
|
||||
cs.evpool.AddEvidence(voteErr.DuplicateVoteEvidence)
|
||||
return err
|
||||
return added, err
|
||||
} else {
|
||||
// Probably an invalid signature / Bad peer.
|
||||
// Seems this can also err sometimes with "Unexpected step" - perhaps not from a bad peer ?
|
||||
cs.Logger.Error("Error attempting to add vote", "err", err)
|
||||
return ErrAddingVote
|
||||
return added, ErrAddingVote
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return added, nil
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -1500,7 +1546,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
// A precommit for the previous height?
|
||||
// These come in while we wait timeoutCommit
|
||||
if vote.Height+1 == cs.Height {
|
||||
if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.VoteTypePrecommit) {
|
||||
if !(cs.Step == cstypes.RoundStepNewHeight && vote.Type == types.PrecommitType) {
|
||||
// TODO: give the reason ..
|
||||
// fmt.Errorf("tryAddVote: Wrong height, not a LastCommit straggler commit.")
|
||||
return added, ErrVoteHeightMismatch
|
||||
@@ -1510,7 +1556,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
return added, err
|
||||
}
|
||||
|
||||
cs.Logger.Info(cmn.Fmt("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
|
||||
cs.Logger.Info(fmt.Sprintf("Added to lastPrecommits: %v", cs.LastCommit.StringShort()))
|
||||
cs.eventBus.PublishEventVote(types.EventDataVote{vote})
|
||||
cs.evsw.FireEvent(types.EventVote, vote)
|
||||
|
||||
@@ -1543,7 +1589,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
cs.evsw.FireEvent(types.EventVote, vote)
|
||||
|
||||
switch vote.Type {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
prevotes := cs.Votes.Prevotes(vote.Round)
|
||||
cs.Logger.Info("Added to prevote", "vote", vote, "prevotes", prevotes.StringShort())
|
||||
|
||||
@@ -1562,7 +1608,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
!cs.LockedBlock.HashesTo(blockID.Hash) {
|
||||
|
||||
cs.Logger.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", vote.Round)
|
||||
cs.LockedRound = 0
|
||||
cs.LockedRound = -1
|
||||
cs.LockedBlock = nil
|
||||
cs.LockedBlockParts = nil
|
||||
cs.eventBus.PublishEventUnlock(cs.RoundStateEvent())
|
||||
@@ -1571,7 +1617,7 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
// Update Valid* if we can.
|
||||
// NOTE: our proposal block may be nil or not what received a polka..
|
||||
// TODO: we may want to still update the ValidBlock and obtain it via gossipping
|
||||
if !blockID.IsZero() &&
|
||||
if len(blockID.Hash) != 0 &&
|
||||
(cs.ValidRound < vote.Round) &&
|
||||
(vote.Round <= cs.Round) &&
|
||||
cs.ProposalBlock.HashesTo(blockID.Hash) {
|
||||
@@ -1583,14 +1629,14 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
}
|
||||
}
|
||||
|
||||
// If +2/3 prevotes for *anything* for this or future round:
|
||||
if cs.Round <= vote.Round && prevotes.HasTwoThirdsAny() {
|
||||
// Round-skip over to PrevoteWait or goto Precommit.
|
||||
cs.enterNewRound(height, vote.Round) // if the vote is ahead of us
|
||||
// If +2/3 prevotes for *anything* for future round:
|
||||
if cs.Round < vote.Round && prevotes.HasTwoThirdsAny() {
|
||||
// Round-skip if there is any 2/3+ of votes ahead of us
|
||||
cs.enterNewRound(height, vote.Round)
|
||||
} else if cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step { // current round
|
||||
if prevotes.HasTwoThirdsMajority() {
|
||||
cs.enterPrecommit(height, vote.Round)
|
||||
} else {
|
||||
cs.enterPrevote(height, vote.Round) // if the vote is ahead of us
|
||||
} else if prevotes.HasTwoThirdsAny() {
|
||||
cs.enterPrevoteWait(height, vote.Round)
|
||||
}
|
||||
} else if cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round {
|
||||
@@ -1600,47 +1646,45 @@ func (cs *ConsensusState) addVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
}
|
||||
}
|
||||
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
precommits := cs.Votes.Precommits(vote.Round)
|
||||
cs.Logger.Info("Added to precommit", "vote", vote, "precommits", precommits.StringShort())
|
||||
|
||||
blockID, ok := precommits.TwoThirdsMajority()
|
||||
if ok {
|
||||
if len(blockID.Hash) == 0 {
|
||||
cs.enterNewRound(height, vote.Round+1)
|
||||
} else {
|
||||
cs.enterNewRound(height, vote.Round)
|
||||
cs.enterPrecommit(height, vote.Round)
|
||||
// Executed as TwoThirdsMajority could be from a higher round
|
||||
cs.enterNewRound(height, vote.Round)
|
||||
cs.enterPrecommit(height, vote.Round)
|
||||
if len(blockID.Hash) != 0 {
|
||||
cs.enterCommit(height, vote.Round)
|
||||
|
||||
if cs.config.SkipTimeoutCommit && precommits.HasAll() {
|
||||
// if we have all the votes now,
|
||||
// go straight to new round (skip timeout commit)
|
||||
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
|
||||
cs.enterNewRound(cs.Height, 0)
|
||||
}
|
||||
|
||||
} else {
|
||||
cs.enterPrecommitWait(height, vote.Round)
|
||||
}
|
||||
} else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() {
|
||||
cs.enterNewRound(height, vote.Round)
|
||||
cs.enterPrecommit(height, vote.Round)
|
||||
cs.enterPrecommitWait(height, vote.Round)
|
||||
}
|
||||
|
||||
default:
|
||||
panic(cmn.Fmt("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
|
||||
panic(fmt.Sprintf("Unexpected vote type %X", vote.Type)) // go-wire should prevent this.
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
func (cs *ConsensusState) signVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) (*types.Vote, error) {
|
||||
addr := cs.privValidator.GetAddress()
|
||||
valIndex, _ := cs.Validators.GetByAddress(addr)
|
||||
|
||||
vote := &types.Vote{
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: valIndex,
|
||||
Height: cs.Height,
|
||||
Round: cs.Round,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Timestamp: cs.voteTime(),
|
||||
Type: type_,
|
||||
BlockID: types.BlockID{hash, header},
|
||||
}
|
||||
@@ -1648,8 +1692,25 @@ func (cs *ConsensusState) signVote(type_ byte, hash []byte, header types.PartSet
|
||||
return vote, err
|
||||
}
|
||||
|
||||
func (cs *ConsensusState) voteTime() time.Time {
|
||||
now := tmtime.Now()
|
||||
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.
|
||||
if cs.LockedBlock != nil {
|
||||
minVoteTime = cs.config.MinValidVoteTime(cs.LockedBlock.Time)
|
||||
} else if cs.ProposalBlock != nil {
|
||||
minVoteTime = cs.config.MinValidVoteTime(cs.ProposalBlock.Time)
|
||||
}
|
||||
|
||||
if now.After(minVoteTime) {
|
||||
return now
|
||||
}
|
||||
return minVoteTime
|
||||
}
|
||||
|
||||
// sign the vote and publish on internalMsgQueue
|
||||
func (cs *ConsensusState) signAddVote(type_ byte, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||
func (cs *ConsensusState) signAddVote(type_ types.SignedMsgType, hash []byte, header types.PartSetHeader) *types.Vote {
|
||||
// if we don't have a key or we're not in the validator set, do nothing
|
||||
if cs.privValidator == nil || !cs.Validators.HasAddress(cs.privValidator.GetAddress()) {
|
||||
return nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6,9 +6,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
type RoundVoteSet struct {
|
||||
@@ -99,8 +99,8 @@ func (hvs *HeightVoteSet) addRound(round int) {
|
||||
cmn.PanicSanity("addRound() for an existing round")
|
||||
}
|
||||
// log.Debug("addRound(round)", "round", round)
|
||||
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrevote, hvs.valSet)
|
||||
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.VoteTypePrecommit, hvs.valSet)
|
||||
prevotes := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrevoteType, hvs.valSet)
|
||||
precommits := types.NewVoteSet(hvs.chainID, hvs.height, round, types.PrecommitType, hvs.valSet)
|
||||
hvs.roundVoteSets[round] = RoundVoteSet{
|
||||
Prevotes: prevotes,
|
||||
Precommits: precommits,
|
||||
@@ -134,13 +134,13 @@ func (hvs *HeightVoteSet) AddVote(vote *types.Vote, peerID p2p.ID) (added bool,
|
||||
func (hvs *HeightVoteSet) Prevotes(round int) *types.VoteSet {
|
||||
hvs.mtx.Lock()
|
||||
defer hvs.mtx.Unlock()
|
||||
return hvs.getVoteSet(round, types.VoteTypePrevote)
|
||||
return hvs.getVoteSet(round, types.PrevoteType)
|
||||
}
|
||||
|
||||
func (hvs *HeightVoteSet) Precommits(round int) *types.VoteSet {
|
||||
hvs.mtx.Lock()
|
||||
defer hvs.mtx.Unlock()
|
||||
return hvs.getVoteSet(round, types.VoteTypePrecommit)
|
||||
return hvs.getVoteSet(round, types.PrecommitType)
|
||||
}
|
||||
|
||||
// Last round and blockID that has +2/3 prevotes for a particular block or nil.
|
||||
@@ -149,7 +149,7 @@ func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
|
||||
hvs.mtx.Lock()
|
||||
defer hvs.mtx.Unlock()
|
||||
for r := hvs.round; r >= 0; r-- {
|
||||
rvs := hvs.getVoteSet(r, types.VoteTypePrevote)
|
||||
rvs := hvs.getVoteSet(r, types.PrevoteType)
|
||||
polBlockID, ok := rvs.TwoThirdsMajority()
|
||||
if ok {
|
||||
return r, polBlockID
|
||||
@@ -158,18 +158,18 @@ func (hvs *HeightVoteSet) POLInfo() (polRound int, polBlockID types.BlockID) {
|
||||
return -1, types.BlockID{}
|
||||
}
|
||||
|
||||
func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
|
||||
func (hvs *HeightVoteSet) getVoteSet(round int, type_ types.SignedMsgType) *types.VoteSet {
|
||||
rvs, ok := hvs.roundVoteSets[round]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
switch type_ {
|
||||
case types.VoteTypePrevote:
|
||||
case types.PrevoteType:
|
||||
return rvs.Prevotes
|
||||
case types.VoteTypePrecommit:
|
||||
case types.PrecommitType:
|
||||
return rvs.Precommits
|
||||
default:
|
||||
cmn.PanicSanity(cmn.Fmt("Unexpected vote type %X", type_))
|
||||
cmn.PanicSanity(fmt.Sprintf("Unexpected vote type %X", type_))
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -178,7 +178,7 @@ func (hvs *HeightVoteSet) getVoteSet(round int, type_ byte) *types.VoteSet {
|
||||
// NOTE: if there are too many peers, or too much peer churn,
|
||||
// this can cause memory issues.
|
||||
// TODO: implement ability to remove peers too
|
||||
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ byte, peerID p2p.ID, blockID types.BlockID) error {
|
||||
func (hvs *HeightVoteSet) SetPeerMaj23(round int, type_ types.SignedMsgType, peerID p2p.ID, blockID types.BlockID) error {
|
||||
hvs.mtx.Lock()
|
||||
defer hvs.mtx.Unlock()
|
||||
if !types.IsVoteTypeValid(type_) {
|
||||
@@ -219,7 +219,7 @@ func (hvs *HeightVoteSet) StringIndented(indent string) string {
|
||||
voteSetString = roundVoteSet.Precommits.StringShort()
|
||||
vsStrings = append(vsStrings, voteSetString)
|
||||
}
|
||||
return cmn.Fmt(`HeightVoteSet{H:%v R:0~%v
|
||||
return fmt.Sprintf(`HeightVoteSet{H:%v R:0~%v
|
||||
%s %v
|
||||
%s}`,
|
||||
hvs.height, hvs.round,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
var config *cfg.Config // NOTE: must be reset for each _test.go file
|
||||
@@ -55,14 +55,14 @@ func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivVali
|
||||
ValidatorIndex: valIndex,
|
||||
Height: height,
|
||||
Round: round,
|
||||
Timestamp: time.Now().UTC(),
|
||||
Type: types.VoteTypePrecommit,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: types.PrecommitType,
|
||||
BlockID: types.BlockID{[]byte("fakehash"), types.PartSetHeader{}},
|
||||
}
|
||||
chainID := config.ChainID()
|
||||
err := privVal.SignVote(chainID, vote)
|
||||
if err != nil {
|
||||
panic(cmn.Fmt("Error signing vote: %v", err))
|
||||
panic(fmt.Sprintf("Error signing vote: %v", err))
|
||||
return nil
|
||||
}
|
||||
return vote
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -107,8 +107,8 @@ func (rs *RoundState) RoundStateSimple() RoundStateSimple {
|
||||
|
||||
// RoundStateEvent returns the H/R/S of the RoundState as an event.
|
||||
func (rs *RoundState) RoundStateEvent() types.EventDataRoundState {
|
||||
// XXX: copy the RoundState
|
||||
// if we want to avoid this, we may need synchronous events after all
|
||||
// copy the RoundState.
|
||||
// TODO: if we want to avoid this, we may need synchronous events after all
|
||||
rsCopy := *rs
|
||||
edrs := types.EventDataRoundState{
|
||||
Height: rs.Height,
|
||||
|
||||
@@ -2,12 +2,12 @@ package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
func BenchmarkRoundStateDeepCopy(b *testing.B) {
|
||||
@@ -23,11 +23,11 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
|
||||
Hash: cmn.RandBytes(20),
|
||||
},
|
||||
}
|
||||
sig := make([]byte, ed25519.SignatureEd25519Size)
|
||||
sig := make([]byte, ed25519.SignatureSize)
|
||||
for i := 0; i < nval; i++ {
|
||||
precommits[i] = &types.Vote{
|
||||
ValidatorAddress: types.Address(cmn.RandBytes(20)),
|
||||
Timestamp: time.Now(),
|
||||
Timestamp: tmtime.Now(),
|
||||
BlockID: blockID,
|
||||
Signature: sig,
|
||||
}
|
||||
@@ -40,7 +40,7 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
|
||||
block := &types.Block{
|
||||
Header: types.Header{
|
||||
ChainID: cmn.RandStr(12),
|
||||
Time: time.Now(),
|
||||
Time: tmtime.Now(),
|
||||
LastBlockID: blockID,
|
||||
LastCommitHash: cmn.RandBytes(20),
|
||||
DataHash: cmn.RandBytes(20),
|
||||
@@ -62,7 +62,7 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
|
||||
parts := block.MakePartSet(4096)
|
||||
// Random Proposal
|
||||
proposal := &types.Proposal{
|
||||
Timestamp: time.Now(),
|
||||
Timestamp: tmtime.Now(),
|
||||
BlockPartsHeader: types.PartSetHeader{
|
||||
Hash: cmn.RandBytes(20),
|
||||
},
|
||||
@@ -73,8 +73,8 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
|
||||
// TODO: hvs :=
|
||||
|
||||
rs := &RoundState{
|
||||
StartTime: time.Now(),
|
||||
CommitTime: time.Now(),
|
||||
StartTime: tmtime.Now(),
|
||||
CommitTime: tmtime.Now(),
|
||||
Validators: vset,
|
||||
Proposal: proposal,
|
||||
ProposalBlock: block,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/tendermint/go-amino"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
package consensus
|
||||
|
||||
import (
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// kind of arbitrary
|
||||
var Spec = "1" // async
|
||||
var Major = "0" //
|
||||
var Minor = "2" // replay refactor
|
||||
var Revision = "2" // validation -> commit
|
||||
|
||||
var Version = cmn.Fmt("v%s/%s.%s.%s", Spec, Major, Minor, Revision)
|
||||
@@ -11,13 +11,14 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
auto "github.com/tendermint/tendermint/libs/autofile"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
const (
|
||||
// must be greater than params.BlockGossip.BlockPartSizeBytes + a few bytes
|
||||
// must be greater than types.BlockPartSizeBytes + a few bytes
|
||||
maxMsgSizeBytes = 1024 * 1024 // 1MB
|
||||
)
|
||||
|
||||
@@ -72,13 +73,13 @@ type baseWAL struct {
|
||||
enc *WALEncoder
|
||||
}
|
||||
|
||||
func NewWAL(walFile string) (*baseWAL, error) {
|
||||
func NewWAL(walFile string, groupOptions ...func(*auto.Group)) (*baseWAL, error) {
|
||||
err := cmn.EnsureDir(filepath.Dir(walFile), 0700)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to ensure WAL directory is in place")
|
||||
}
|
||||
|
||||
group, err := auto.OpenGroup(walFile)
|
||||
group, err := auto.OpenGroup(walFile, groupOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -119,8 +120,8 @@ func (wal *baseWAL) Write(msg WALMessage) {
|
||||
}
|
||||
|
||||
// Write the wal message
|
||||
if err := wal.enc.Encode(&TimedWALMessage{time.Now(), msg}); err != nil {
|
||||
panic(cmn.Fmt("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg))
|
||||
if err := wal.enc.Encode(&TimedWALMessage{tmtime.Now(), msg}); err != nil {
|
||||
panic(fmt.Sprintf("Error writing msg to consensus wal: %v \n\nMessage: %v", err, msg))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +135,7 @@ func (wal *baseWAL) WriteSync(msg WALMessage) {
|
||||
|
||||
wal.Write(msg)
|
||||
if err := wal.group.Flush(); err != nil {
|
||||
panic(cmn.Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
|
||||
panic(fmt.Sprintf("Error flushing consensus wal buf to file. Error: %v \n", err))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -13,22 +14,21 @@ import (
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
bc "github.com/tendermint/tendermint/blockchain"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
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"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
// WALWithNBlocks generates a consensus WAL. It does this by spining up a
|
||||
// WALGenerateNBlocks generates a consensus WAL. It does this by spining 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. Then it returns a WAL
|
||||
// content. If the node fails to produce given numBlocks, it returns an error.
|
||||
func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
||||
// (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()
|
||||
|
||||
app := kvstore.NewPersistentKVStoreApplication(filepath.Join(config.DBDir(), "wal_generator"))
|
||||
@@ -38,31 +38,33 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// COPY PASTE FROM node.go WITH A FEW MODIFICATIONS
|
||||
// NOTE: we can't import node package because of circular dependency
|
||||
// NOTE: we can't import node package because of circular dependency.
|
||||
// NOTE: we don't do handshake so need to set state.Version.Consensus.App directly.
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
||||
genDoc, err := types.GenesisDocFromFile(config.GenesisFile())
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to read genesis file")
|
||||
return errors.Wrap(err, "failed to read genesis file")
|
||||
}
|
||||
stateDB := db.NewMemDB()
|
||||
blockStoreDB := db.NewMemDB()
|
||||
state, err := sm.MakeGenesisState(genDoc)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to make genesis state")
|
||||
return errors.Wrap(err, "failed to make genesis state")
|
||||
}
|
||||
state.Version.Consensus.App = kvstore.ProtocolVersion
|
||||
blockStore := bc.NewBlockStore(blockStoreDB)
|
||||
handshaker := NewHandshaker(stateDB, state, blockStore, genDoc)
|
||||
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app), handshaker)
|
||||
proxyApp := proxy.NewAppConns(proxy.NewLocalClientCreator(app))
|
||||
proxyApp.SetLogger(logger.With("module", "proxy"))
|
||||
if err := proxyApp.Start(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start proxy app connections")
|
||||
return errors.Wrap(err, "failed to start proxy app connections")
|
||||
}
|
||||
defer proxyApp.Stop()
|
||||
|
||||
eventBus := types.NewEventBus()
|
||||
eventBus.SetLogger(logger.With("module", "events"))
|
||||
if err := eventBus.Start(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start event bus")
|
||||
return errors.Wrap(err, "failed to start event bus")
|
||||
}
|
||||
defer eventBus.Stop()
|
||||
mempool := sm.MockMempool{}
|
||||
@@ -78,8 +80,6 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// set consensus wal to buffered WAL, which will write all incoming msgs to buffer
|
||||
var b bytes.Buffer
|
||||
wr := bufio.NewWriter(&b)
|
||||
numBlocksWritten := make(chan struct{})
|
||||
wal := newByteBufferWAL(logger, NewWALEncoder(wr), int64(numBlocks), numBlocksWritten)
|
||||
// see wal.go#103
|
||||
@@ -87,20 +87,32 @@ func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
||||
consensusState.wal = wal
|
||||
|
||||
if err := consensusState.Start(); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to start consensus state")
|
||||
return errors.Wrap(err, "failed to start consensus state")
|
||||
}
|
||||
|
||||
select {
|
||||
case <-numBlocksWritten:
|
||||
consensusState.Stop()
|
||||
wr.Flush()
|
||||
return b.Bytes(), nil
|
||||
return nil
|
||||
case <-time.After(1 * time.Minute):
|
||||
consensusState.Stop()
|
||||
return []byte{}, fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks)
|
||||
return fmt.Errorf("waited too long for tendermint to produce %d blocks (grep logs for `wal_generator`)", numBlocks)
|
||||
}
|
||||
}
|
||||
|
||||
//WALWithNBlocks returns a WAL content with numBlocks.
|
||||
func WALWithNBlocks(numBlocks int) (data []byte, err error) {
|
||||
var b bytes.Buffer
|
||||
wr := bufio.NewWriter(&b)
|
||||
|
||||
if err := WALGenerateNBlocks(wr, numBlocks); err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
wr.Flush()
|
||||
return b.Bytes(), nil
|
||||
}
|
||||
|
||||
// f**ing long, but unique for each test
|
||||
func makePathname() string {
|
||||
// get path
|
||||
|
||||
@@ -3,20 +3,71 @@ package consensus
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
// "sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/consensus/types"
|
||||
"github.com/tendermint/tendermint/libs/autofile"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestWALTruncate(t *testing.T) {
|
||||
walDir, err := ioutil.TempDir("", "wal")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create temp WAL file: %v", err))
|
||||
}
|
||||
defer os.RemoveAll(walDir)
|
||||
|
||||
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.
|
||||
wal, err := NewWAL(walFile, autofile.GroupHeadSizeLimit(4096), autofile.GroupCheckDuration(1*time.Millisecond))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
wal.Start()
|
||||
defer wal.Stop()
|
||||
|
||||
//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)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Millisecond) //wait groupCheckDuration, make sure RotateFile run
|
||||
|
||||
wal.Group().Flush()
|
||||
|
||||
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")
|
||||
defer gr.Close()
|
||||
|
||||
dec := NewWALDecoder(gr)
|
||||
msg, err := dec.Decode()
|
||||
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"))
|
||||
}
|
||||
|
||||
func TestWALEncoderDecoder(t *testing.T) {
|
||||
now := time.Now()
|
||||
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}},
|
||||
@@ -54,8 +105,8 @@ func TestWALSearchForEndHeight(t *testing.T) {
|
||||
|
||||
h := int64(3)
|
||||
gr, found, err := wal.SearchForEndHeight(h, &WALSearchOptions{})
|
||||
assert.NoError(t, err, cmn.Fmt("expected not to err on height %d", h))
|
||||
assert.True(t, found, cmn.Fmt("expected to find end height for %d", h))
|
||||
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")
|
||||
defer gr.Close()
|
||||
|
||||
@@ -64,7 +115,7 @@ 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, cmn.Fmt("wrong height"))
|
||||
assert.Equal(t, rs.Height, h+1, fmt.Sprintf("wrong height"))
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -93,7 +144,7 @@ func benchmarkWalDecode(b *testing.B, n int) {
|
||||
enc := NewWALEncoder(buf)
|
||||
|
||||
data := nBytes(n)
|
||||
enc.Encode(&TimedWALMessage{Msg: data, Time: time.Now().Round(time.Second)})
|
||||
enc.Encode(&TimedWALMessage{Msg: data, Time: time.Now().Round(time.Second).UTC()})
|
||||
|
||||
encoded := buf.Bytes()
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"golang.org/x/crypto/openpgp/armor"
|
||||
"golang.org/x/crypto/openpgp/armor" // forked to github.com/tendermint/crypto
|
||||
)
|
||||
|
||||
func EncodeArmor(blockType string, headers map[string]string, data []byte) string {
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tendermint/ed25519"
|
||||
"github.com/tendermint/ed25519/extra25519"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"golang.org/x/crypto/ed25519" // forked to github.com/tendermint/crypto
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
)
|
||||
@@ -18,11 +18,11 @@ import (
|
||||
var _ crypto.PrivKey = PrivKeyEd25519{}
|
||||
|
||||
const (
|
||||
Ed25519PrivKeyAminoRoute = "tendermint/PrivKeyEd25519"
|
||||
Ed25519PubKeyAminoRoute = "tendermint/PubKeyEd25519"
|
||||
PrivKeyAminoRoute = "tendermint/PrivKeyEd25519"
|
||||
PubKeyAminoRoute = "tendermint/PubKeyEd25519"
|
||||
// Size of an Edwards25519 signature. Namely the size of a compressed
|
||||
// Edwards25519 point, and a field element. Both of which are 32 bytes.
|
||||
SignatureEd25519Size = 64
|
||||
SignatureSize = 64
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
@@ -30,11 +30,11 @@ var cdc = amino.NewCodec()
|
||||
func init() {
|
||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PubKeyEd25519{},
|
||||
Ed25519PubKeyAminoRoute, nil)
|
||||
PubKeyAminoRoute, nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PrivKeyEd25519{},
|
||||
Ed25519PrivKeyAminoRoute, nil)
|
||||
PrivKeyAminoRoute, nil)
|
||||
}
|
||||
|
||||
// PrivKeyEd25519 implements crypto.PrivKey.
|
||||
@@ -46,9 +46,14 @@ func (privKey PrivKeyEd25519) Bytes() []byte {
|
||||
}
|
||||
|
||||
// Sign produces a signature on the provided message.
|
||||
// This assumes the privkey is wellformed in the golang format.
|
||||
// The first 32 bytes should be random,
|
||||
// corresponding to the normal ed25519 private key.
|
||||
// The latter 32 bytes should be the compressed public key.
|
||||
// If these conditions aren't met, Sign will panic or produce an
|
||||
// incorrect signature.
|
||||
func (privKey PrivKeyEd25519) Sign(msg []byte) ([]byte, error) {
|
||||
privKeyBytes := [64]byte(privKey)
|
||||
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
|
||||
signatureBytes := ed25519.Sign(privKey[:], msg)
|
||||
return signatureBytes[:], nil
|
||||
}
|
||||
|
||||
@@ -65,14 +70,14 @@ func (privKey PrivKeyEd25519) PubKey() crypto.PubKey {
|
||||
break
|
||||
}
|
||||
}
|
||||
if initialized {
|
||||
var pubkeyBytes [PubKeyEd25519Size]byte
|
||||
copy(pubkeyBytes[:], privKeyBytes[32:])
|
||||
return PubKeyEd25519(pubkeyBytes)
|
||||
|
||||
if !initialized {
|
||||
panic("Expected PrivKeyEd25519 to include concatenated pubkey bytes")
|
||||
}
|
||||
|
||||
pubBytes := *ed25519.MakePublicKey(&privKeyBytes)
|
||||
return PubKeyEd25519(pubBytes)
|
||||
var pubkeyBytes [PubKeyEd25519Size]byte
|
||||
copy(pubkeyBytes[:], privKeyBytes[32:])
|
||||
return PubKeyEd25519(pubkeyBytes)
|
||||
}
|
||||
|
||||
// Equals - you probably don't need to use this.
|
||||
@@ -85,17 +90,6 @@ func (privKey PrivKeyEd25519) Equals(other crypto.PrivKey) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// ToCurve25519 takes a private key and returns its representation on
|
||||
// Curve25519. Curve25519 is birationally equivalent to Edwards25519,
|
||||
// which Ed25519 uses internally. This method is intended for use in
|
||||
// an X25519 Diffie Hellman key exchange.
|
||||
func (privKey PrivKeyEd25519) ToCurve25519() *[PubKeyEd25519Size]byte {
|
||||
keyCurve25519 := new([32]byte)
|
||||
privKeyBytes := [64]byte(privKey)
|
||||
extra25519.PrivateKeyToCurve25519(keyCurve25519, &privKeyBytes)
|
||||
return keyCurve25519
|
||||
}
|
||||
|
||||
// GenPrivKey generates a new ed25519 private key.
|
||||
// It uses OS randomness in conjunction with the current global random seed
|
||||
// in tendermint/libs/common to generate the private key.
|
||||
@@ -105,16 +99,16 @@ func GenPrivKey() PrivKeyEd25519 {
|
||||
|
||||
// genPrivKey generates a new ed25519 private key using the provided reader.
|
||||
func genPrivKey(rand io.Reader) PrivKeyEd25519 {
|
||||
privKey := new([64]byte)
|
||||
_, err := io.ReadFull(rand, privKey[:32])
|
||||
seed := make([]byte, 32)
|
||||
_, err := io.ReadFull(rand, seed[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// ed25519.MakePublicKey(privKey) alters the last 32 bytes of privKey.
|
||||
// It places the pubkey in the last 32 bytes of privKey, and returns the
|
||||
// public key.
|
||||
ed25519.MakePublicKey(privKey)
|
||||
return PrivKeyEd25519(*privKey)
|
||||
|
||||
privKey := ed25519.NewKeyFromSeed(seed)
|
||||
var privKeyEd PrivKeyEd25519
|
||||
copy(privKeyEd[:], privKey)
|
||||
return privKeyEd
|
||||
}
|
||||
|
||||
// GenPrivKeyFromSecret hashes the secret with SHA2, and uses
|
||||
@@ -122,14 +116,12 @@ func genPrivKey(rand io.Reader) PrivKeyEd25519 {
|
||||
// NOTE: secret should be the output of a KDF like bcrypt,
|
||||
// if it's derived from user input.
|
||||
func GenPrivKeyFromSecret(secret []byte) PrivKeyEd25519 {
|
||||
privKey32 := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.
|
||||
privKey := new([64]byte)
|
||||
copy(privKey[:32], privKey32)
|
||||
// ed25519.MakePublicKey(privKey) alters the last 32 bytes of privKey.
|
||||
// It places the pubkey in the last 32 bytes of privKey, and returns the
|
||||
// public key.
|
||||
ed25519.MakePublicKey(privKey)
|
||||
return PrivKeyEd25519(*privKey)
|
||||
seed := crypto.Sha256(secret) // Not Ripemd160 because we want 32 bytes.
|
||||
|
||||
privKey := ed25519.NewKeyFromSeed(seed)
|
||||
var privKeyEd PrivKeyEd25519
|
||||
copy(privKeyEd[:], privKey)
|
||||
return privKeyEd
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -156,30 +148,12 @@ func (pubKey PubKeyEd25519) Bytes() []byte {
|
||||
return bz
|
||||
}
|
||||
|
||||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ []byte) bool {
|
||||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig []byte) bool {
|
||||
// make sure we use the same algorithm to sign
|
||||
if len(sig_) != SignatureEd25519Size {
|
||||
if len(sig) != SignatureSize {
|
||||
return false
|
||||
}
|
||||
sig := new([SignatureEd25519Size]byte)
|
||||
copy(sig[:], sig_)
|
||||
pubKeyBytes := [PubKeyEd25519Size]byte(pubKey)
|
||||
return ed25519.Verify(&pubKeyBytes, msg, sig)
|
||||
}
|
||||
|
||||
// ToCurve25519 takes a public key and returns its representation on
|
||||
// Curve25519. Curve25519 is birationally equivalent to Edwards25519,
|
||||
// which Ed25519 uses internally. This method is intended for use in
|
||||
// an X25519 Diffie Hellman key exchange.
|
||||
//
|
||||
// If there is an error, then this function returns nil.
|
||||
func (pubKey PubKeyEd25519) ToCurve25519() *[PubKeyEd25519Size]byte {
|
||||
keyCurve25519, pubKeyBytes := new([PubKeyEd25519Size]byte), [PubKeyEd25519Size]byte(pubKey)
|
||||
ok := extra25519.PublicKeyToCurve25519(keyCurve25519, &pubKeyBytes)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return keyCurve25519
|
||||
return ed25519.Verify(pubKey[:], msg, sig)
|
||||
}
|
||||
|
||||
func (pubKey PubKeyEd25519) String() string {
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
package cryptoAmino
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/multisig"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
|
||||
// routeTable is used to map public key concrete types back
|
||||
// to their amino routes. This should eventually be handled
|
||||
// by amino. Example usage:
|
||||
// routeTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoRoute
|
||||
var routeTable = make(map[reflect.Type]string, 3)
|
||||
|
||||
func init() {
|
||||
// NOTE: It's important that there be no conflicts here,
|
||||
// as that would change the canonical representations,
|
||||
@@ -17,6 +26,20 @@ func init() {
|
||||
// https://github.com/tendermint/go-amino/issues/9
|
||||
// is resolved
|
||||
RegisterAmino(cdc)
|
||||
|
||||
// TODO: Have amino provide a way to go from concrete struct to route directly.
|
||||
// Its currently a private API
|
||||
routeTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoRoute
|
||||
routeTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoRoute
|
||||
routeTable[reflect.TypeOf(&multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute
|
||||
}
|
||||
|
||||
// PubkeyAminoRoute returns the amino route of a pubkey
|
||||
// cdc is currently passed in, as eventually this will not be using
|
||||
// a package level codec.
|
||||
func PubkeyAminoRoute(cdc *amino.Codec, key crypto.PubKey) (string, bool) {
|
||||
route, found := routeTable[reflect.TypeOf(key)]
|
||||
return route, found
|
||||
}
|
||||
|
||||
// RegisterAmino registers all crypto related types in the given (amino) codec.
|
||||
@@ -24,15 +47,17 @@ func RegisterAmino(cdc *amino.Codec) {
|
||||
// These are all written here instead of
|
||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
|
||||
"tendermint/PubKeyEd25519", nil)
|
||||
ed25519.PubKeyAminoRoute, nil)
|
||||
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
||||
"tendermint/PubKeySecp256k1", nil)
|
||||
secp256k1.PubKeyAminoRoute, nil)
|
||||
cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{},
|
||||
multisig.PubKeyMultisigThresholdAminoRoute, nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||
cdc.RegisterConcrete(ed25519.PrivKeyEd25519{},
|
||||
"tendermint/PrivKeyEd25519", nil)
|
||||
ed25519.PrivKeyAminoRoute, nil)
|
||||
cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{},
|
||||
"tendermint/PrivKeySecp256k1", nil)
|
||||
secp256k1.PrivKeyAminoRoute, nil)
|
||||
}
|
||||
|
||||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/multisig"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
@@ -53,24 +54,27 @@ func ExamplePrintRegisteredTypes() {
|
||||
//| ---- | ---- | ------ | ----- | ------ |
|
||||
//| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | |
|
||||
//| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | |
|
||||
//| PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | |
|
||||
//| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | |
|
||||
//| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | |
|
||||
}
|
||||
|
||||
func TestKeyEncodings(t *testing.T) {
|
||||
cases := []struct {
|
||||
privKey crypto.PrivKey
|
||||
privSize, pubSize int // binary sizes
|
||||
privKey crypto.PrivKey
|
||||
privSize, pubSize, sigSize int // binary sizes
|
||||
}{
|
||||
{
|
||||
privKey: ed25519.GenPrivKey(),
|
||||
privSize: 69,
|
||||
pubSize: 37,
|
||||
sigSize: 65,
|
||||
},
|
||||
{
|
||||
privKey: secp256k1.GenPrivKey(),
|
||||
privSize: 37,
|
||||
pubSize: 38,
|
||||
sigSize: 65,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -87,7 +91,7 @@ func TestKeyEncodings(t *testing.T) {
|
||||
var sig1, sig2 []byte
|
||||
sig1, err := tc.privKey.Sign([]byte("something"))
|
||||
assert.NoError(t, err, "tc #%d", tcIndex)
|
||||
checkAminoBinary(t, sig1, &sig2, -1) // Signature size changes for Secp anyways.
|
||||
checkAminoBinary(t, sig1, &sig2, tc.sigSize)
|
||||
assert.EqualValues(t, sig1, sig2, "tc #%d", tcIndex)
|
||||
|
||||
// Check (de/en)codings of PubKeys.
|
||||
@@ -116,11 +120,29 @@ func TestNilEncodings(t *testing.T) {
|
||||
var e, f crypto.PrivKey
|
||||
checkAminoJSON(t, &e, &f, true)
|
||||
assert.EqualValues(t, e, f)
|
||||
|
||||
}
|
||||
|
||||
func TestPubKeyInvalidDataProperReturnsEmpty(t *testing.T) {
|
||||
pk, err := PubKeyFromBytes([]byte("foo"))
|
||||
require.NotNil(t, err, "expecting a non-nil error")
|
||||
require.Nil(t, pk, "expecting an empty public key on error")
|
||||
require.NotNil(t, err)
|
||||
require.Nil(t, pk)
|
||||
}
|
||||
|
||||
func TestPubkeyAminoRoute(t *testing.T) {
|
||||
tests := []struct {
|
||||
key crypto.PubKey
|
||||
want string
|
||||
found bool
|
||||
}{
|
||||
{ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoRoute, true},
|
||||
{secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoRoute, true},
|
||||
{&multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true},
|
||||
}
|
||||
for i, tc := range tests {
|
||||
got, found := PubkeyAminoRoute(cdc, tc.key)
|
||||
require.Equal(t, tc.found, found, "not equal on tc %d", i)
|
||||
if tc.found {
|
||||
require.Equal(t, tc.want, got, "not equal on tc %d", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package crypto
|
||||
import (
|
||||
"crypto/sha256"
|
||||
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
"golang.org/x/crypto/ripemd160" // forked to github.com/tendermint/crypto
|
||||
)
|
||||
|
||||
func Sha256(bytes []byte) []byte {
|
||||
|
||||
6
crypto/merkle/compile.sh
Normal file
6
crypto/merkle/compile.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#! /bin/bash
|
||||
|
||||
protoc --gogo_out=. -I $GOPATH/src/ -I . -I $GOPATH/src/github.com/gogo/protobuf/protobuf merkle.proto
|
||||
echo "--> adding nolint declarations to protobuf generated files"
|
||||
awk '/package merkle/ { print "//nolint: gas"; print; next }1' merkle.pb.go > merkle.pb.go.new
|
||||
mv merkle.pb.go.new merkle.pb.go
|
||||
792
crypto/merkle/merkle.pb.go
Normal file
792
crypto/merkle/merkle.pb.go
Normal file
@@ -0,0 +1,792 @@
|
||||
// Code generated by protoc-gen-gogo. DO NOT EDIT.
|
||||
// source: crypto/merkle/merkle.proto
|
||||
|
||||
package merkle
|
||||
|
||||
import proto "github.com/gogo/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import _ "github.com/gogo/protobuf/gogoproto"
|
||||
|
||||
import bytes "bytes"
|
||||
|
||||
import io "io"
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
|
||||
|
||||
// ProofOp defines an operation used for calculating Merkle root
|
||||
// The data could be arbitrary format, providing nessecary data
|
||||
// for example neighbouring node hash
|
||||
type ProofOp struct {
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
Key []byte `protobuf:"bytes,2,opt,name=key,proto3" json:"key,omitempty"`
|
||||
Data []byte `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
func (m *ProofOp) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *ProofOp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_ProofOp.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 *ProofOp) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ProofOp.Merge(dst, src)
|
||||
}
|
||||
func (m *ProofOp) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *ProofOp) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ProofOp.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ProofOp proto.InternalMessageInfo
|
||||
|
||||
func (m *ProofOp) GetType() string {
|
||||
if m != nil {
|
||||
return m.Type
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ProofOp) GetKey() []byte {
|
||||
if m != nil {
|
||||
return m.Key
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ProofOp) GetData() []byte {
|
||||
if m != nil {
|
||||
return m.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Proof is Merkle proof defined by the list of ProofOps
|
||||
type Proof struct {
|
||||
Ops []ProofOp `protobuf:"bytes,1,rep,name=ops" json:"ops"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
}
|
||||
|
||||
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}
|
||||
}
|
||||
func (m *Proof) XXX_Unmarshal(b []byte) error {
|
||||
return m.Unmarshal(b)
|
||||
}
|
||||
func (m *Proof) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
if deterministic {
|
||||
return xxx_messageInfo_Proof.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 *Proof) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Proof.Merge(dst, src)
|
||||
}
|
||||
func (m *Proof) XXX_Size() int {
|
||||
return m.Size()
|
||||
}
|
||||
func (m *Proof) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Proof.DiscardUnknown(m)
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Proof proto.InternalMessageInfo
|
||||
|
||||
func (m *Proof) GetOps() []ProofOp {
|
||||
if m != nil {
|
||||
return m.Ops
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*ProofOp)(nil), "merkle.ProofOp")
|
||||
proto.RegisterType((*Proof)(nil), "merkle.Proof")
|
||||
}
|
||||
func (this *ProofOp) Equal(that interface{}) bool {
|
||||
if that == nil {
|
||||
return this == nil
|
||||
}
|
||||
|
||||
that1, ok := that.(*ProofOp)
|
||||
if !ok {
|
||||
that2, ok := that.(ProofOp)
|
||||
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 !bytes.Equal(this.Key, that1.Key) {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(this.Data, that1.Data) {
|
||||
return false
|
||||
}
|
||||
if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (this *Proof) Equal(that interface{}) bool {
|
||||
if that == nil {
|
||||
return this == nil
|
||||
}
|
||||
|
||||
that1, ok := that.(*Proof)
|
||||
if !ok {
|
||||
that2, ok := that.(Proof)
|
||||
if ok {
|
||||
that1 = &that2
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if that1 == nil {
|
||||
return this == nil
|
||||
} else if this == nil {
|
||||
return false
|
||||
}
|
||||
if len(this.Ops) != len(that1.Ops) {
|
||||
return false
|
||||
}
|
||||
for i := range this.Ops {
|
||||
if !this.Ops[i].Equal(&that1.Ops[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (m *ProofOp) 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 *ProofOp) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Type) > 0 {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintMerkle(dAtA, i, uint64(len(m.Type)))
|
||||
i += copy(dAtA[i:], m.Type)
|
||||
}
|
||||
if len(m.Key) > 0 {
|
||||
dAtA[i] = 0x12
|
||||
i++
|
||||
i = encodeVarintMerkle(dAtA, i, uint64(len(m.Key)))
|
||||
i += copy(dAtA[i:], m.Key)
|
||||
}
|
||||
if len(m.Data) > 0 {
|
||||
dAtA[i] = 0x1a
|
||||
i++
|
||||
i = encodeVarintMerkle(dAtA, i, uint64(len(m.Data)))
|
||||
i += copy(dAtA[i:], m.Data)
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (m *Proof) 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 *Proof) MarshalTo(dAtA []byte) (int, error) {
|
||||
var i int
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Ops) > 0 {
|
||||
for _, msg := range m.Ops {
|
||||
dAtA[i] = 0xa
|
||||
i++
|
||||
i = encodeVarintMerkle(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 encodeVarintMerkle(dAtA []byte, offset int, v uint64) int {
|
||||
for v >= 1<<7 {
|
||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||
v >>= 7
|
||||
offset++
|
||||
}
|
||||
dAtA[offset] = uint8(v)
|
||||
return offset + 1
|
||||
}
|
||||
func NewPopulatedProofOp(r randyMerkle, easy bool) *ProofOp {
|
||||
this := &ProofOp{}
|
||||
this.Type = string(randStringMerkle(r))
|
||||
v1 := r.Intn(100)
|
||||
this.Key = make([]byte, v1)
|
||||
for i := 0; i < v1; i++ {
|
||||
this.Key[i] = byte(r.Intn(256))
|
||||
}
|
||||
v2 := r.Intn(100)
|
||||
this.Data = make([]byte, v2)
|
||||
for i := 0; i < v2; i++ {
|
||||
this.Data[i] = byte(r.Intn(256))
|
||||
}
|
||||
if !easy && r.Intn(10) != 0 {
|
||||
this.XXX_unrecognized = randUnrecognizedMerkle(r, 4)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
func NewPopulatedProof(r randyMerkle, easy bool) *Proof {
|
||||
this := &Proof{}
|
||||
if r.Intn(10) != 0 {
|
||||
v3 := r.Intn(5)
|
||||
this.Ops = make([]ProofOp, v3)
|
||||
for i := 0; i < v3; i++ {
|
||||
v4 := NewPopulatedProofOp(r, easy)
|
||||
this.Ops[i] = *v4
|
||||
}
|
||||
}
|
||||
if !easy && r.Intn(10) != 0 {
|
||||
this.XXX_unrecognized = randUnrecognizedMerkle(r, 2)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
type randyMerkle interface {
|
||||
Float32() float32
|
||||
Float64() float64
|
||||
Int63() int64
|
||||
Int31() int32
|
||||
Uint32() uint32
|
||||
Intn(n int) int
|
||||
}
|
||||
|
||||
func randUTF8RuneMerkle(r randyMerkle) rune {
|
||||
ru := r.Intn(62)
|
||||
if ru < 10 {
|
||||
return rune(ru + 48)
|
||||
} else if ru < 36 {
|
||||
return rune(ru + 55)
|
||||
}
|
||||
return rune(ru + 61)
|
||||
}
|
||||
func randStringMerkle(r randyMerkle) string {
|
||||
v5 := r.Intn(100)
|
||||
tmps := make([]rune, v5)
|
||||
for i := 0; i < v5; i++ {
|
||||
tmps[i] = randUTF8RuneMerkle(r)
|
||||
}
|
||||
return string(tmps)
|
||||
}
|
||||
func randUnrecognizedMerkle(r randyMerkle, maxFieldNumber int) (dAtA []byte) {
|
||||
l := r.Intn(5)
|
||||
for i := 0; i < l; i++ {
|
||||
wire := r.Intn(4)
|
||||
if wire == 3 {
|
||||
wire = 5
|
||||
}
|
||||
fieldNumber := maxFieldNumber + r.Intn(100)
|
||||
dAtA = randFieldMerkle(dAtA, r, fieldNumber, wire)
|
||||
}
|
||||
return dAtA
|
||||
}
|
||||
func randFieldMerkle(dAtA []byte, r randyMerkle, fieldNumber int, wire int) []byte {
|
||||
key := uint32(fieldNumber)<<3 | uint32(wire)
|
||||
switch wire {
|
||||
case 0:
|
||||
dAtA = encodeVarintPopulateMerkle(dAtA, uint64(key))
|
||||
v6 := r.Int63()
|
||||
if r.Intn(2) == 0 {
|
||||
v6 *= -1
|
||||
}
|
||||
dAtA = encodeVarintPopulateMerkle(dAtA, uint64(v6))
|
||||
case 1:
|
||||
dAtA = encodeVarintPopulateMerkle(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)))
|
||||
case 2:
|
||||
dAtA = encodeVarintPopulateMerkle(dAtA, uint64(key))
|
||||
ll := r.Intn(100)
|
||||
dAtA = encodeVarintPopulateMerkle(dAtA, uint64(ll))
|
||||
for j := 0; j < ll; j++ {
|
||||
dAtA = append(dAtA, byte(r.Intn(256)))
|
||||
}
|
||||
default:
|
||||
dAtA = encodeVarintPopulateMerkle(dAtA, uint64(key))
|
||||
dAtA = append(dAtA, byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)), byte(r.Intn(256)))
|
||||
}
|
||||
return dAtA
|
||||
}
|
||||
func encodeVarintPopulateMerkle(dAtA []byte, v uint64) []byte {
|
||||
for v >= 1<<7 {
|
||||
dAtA = append(dAtA, uint8(uint64(v)&0x7f|0x80))
|
||||
v >>= 7
|
||||
}
|
||||
dAtA = append(dAtA, uint8(v))
|
||||
return dAtA
|
||||
}
|
||||
func (m *ProofOp) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
l = len(m.Type)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovMerkle(uint64(l))
|
||||
}
|
||||
l = len(m.Key)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovMerkle(uint64(l))
|
||||
}
|
||||
l = len(m.Data)
|
||||
if l > 0 {
|
||||
n += 1 + l + sovMerkle(uint64(l))
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (m *Proof) Size() (n int) {
|
||||
var l int
|
||||
_ = l
|
||||
if len(m.Ops) > 0 {
|
||||
for _, e := range m.Ops {
|
||||
l = e.Size()
|
||||
n += 1 + l + sovMerkle(uint64(l))
|
||||
}
|
||||
}
|
||||
if m.XXX_unrecognized != nil {
|
||||
n += len(m.XXX_unrecognized)
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func sovMerkle(x uint64) (n int) {
|
||||
for {
|
||||
n++
|
||||
x >>= 7
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
func sozMerkle(x uint64) (n int) {
|
||||
return sovMerkle(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||
}
|
||||
func (m *ProofOp) 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 ErrIntOverflowMerkle
|
||||
}
|
||||
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: ProofOp: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: ProofOp: 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 ErrIntOverflowMerkle
|
||||
}
|
||||
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 ErrInvalidLengthMerkle
|
||||
}
|
||||
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 Key", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthMerkle
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Key = append(m.Key[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.Key == nil {
|
||||
m.Key = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Data", wireType)
|
||||
}
|
||||
var byteLen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
byteLen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if byteLen < 0 {
|
||||
return ErrInvalidLengthMerkle
|
||||
}
|
||||
postIndex := iNdEx + byteLen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...)
|
||||
if m.Data == nil {
|
||||
m.Data = []byte{}
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipMerkle(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthMerkle
|
||||
}
|
||||
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 *Proof) 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 ErrIntOverflowMerkle
|
||||
}
|
||||
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: Proof: wiretype end group for non-group")
|
||||
}
|
||||
if fieldNum <= 0 {
|
||||
return fmt.Errorf("proto: Proof: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||
}
|
||||
switch fieldNum {
|
||||
case 1:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Ops", wireType)
|
||||
}
|
||||
var msglen int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
msglen |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if msglen < 0 {
|
||||
return ErrInvalidLengthMerkle
|
||||
}
|
||||
postIndex := iNdEx + msglen
|
||||
if postIndex > l {
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
m.Ops = append(m.Ops, ProofOp{})
|
||||
if err := m.Ops[len(m.Ops)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipMerkle(dAtA[iNdEx:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if skippy < 0 {
|
||||
return ErrInvalidLengthMerkle
|
||||
}
|
||||
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 skipMerkle(dAtA []byte) (n int, err error) {
|
||||
l := len(dAtA)
|
||||
iNdEx := 0
|
||||
for iNdEx < l {
|
||||
var wire uint64
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
wire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
wireType := int(wire & 0x7)
|
||||
switch wireType {
|
||||
case 0:
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
iNdEx++
|
||||
if dAtA[iNdEx-1] < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 1:
|
||||
iNdEx += 8
|
||||
return iNdEx, nil
|
||||
case 2:
|
||||
var length int
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
length |= (int(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
iNdEx += length
|
||||
if length < 0 {
|
||||
return 0, ErrInvalidLengthMerkle
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 3:
|
||||
for {
|
||||
var innerWire uint64
|
||||
var start int = iNdEx
|
||||
for shift := uint(0); ; shift += 7 {
|
||||
if shift >= 64 {
|
||||
return 0, ErrIntOverflowMerkle
|
||||
}
|
||||
if iNdEx >= l {
|
||||
return 0, io.ErrUnexpectedEOF
|
||||
}
|
||||
b := dAtA[iNdEx]
|
||||
iNdEx++
|
||||
innerWire |= (uint64(b) & 0x7F) << shift
|
||||
if b < 0x80 {
|
||||
break
|
||||
}
|
||||
}
|
||||
innerWireType := int(innerWire & 0x7)
|
||||
if innerWireType == 4 {
|
||||
break
|
||||
}
|
||||
next, err := skipMerkle(dAtA[start:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iNdEx = start + next
|
||||
}
|
||||
return iNdEx, nil
|
||||
case 4:
|
||||
return iNdEx, nil
|
||||
case 5:
|
||||
iNdEx += 4
|
||||
return iNdEx, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
|
||||
}
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidLengthMerkle = fmt.Errorf("proto: negative length found during unmarshaling")
|
||||
ErrIntOverflowMerkle = fmt.Errorf("proto: integer overflow")
|
||||
)
|
||||
|
||||
func init() { proto.RegisterFile("crypto/merkle/merkle.proto", fileDescriptor_merkle_5d3f6051907285da) }
|
||||
|
||||
var fileDescriptor_merkle_5d3f6051907285da = []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,
|
||||
0xf9, 0x42, 0x6c, 0x10, 0x9e, 0x94, 0x6e, 0x7a, 0x66, 0x49, 0x46, 0x69, 0x92, 0x5e, 0x72, 0x7e,
|
||||
0xae, 0x7e, 0x7a, 0x7e, 0x7a, 0xbe, 0x3e, 0x58, 0x3a, 0xa9, 0x34, 0x0d, 0xcc, 0x03, 0x73, 0xc0,
|
||||
0x2c, 0x88, 0x36, 0x25, 0x67, 0x2e, 0xf6, 0x80, 0xa2, 0xfc, 0xfc, 0x34, 0xff, 0x02, 0x21, 0x21,
|
||||
0x2e, 0x96, 0x92, 0xca, 0x82, 0x54, 0x09, 0x46, 0x05, 0x46, 0x0d, 0xce, 0x20, 0x30, 0x5b, 0x48,
|
||||
0x80, 0x8b, 0x39, 0x3b, 0xb5, 0x52, 0x82, 0x49, 0x81, 0x51, 0x83, 0x27, 0x08, 0xc4, 0x04, 0xa9,
|
||||
0x4a, 0x49, 0x2c, 0x49, 0x94, 0x60, 0x06, 0x0b, 0x81, 0xd9, 0x4a, 0x06, 0x5c, 0xac, 0x60, 0x43,
|
||||
0x84, 0xd4, 0xb9, 0x98, 0xf3, 0x0b, 0x8a, 0x25, 0x18, 0x15, 0x98, 0x35, 0xb8, 0x8d, 0xf8, 0xf5,
|
||||
0xa0, 0x0e, 0x84, 0x5a, 0xe0, 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x48, 0x85, 0x93, 0xc8,
|
||||
0x8f, 0x87, 0x72, 0x8c, 0x2b, 0x1e, 0xc9, 0x31, 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c,
|
||||
0xe3, 0x83, 0x47, 0x72, 0x8c, 0x49, 0x6c, 0x60, 0x37, 0x19, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff,
|
||||
0xb9, 0x2b, 0x0f, 0xd1, 0xe8, 0x00, 0x00, 0x00,
|
||||
}
|
||||
30
crypto/merkle/merkle.proto
Normal file
30
crypto/merkle/merkle.proto
Normal file
@@ -0,0 +1,30 @@
|
||||
syntax = "proto3";
|
||||
package merkle;
|
||||
|
||||
// For more information on gogo.proto, see:
|
||||
// https://github.com/gogo/protobuf/blob/master/extensions.md
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
|
||||
option (gogoproto.marshaler_all) = true;
|
||||
option (gogoproto.unmarshaler_all) = true;
|
||||
option (gogoproto.sizer_all) = true;
|
||||
|
||||
option (gogoproto.populate_all) = true;
|
||||
option (gogoproto.equal_all) = true;
|
||||
|
||||
//----------------------------------------
|
||||
// Message types
|
||||
|
||||
// ProofOp defines an operation used for calculating Merkle root
|
||||
// The data could be arbitrary format, providing nessecary data
|
||||
// for example neighbouring node hash
|
||||
message ProofOp {
|
||||
string type = 1;
|
||||
bytes key = 2;
|
||||
bytes data = 3;
|
||||
}
|
||||
|
||||
// Proof is Merkle proof defined by the list of ProofOps
|
||||
message Proof {
|
||||
repeated ProofOp ops = 1 [(gogoproto.nullable)=false];
|
||||
}
|
||||
134
crypto/merkle/proof.go
Normal file
134
crypto/merkle/proof.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
//----------------------------------------
|
||||
// ProofOp gets converted to an instance of ProofOperator:
|
||||
|
||||
// ProofOperator is a layer for calculating intermediate Merkle roots
|
||||
// when a series of Merkle trees are chained together.
|
||||
// Run() takes leaf values from a tree and returns the Merkle
|
||||
// root for the corresponding tree. It takes and returns a list of bytes
|
||||
// to allow multiple leaves to be part of a single proof, for instance in a range proof.
|
||||
// ProofOp() encodes the ProofOperator in a generic way so it can later be
|
||||
// decoded with OpDecoder.
|
||||
type ProofOperator interface {
|
||||
Run([][]byte) ([][]byte, error)
|
||||
GetKey() []byte
|
||||
ProofOp() ProofOp
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// Operations on a list of ProofOperators
|
||||
|
||||
// ProofOperators is a slice of ProofOperator(s).
|
||||
// Each operator will be applied to the input value sequentially
|
||||
// and the last Merkle root will be verified with already known data
|
||||
type ProofOperators []ProofOperator
|
||||
|
||||
func (poz ProofOperators) VerifyValue(root []byte, keypath string, value []byte) (err error) {
|
||||
return poz.Verify(root, keypath, [][]byte{value})
|
||||
}
|
||||
|
||||
func (poz ProofOperators) Verify(root []byte, keypath string, args [][]byte) (err error) {
|
||||
keys, err := KeyPathToKeys(keypath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for i, op := range poz {
|
||||
key := op.GetKey()
|
||||
if len(key) != 0 {
|
||||
if !bytes.Equal(keys[0], key) {
|
||||
return cmn.NewError("Key mismatch on operation #%d: expected %+v but %+v", i, []byte(keys[0]), []byte(key))
|
||||
}
|
||||
keys = keys[1:]
|
||||
}
|
||||
args, err = op.Run(args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !bytes.Equal(root, args[0]) {
|
||||
return cmn.NewError("Calculated root hash is invalid: expected %+v but %+v", root, args[0])
|
||||
}
|
||||
if len(keys) != 0 {
|
||||
return cmn.NewError("Keypath not consumed all")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
// ProofRuntime - main entrypoint
|
||||
|
||||
type OpDecoder func(ProofOp) (ProofOperator, error)
|
||||
|
||||
type ProofRuntime struct {
|
||||
decoders map[string]OpDecoder
|
||||
}
|
||||
|
||||
func NewProofRuntime() *ProofRuntime {
|
||||
return &ProofRuntime{
|
||||
decoders: make(map[string]OpDecoder),
|
||||
}
|
||||
}
|
||||
|
||||
func (prt *ProofRuntime) RegisterOpDecoder(typ string, dec OpDecoder) {
|
||||
_, ok := prt.decoders[typ]
|
||||
if ok {
|
||||
panic("already registered for type " + typ)
|
||||
}
|
||||
prt.decoders[typ] = dec
|
||||
}
|
||||
|
||||
func (prt *ProofRuntime) Decode(pop ProofOp) (ProofOperator, error) {
|
||||
decoder := prt.decoders[pop.Type]
|
||||
if decoder == nil {
|
||||
return nil, cmn.NewError("unrecognized proof type %v", pop.Type)
|
||||
}
|
||||
return decoder(pop)
|
||||
}
|
||||
|
||||
func (prt *ProofRuntime) DecodeProof(proof *Proof) (ProofOperators, error) {
|
||||
var poz ProofOperators
|
||||
for _, pop := range proof.Ops {
|
||||
operator, err := prt.Decode(pop)
|
||||
if err != nil {
|
||||
return nil, cmn.ErrorWrap(err, "decoding a proof operator")
|
||||
}
|
||||
poz = append(poz, operator)
|
||||
}
|
||||
return poz, nil
|
||||
}
|
||||
|
||||
func (prt *ProofRuntime) VerifyValue(proof *Proof, root []byte, keypath string, value []byte) (err error) {
|
||||
return prt.Verify(proof, root, keypath, [][]byte{value})
|
||||
}
|
||||
|
||||
// TODO In the long run we'll need a method of classifcation of ops,
|
||||
// whether existence or absence or perhaps a third?
|
||||
func (prt *ProofRuntime) VerifyAbsence(proof *Proof, root []byte, keypath string) (err error) {
|
||||
return prt.Verify(proof, root, keypath, nil)
|
||||
}
|
||||
|
||||
func (prt *ProofRuntime) Verify(proof *Proof, root []byte, keypath string, args [][]byte) (err error) {
|
||||
poz, err := prt.DecodeProof(proof)
|
||||
if err != nil {
|
||||
return cmn.ErrorWrap(err, "decoding proof")
|
||||
}
|
||||
return poz.Verify(root, keypath, args)
|
||||
}
|
||||
|
||||
// DefaultProofRuntime only knows about Simple value
|
||||
// proofs.
|
||||
// To use e.g. IAVL proofs, register op-decoders as
|
||||
// defined in the IAVL package.
|
||||
func DefaultProofRuntime() (prt *ProofRuntime) {
|
||||
prt = NewProofRuntime()
|
||||
prt.RegisterOpDecoder(ProofOpSimpleValue, SimpleValueOpDecoder)
|
||||
return
|
||||
}
|
||||
111
crypto/merkle/proof_key_path.go
Normal file
111
crypto/merkle/proof_key_path.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
For generalized Merkle proofs, each layer of the proof may require an
|
||||
optional key. The key may be encoded either by URL-encoding or
|
||||
(upper-case) hex-encoding.
|
||||
TODO: In the future, more encodings may be supported, like base32 (e.g.
|
||||
/32:)
|
||||
|
||||
For example, for a Cosmos-SDK application where the first two proof layers
|
||||
are SimpleValueOps, and the third proof layer is an IAVLValueOp, the keys
|
||||
might look like:
|
||||
|
||||
0: []byte("App")
|
||||
1: []byte("IBC")
|
||||
2: []byte{0x01, 0x02, 0x03}
|
||||
|
||||
Assuming that we know that the first two layers are always ASCII texts, we
|
||||
probably want to use URLEncoding for those, whereas the third layer will
|
||||
require HEX encoding for efficient representation.
|
||||
|
||||
kp := new(KeyPath)
|
||||
kp.AppendKey([]byte("App"), KeyEncodingURL)
|
||||
kp.AppendKey([]byte("IBC"), KeyEncodingURL)
|
||||
kp.AppendKey([]byte{0x01, 0x02, 0x03}, KeyEncodingURL)
|
||||
kp.String() // Should return "/App/IBC/x:010203"
|
||||
|
||||
NOTE: Key paths must begin with a `/`.
|
||||
|
||||
NOTE: All encodings *MUST* work compatibly, such that you can choose to use
|
||||
whatever encoding, and the decoded keys will always be the same. In other
|
||||
words, it's just as good to encode all three keys using URL encoding or HEX
|
||||
encoding... it just wouldn't be optimal in terms of readability or space
|
||||
efficiency.
|
||||
|
||||
NOTE: Punycode will never be supported here, because not all values can be
|
||||
decoded. For example, no string decodes to the string "xn--blah" in
|
||||
Punycode.
|
||||
|
||||
*/
|
||||
|
||||
type keyEncoding int
|
||||
|
||||
const (
|
||||
KeyEncodingURL keyEncoding = iota
|
||||
KeyEncodingHex
|
||||
KeyEncodingMax // Number of known encodings. Used for testing
|
||||
)
|
||||
|
||||
type Key struct {
|
||||
name []byte
|
||||
enc keyEncoding
|
||||
}
|
||||
|
||||
type KeyPath []Key
|
||||
|
||||
func (pth KeyPath) AppendKey(key []byte, enc keyEncoding) KeyPath {
|
||||
return append(pth, Key{key, enc})
|
||||
}
|
||||
|
||||
func (pth KeyPath) String() string {
|
||||
res := ""
|
||||
for _, key := range pth {
|
||||
switch key.enc {
|
||||
case KeyEncodingURL:
|
||||
res += "/" + url.PathEscape(string(key.name))
|
||||
case KeyEncodingHex:
|
||||
res += "/x:" + fmt.Sprintf("%X", key.name)
|
||||
default:
|
||||
panic("unexpected key encoding type")
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Decode a path to a list of keys. Path must begin with `/`.
|
||||
// Each key must use a known encoding.
|
||||
func KeyPathToKeys(path string) (keys [][]byte, err error) {
|
||||
if path == "" || path[0] != '/' {
|
||||
return nil, cmn.NewError("key path string must start with a forward slash '/'")
|
||||
}
|
||||
parts := strings.Split(path[1:], "/")
|
||||
keys = make([][]byte, len(parts))
|
||||
for i, part := range parts {
|
||||
if strings.HasPrefix(part, "x:") {
|
||||
hexPart := part[2:]
|
||||
key, err := hex.DecodeString(hexPart)
|
||||
if err != nil {
|
||||
return nil, cmn.ErrorWrap(err, "decoding hex-encoded part #%d: /%s", i, part)
|
||||
}
|
||||
keys[i] = key
|
||||
} else {
|
||||
key, err := url.PathUnescape(part)
|
||||
if err != nil {
|
||||
return nil, cmn.ErrorWrap(err, "decoding url-encoded part #%d: /%s", i, part)
|
||||
}
|
||||
keys[i] = []byte(key) // TODO Test this with random bytes, I'm not sure that it works for arbitrary bytes...
|
||||
}
|
||||
}
|
||||
return keys, nil
|
||||
}
|
||||
41
crypto/merkle/proof_key_path_test.go
Normal file
41
crypto/merkle/proof_key_path_test.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestKeyPath(t *testing.T) {
|
||||
var path KeyPath
|
||||
keys := make([][]byte, 10)
|
||||
alphanum := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
for d := 0; d < 1e4; d++ {
|
||||
path = nil
|
||||
|
||||
for i := range keys {
|
||||
enc := keyEncoding(rand.Intn(int(KeyEncodingMax)))
|
||||
keys[i] = make([]byte, rand.Uint32()%20)
|
||||
switch enc {
|
||||
case KeyEncodingURL:
|
||||
for j := range keys[i] {
|
||||
keys[i][j] = alphanum[rand.Intn(len(alphanum))]
|
||||
}
|
||||
case KeyEncodingHex:
|
||||
rand.Read(keys[i])
|
||||
default:
|
||||
panic("Unexpected encoding")
|
||||
}
|
||||
path = path.AppendKey(keys[i], enc)
|
||||
}
|
||||
|
||||
res, err := KeyPathToKeys(path.String())
|
||||
require.Nil(t, err)
|
||||
|
||||
for i, key := range keys {
|
||||
require.Equal(t, key, res[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
91
crypto/merkle/proof_simple_value.go
Normal file
91
crypto/merkle/proof_simple_value.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
const ProofOpSimpleValue = "simple:v"
|
||||
|
||||
// SimpleValueOp takes a key and a single value as argument and
|
||||
// produces the root hash. The corresponding tree structure is
|
||||
// the SimpleMap tree. SimpleMap takes a Hasher, and currently
|
||||
// Tendermint uses aminoHasher. SimpleValueOp should support
|
||||
// the hash function as used in aminoHasher. TODO support
|
||||
// additional hash functions here as options/args to this
|
||||
// operator.
|
||||
//
|
||||
// If the produced root hash matches the expected hash, the
|
||||
// proof is good.
|
||||
type SimpleValueOp struct {
|
||||
// Encoded in ProofOp.Key.
|
||||
key []byte
|
||||
|
||||
// To encode in ProofOp.Data
|
||||
Proof *SimpleProof `json:"simple_proof"`
|
||||
}
|
||||
|
||||
var _ ProofOperator = SimpleValueOp{}
|
||||
|
||||
func NewSimpleValueOp(key []byte, proof *SimpleProof) SimpleValueOp {
|
||||
return SimpleValueOp{
|
||||
key: key,
|
||||
Proof: proof,
|
||||
}
|
||||
}
|
||||
|
||||
func SimpleValueOpDecoder(pop ProofOp) (ProofOperator, error) {
|
||||
if pop.Type != ProofOpSimpleValue {
|
||||
return nil, cmn.NewError("unexpected ProofOp.Type; got %v, want %v", pop.Type, ProofOpSimpleValue)
|
||||
}
|
||||
var op SimpleValueOp // a bit strange as we'll discard this, but it works.
|
||||
err := cdc.UnmarshalBinaryLengthPrefixed(pop.Data, &op)
|
||||
if err != nil {
|
||||
return nil, cmn.ErrorWrap(err, "decoding ProofOp.Data into SimpleValueOp")
|
||||
}
|
||||
return NewSimpleValueOp(pop.Key, op.Proof), nil
|
||||
}
|
||||
|
||||
func (op SimpleValueOp) ProofOp() ProofOp {
|
||||
bz := cdc.MustMarshalBinaryLengthPrefixed(op)
|
||||
return ProofOp{
|
||||
Type: ProofOpSimpleValue,
|
||||
Key: op.key,
|
||||
Data: bz,
|
||||
}
|
||||
}
|
||||
|
||||
func (op SimpleValueOp) String() string {
|
||||
return fmt.Sprintf("SimpleValueOp{%v}", op.GetKey())
|
||||
}
|
||||
|
||||
func (op SimpleValueOp) Run(args [][]byte) ([][]byte, error) {
|
||||
if len(args) != 1 {
|
||||
return nil, cmn.NewError("expected 1 arg, got %v", len(args))
|
||||
}
|
||||
value := args[0]
|
||||
hasher := tmhash.New()
|
||||
hasher.Write(value) // does not error
|
||||
vhash := hasher.Sum(nil)
|
||||
|
||||
// Wrap <op.Key, vhash> to hash the KVPair.
|
||||
hasher = tmhash.New()
|
||||
encodeByteSlice(hasher, []byte(op.key)) // does not error
|
||||
encodeByteSlice(hasher, []byte(vhash)) // does not error
|
||||
kvhash := hasher.Sum(nil)
|
||||
|
||||
if !bytes.Equal(kvhash, op.Proof.LeafHash) {
|
||||
return nil, cmn.NewError("leaf hash mismatch: want %X got %X", op.Proof.LeafHash, kvhash)
|
||||
}
|
||||
|
||||
return [][]byte{
|
||||
op.Proof.ComputeRootHash(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (op SimpleValueOp) GetKey() []byte {
|
||||
return op.key
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
@@ -20,14 +23,15 @@ func newSimpleMap() *simpleMap {
|
||||
}
|
||||
}
|
||||
|
||||
// Set hashes the key and value and appends it to the kv pairs.
|
||||
func (sm *simpleMap) Set(key string, value Hasher) {
|
||||
// Set creates a kv pair of the key and the hash of the value,
|
||||
// and then appends it to simpleMap's kv pairs.
|
||||
func (sm *simpleMap) Set(key string, value []byte) {
|
||||
sm.sorted = false
|
||||
|
||||
// The value is hashed, so you can
|
||||
// check for equality with a cached value (say)
|
||||
// and make a determination to fetch or not.
|
||||
vhash := value.Hash()
|
||||
vhash := tmhash.Sum(value)
|
||||
|
||||
sm.kvs = append(sm.kvs, cmn.KVPair{
|
||||
Key: []byte(key),
|
||||
@@ -66,23 +70,25 @@ func (sm *simpleMap) KVPairs() cmn.KVPairs {
|
||||
// then hashed.
|
||||
type KVPair cmn.KVPair
|
||||
|
||||
func (kv KVPair) Hash() []byte {
|
||||
hasher := tmhash.New()
|
||||
err := encodeByteSlice(hasher, kv.Key)
|
||||
// Bytes returns key || value, with both the
|
||||
// key and value length prefixed.
|
||||
func (kv KVPair) Bytes() []byte {
|
||||
var b bytes.Buffer
|
||||
err := amino.EncodeByteSlice(&b, kv.Key)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err = encodeByteSlice(hasher, kv.Value)
|
||||
err = amino.EncodeByteSlice(&b, kv.Value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return hasher.Sum(nil)
|
||||
return b.Bytes()
|
||||
}
|
||||
|
||||
func hashKVPairs(kvs cmn.KVPairs) []byte {
|
||||
kvsH := make([]Hasher, len(kvs))
|
||||
kvsH := make([][]byte, len(kvs))
|
||||
for i, kvp := range kvs {
|
||||
kvsH[i] = KVPair(kvp)
|
||||
kvsH[i] = KVPair(kvp).Bytes()
|
||||
}
|
||||
return SimpleHashFromHashers(kvsH)
|
||||
return SimpleHashFromByteSlices(kvsH)
|
||||
}
|
||||
|
||||
@@ -5,50 +5,29 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
)
|
||||
|
||||
type strHasher string
|
||||
|
||||
func (str strHasher) Hash() []byte {
|
||||
return tmhash.Sum([]byte(str))
|
||||
}
|
||||
|
||||
func TestSimpleMap(t *testing.T) {
|
||||
{
|
||||
db := newSimpleMap()
|
||||
db.Set("key1", strHasher("value1"))
|
||||
assert.Equal(t, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||
tests := []struct {
|
||||
keys []string
|
||||
values []string // each string gets converted to []byte in test
|
||||
want string
|
||||
}{
|
||||
{[]string{"key1"}, []string{"value1"}, "fa9bc106ffd932d919bee935ceb6cf2b3dd72d8f"},
|
||||
{[]string{"key1"}, []string{"value2"}, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8"},
|
||||
// swap order with 2 keys
|
||||
{[]string{"key1", "key2"}, []string{"value1", "value2"}, "eff12d1c703a1022ab509287c0f196130123d786"},
|
||||
{[]string{"key2", "key1"}, []string{"value2", "value1"}, "eff12d1c703a1022ab509287c0f196130123d786"},
|
||||
// swap order with 3 keys
|
||||
{[]string{"key1", "key2", "key3"}, []string{"value1", "value2", "value3"}, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26"},
|
||||
{[]string{"key1", "key3", "key2"}, []string{"value1", "value3", "value2"}, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26"},
|
||||
}
|
||||
{
|
||||
for i, tc := range tests {
|
||||
db := newSimpleMap()
|
||||
db.Set("key1", strHasher("value2"))
|
||||
assert.Equal(t, "e00e7dcfe54e9fafef5111e813a587f01ba9c3e8", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||
}
|
||||
{
|
||||
db := newSimpleMap()
|
||||
db.Set("key1", strHasher("value1"))
|
||||
db.Set("key2", strHasher("value2"))
|
||||
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||
}
|
||||
{
|
||||
db := newSimpleMap()
|
||||
db.Set("key2", strHasher("value2")) // NOTE: out of order
|
||||
db.Set("key1", strHasher("value1"))
|
||||
assert.Equal(t, "eff12d1c703a1022ab509287c0f196130123d786", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||
}
|
||||
{
|
||||
db := newSimpleMap()
|
||||
db.Set("key1", strHasher("value1"))
|
||||
db.Set("key2", strHasher("value2"))
|
||||
db.Set("key3", strHasher("value3"))
|
||||
assert.Equal(t, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||
}
|
||||
{
|
||||
db := newSimpleMap()
|
||||
db.Set("key2", strHasher("value2")) // NOTE: out of order
|
||||
db.Set("key1", strHasher("value1"))
|
||||
db.Set("key3", strHasher("value3"))
|
||||
assert.Equal(t, "b2c62a277c08dbd2ad73ca53cd1d6bfdf5830d26", fmt.Sprintf("%x", db.Hash()), "Hash didn't match")
|
||||
for i := 0; i < len(tc.keys); i++ {
|
||||
db.Set(tc.keys[i], []byte(tc.values[i]))
|
||||
}
|
||||
got := db.Hash()
|
||||
assert.Equal(t, tc.want, fmt.Sprintf("%x", got), "Hash didn't match on tc %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,23 +2,39 @@ package merkle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
// SimpleProof represents a simple merkle proof.
|
||||
// SimpleProof represents a simple Merkle proof.
|
||||
// NOTE: The convention for proofs is to include leaf hashes but to
|
||||
// exclude the root hash.
|
||||
// This convention is implemented across IAVL range proofs as well.
|
||||
// Keep this consistent unless there's a very good reason to change
|
||||
// everything. This also affects the generalized proof system as
|
||||
// well.
|
||||
type SimpleProof struct {
|
||||
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child.
|
||||
Total int `json:"total"` // Total number of items.
|
||||
Index int `json:"index"` // Index of item to prove.
|
||||
LeafHash []byte `json:"leaf_hash"` // Hash of item value.
|
||||
Aunts [][]byte `json:"aunts"` // Hashes from leaf's sibling to a root's child.
|
||||
}
|
||||
|
||||
// SimpleProofsFromHashers computes inclusion proof for given items.
|
||||
// SimpleProofsFromByteSlices computes inclusion proof for given items.
|
||||
// proofs[0] is the proof for items[0].
|
||||
func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleProof) {
|
||||
trails, rootSPN := trailsFromHashers(items)
|
||||
func SimpleProofsFromByteSlices(items [][]byte) (rootHash []byte, proofs []*SimpleProof) {
|
||||
trails, rootSPN := trailsFromByteSlices(items)
|
||||
rootHash = rootSPN.Hash
|
||||
proofs = make([]*SimpleProof, len(items))
|
||||
for i, trail := range trails {
|
||||
proofs[i] = &SimpleProof{
|
||||
Aunts: trail.FlattenAunts(),
|
||||
Total: len(items),
|
||||
Index: i,
|
||||
LeafHash: trail.Hash,
|
||||
Aunts: trail.FlattenAunts(),
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -27,19 +43,19 @@ func SimpleProofsFromHashers(items []Hasher) (rootHash []byte, proofs []*SimpleP
|
||||
// SimpleProofsFromMap generates proofs from a map. The keys/values of the map will be used as the keys/values
|
||||
// in the underlying key-value pairs.
|
||||
// The keys are sorted before the proofs are computed.
|
||||
func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs map[string]*SimpleProof, keys []string) {
|
||||
func SimpleProofsFromMap(m map[string][]byte) (rootHash []byte, proofs map[string]*SimpleProof, keys []string) {
|
||||
sm := newSimpleMap()
|
||||
for k, v := range m {
|
||||
sm.Set(k, v)
|
||||
}
|
||||
sm.Sort()
|
||||
kvs := sm.kvs
|
||||
kvsH := make([]Hasher, 0, len(kvs))
|
||||
for _, kvp := range kvs {
|
||||
kvsH = append(kvsH, KVPair(kvp))
|
||||
kvsBytes := make([][]byte, len(kvs))
|
||||
for i, kvp := range kvs {
|
||||
kvsBytes[i] = KVPair(kvp).Bytes()
|
||||
}
|
||||
|
||||
rootHash, proofList := SimpleProofsFromHashers(kvsH)
|
||||
rootHash, proofList := SimpleProofsFromByteSlices(kvsBytes)
|
||||
proofs = make(map[string]*SimpleProof)
|
||||
keys = make([]string, len(proofList))
|
||||
for i, kvp := range kvs {
|
||||
@@ -49,11 +65,33 @@ func SimpleProofsFromMap(m map[string]Hasher) (rootHash []byte, proofs map[strin
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that leafHash is a leaf hash of the simple-merkle-tree
|
||||
// which hashes to rootHash.
|
||||
func (sp *SimpleProof) Verify(index int, total int, leafHash []byte, rootHash []byte) bool {
|
||||
computedHash := computeHashFromAunts(index, total, leafHash, sp.Aunts)
|
||||
return computedHash != nil && bytes.Equal(computedHash, rootHash)
|
||||
// Verify that the SimpleProof proves the root hash.
|
||||
// Check sp.Index/sp.Total manually if needed
|
||||
func (sp *SimpleProof) Verify(rootHash []byte, leafHash []byte) error {
|
||||
if sp.Total < 0 {
|
||||
return errors.New("Proof total must be positive")
|
||||
}
|
||||
if sp.Index < 0 {
|
||||
return errors.New("Proof index cannot be negative")
|
||||
}
|
||||
if !bytes.Equal(sp.LeafHash, leafHash) {
|
||||
return cmn.NewError("invalid leaf hash: wanted %X got %X", leafHash, sp.LeafHash)
|
||||
}
|
||||
computedHash := sp.ComputeRootHash()
|
||||
if !bytes.Equal(computedHash, rootHash) {
|
||||
return cmn.NewError("invalid root hash: wanted %X got %X", rootHash, computedHash)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Compute the root hash given a leaf hash. Does not verify the result.
|
||||
func (sp *SimpleProof) ComputeRootHash() []byte {
|
||||
return computeHashFromAunts(
|
||||
sp.Index,
|
||||
sp.Total,
|
||||
sp.LeafHash,
|
||||
sp.Aunts,
|
||||
)
|
||||
}
|
||||
|
||||
// String implements the stringer interface for SimpleProof.
|
||||
@@ -96,13 +134,13 @@ func computeHashFromAunts(index int, total int, leafHash []byte, innerHashes [][
|
||||
if leftHash == nil {
|
||||
return nil
|
||||
}
|
||||
return SimpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
|
||||
return simpleHashFromTwoHashes(leftHash, innerHashes[len(innerHashes)-1])
|
||||
}
|
||||
rightHash := computeHashFromAunts(index-numLeft, total-numLeft, leafHash, innerHashes[:len(innerHashes)-1])
|
||||
if rightHash == nil {
|
||||
return nil
|
||||
}
|
||||
return SimpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
|
||||
return simpleHashFromTwoHashes(innerHashes[len(innerHashes)-1], rightHash)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,18 +176,18 @@ func (spn *SimpleProofNode) FlattenAunts() [][]byte {
|
||||
|
||||
// trails[0].Hash is the leaf hash for items[0].
|
||||
// trails[i].Parent.Parent....Parent == root for all i.
|
||||
func trailsFromHashers(items []Hasher) (trails []*SimpleProofNode, root *SimpleProofNode) {
|
||||
func trailsFromByteSlices(items [][]byte) (trails []*SimpleProofNode, root *SimpleProofNode) {
|
||||
// Recursive impl.
|
||||
switch len(items) {
|
||||
case 0:
|
||||
return nil, nil
|
||||
case 1:
|
||||
trail := &SimpleProofNode{items[0].Hash(), nil, nil, nil}
|
||||
trail := &SimpleProofNode{tmhash.Sum(items[0]), nil, nil, nil}
|
||||
return []*SimpleProofNode{trail}, trail
|
||||
default:
|
||||
lefts, leftRoot := trailsFromHashers(items[:(len(items)+1)/2])
|
||||
rights, rightRoot := trailsFromHashers(items[(len(items)+1)/2:])
|
||||
rootHash := SimpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
|
||||
lefts, leftRoot := trailsFromByteSlices(items[:(len(items)+1)/2])
|
||||
rights, rightRoot := trailsFromByteSlices(items[(len(items)+1)/2:])
|
||||
rootHash := simpleHashFromTwoHashes(leftRoot.Hash, rightRoot.Hash)
|
||||
root := &SimpleProofNode{rootHash, nil, nil, nil}
|
||||
leftRoot.Parent = root
|
||||
leftRoot.Right = rightRoot
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
)
|
||||
|
||||
// SimpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
|
||||
func SimpleHashFromTwoHashes(left, right []byte) []byte {
|
||||
// simpleHashFromTwoHashes is the basic operation of the Merkle tree: Hash(left | right).
|
||||
func simpleHashFromTwoHashes(left, right []byte) []byte {
|
||||
var hasher = tmhash.New()
|
||||
err := encodeByteSlice(hasher, left)
|
||||
if err != nil {
|
||||
@@ -18,41 +18,29 @@ func SimpleHashFromTwoHashes(left, right []byte) []byte {
|
||||
return hasher.Sum(nil)
|
||||
}
|
||||
|
||||
// SimpleHashFromHashers computes a Merkle tree from items that can be hashed.
|
||||
func SimpleHashFromHashers(items []Hasher) []byte {
|
||||
hashes := make([][]byte, len(items))
|
||||
for i, item := range items {
|
||||
hash := item.Hash()
|
||||
hashes[i] = hash
|
||||
// SimpleHashFromByteSlices computes a Merkle tree where the leaves are the byte slice,
|
||||
// in the provided order.
|
||||
func SimpleHashFromByteSlices(items [][]byte) []byte {
|
||||
switch len(items) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return tmhash.Sum(items[0])
|
||||
default:
|
||||
left := SimpleHashFromByteSlices(items[:(len(items)+1)/2])
|
||||
right := SimpleHashFromByteSlices(items[(len(items)+1)/2:])
|
||||
return simpleHashFromTwoHashes(left, right)
|
||||
}
|
||||
return simpleHashFromHashes(hashes)
|
||||
}
|
||||
|
||||
// SimpleHashFromMap computes a Merkle tree from sorted map.
|
||||
// Like calling SimpleHashFromHashers with
|
||||
// `item = []byte(Hash(key) | Hash(value))`,
|
||||
// sorted by `item`.
|
||||
func SimpleHashFromMap(m map[string]Hasher) []byte {
|
||||
func SimpleHashFromMap(m map[string][]byte) []byte {
|
||||
sm := newSimpleMap()
|
||||
for k, v := range m {
|
||||
sm.Set(k, v)
|
||||
}
|
||||
return sm.Hash()
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
|
||||
// Expects hashes!
|
||||
func simpleHashFromHashes(hashes [][]byte) []byte {
|
||||
// Recursive impl.
|
||||
switch len(hashes) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
return hashes[0]
|
||||
default:
|
||||
left := simpleHashFromHashes(hashes[:(len(hashes)+1)/2])
|
||||
right := simpleHashFromHashes(hashes[(len(hashes)+1)/2:])
|
||||
return SimpleHashFromTwoHashes(left, right)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
. "github.com/tendermint/tendermint/libs/test"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type testItem []byte
|
||||
@@ -20,69 +21,52 @@ func TestSimpleProof(t *testing.T) {
|
||||
|
||||
total := 100
|
||||
|
||||
items := make([]Hasher, total)
|
||||
items := make([][]byte, total)
|
||||
for i := 0; i < total; i++ {
|
||||
items[i] = testItem(cmn.RandBytes(tmhash.Size))
|
||||
}
|
||||
|
||||
rootHash := SimpleHashFromHashers(items)
|
||||
rootHash := SimpleHashFromByteSlices(items)
|
||||
|
||||
rootHash2, proofs := SimpleProofsFromHashers(items)
|
||||
rootHash2, proofs := SimpleProofsFromByteSlices(items)
|
||||
|
||||
if !bytes.Equal(rootHash, rootHash2) {
|
||||
t.Errorf("Unmatched root hashes: %X vs %X", rootHash, rootHash2)
|
||||
}
|
||||
require.Equal(t, rootHash, rootHash2, "Unmatched root hashes: %X vs %X", rootHash, rootHash2)
|
||||
|
||||
// For each item, check the trail.
|
||||
for i, item := range items {
|
||||
itemHash := item.Hash()
|
||||
itemHash := tmhash.Sum(item)
|
||||
proof := proofs[i]
|
||||
|
||||
// Verify success
|
||||
ok := proof.Verify(i, total, itemHash, rootHash)
|
||||
if !ok {
|
||||
t.Errorf("Verification failed for index %v.", i)
|
||||
}
|
||||
// Check total/index
|
||||
require.Equal(t, proof.Index, i, "Unmatched indicies: %d vs %d", proof.Index, i)
|
||||
|
||||
// Wrong item index should make it fail
|
||||
{
|
||||
ok = proof.Verify((i+1)%total, total, itemHash, rootHash)
|
||||
if ok {
|
||||
t.Errorf("Expected verification to fail for wrong index %v.", i)
|
||||
}
|
||||
}
|
||||
require.Equal(t, proof.Total, total, "Unmatched totals: %d vs %d", proof.Total, total)
|
||||
|
||||
// Verify success
|
||||
err := proof.Verify(rootHash, itemHash)
|
||||
require.NoError(t, err, "Verificatior failed: %v.", err)
|
||||
|
||||
// Trail too long should make it fail
|
||||
origAunts := proof.Aunts
|
||||
proof.Aunts = append(proof.Aunts, cmn.RandBytes(32))
|
||||
{
|
||||
ok = proof.Verify(i, total, itemHash, rootHash)
|
||||
if ok {
|
||||
t.Errorf("Expected verification to fail for wrong trail length.")
|
||||
}
|
||||
}
|
||||
err = proof.Verify(rootHash, itemHash)
|
||||
require.Error(t, err, "Expected verification to fail for wrong trail length")
|
||||
|
||||
proof.Aunts = origAunts
|
||||
|
||||
// Trail too short should make it fail
|
||||
proof.Aunts = proof.Aunts[0 : len(proof.Aunts)-1]
|
||||
{
|
||||
ok = proof.Verify(i, total, itemHash, rootHash)
|
||||
if ok {
|
||||
t.Errorf("Expected verification to fail for wrong trail length.")
|
||||
}
|
||||
}
|
||||
err = proof.Verify(rootHash, itemHash)
|
||||
require.Error(t, err, "Expected verification to fail for wrong trail length")
|
||||
|
||||
proof.Aunts = origAunts
|
||||
|
||||
// Mutating the itemHash should make it fail.
|
||||
ok = proof.Verify(i, total, MutateByteSlice(itemHash), rootHash)
|
||||
if ok {
|
||||
t.Errorf("Expected verification to fail for mutated leaf hash")
|
||||
}
|
||||
err = proof.Verify(rootHash, MutateByteSlice(itemHash))
|
||||
require.Error(t, err, "Expected verification to fail for mutated leaf hash")
|
||||
|
||||
// Mutating the rootHash should make it fail.
|
||||
ok = proof.Verify(i, total, itemHash, MutateByteSlice(rootHash))
|
||||
if ok {
|
||||
t.Errorf("Expected verification to fail for mutated root hash")
|
||||
}
|
||||
err = proof.Verify(MutateByteSlice(rootHash), itemHash)
|
||||
require.Error(t, err, "Expected verification to fail for mutated root hash")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,11 +25,6 @@ type Tree interface {
|
||||
IterateRange(start []byte, end []byte, ascending bool, fx func(key []byte, value []byte) (stop bool)) (stopped bool)
|
||||
}
|
||||
|
||||
// Hasher represents a hashable piece of data which can be hashed in the Tree.
|
||||
type Hasher interface {
|
||||
Hash() []byte
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
// Uvarint length prefixed byteslice
|
||||
|
||||
12
crypto/merkle/wire.go
Normal file
12
crypto/merkle/wire.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package merkle
|
||||
|
||||
import (
|
||||
"github.com/tendermint/go-amino"
|
||||
)
|
||||
|
||||
var cdc *amino.Codec
|
||||
|
||||
func init() {
|
||||
cdc = amino.NewCodec()
|
||||
cdc.Seal()
|
||||
}
|
||||
233
crypto/multisig/bitarray/compact_bit_array.go
Normal file
233
crypto/multisig/bitarray/compact_bit_array.go
Normal file
@@ -0,0 +1,233 @@
|
||||
package bitarray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CompactBitArray is an implementation of a space efficient bit array.
|
||||
// This is used to ensure that the encoded data takes up a minimal amount of
|
||||
// space after amino encoding.
|
||||
// This is not thread safe, and is not intended for concurrent usage.
|
||||
type CompactBitArray struct {
|
||||
ExtraBitsStored byte `json:"extra_bits"` // The number of extra bits in elems.
|
||||
Elems []byte `json:"bits"`
|
||||
}
|
||||
|
||||
// NewCompactBitArray returns a new compact bit array.
|
||||
// It returns nil if the number of bits is zero.
|
||||
func NewCompactBitArray(bits int) *CompactBitArray {
|
||||
if bits <= 0 {
|
||||
return nil
|
||||
}
|
||||
return &CompactBitArray{
|
||||
ExtraBitsStored: byte(bits % 8),
|
||||
Elems: make([]byte, (bits+7)/8),
|
||||
}
|
||||
}
|
||||
|
||||
// Size returns the number of bits in the bitarray
|
||||
func (bA *CompactBitArray) Size() int {
|
||||
if bA == nil {
|
||||
return 0
|
||||
} else if bA.ExtraBitsStored == byte(0) {
|
||||
return len(bA.Elems) * 8
|
||||
}
|
||||
// num_bits = 8*num_full_bytes + overflow_in_last_byte
|
||||
// num_full_bytes = (len(bA.Elems)-1)
|
||||
return (len(bA.Elems)-1)*8 + int(bA.ExtraBitsStored)
|
||||
}
|
||||
|
||||
// GetIndex returns the bit at index i within the bit array.
|
||||
// The behavior is undefined if i >= bA.Size()
|
||||
func (bA *CompactBitArray) GetIndex(i int) bool {
|
||||
if bA == nil {
|
||||
return false
|
||||
}
|
||||
if i >= bA.Size() {
|
||||
return false
|
||||
}
|
||||
return bA.Elems[i>>3]&(uint8(1)<<uint8(7-(i%8))) > 0
|
||||
}
|
||||
|
||||
// SetIndex sets the bit at index i within the bit array.
|
||||
// The behavior is undefined if i >= bA.Size()
|
||||
func (bA *CompactBitArray) SetIndex(i int, v bool) bool {
|
||||
if bA == nil {
|
||||
return false
|
||||
}
|
||||
if i >= bA.Size() {
|
||||
return false
|
||||
}
|
||||
if v {
|
||||
bA.Elems[i>>3] |= (uint8(1) << uint8(7-(i%8)))
|
||||
} else {
|
||||
bA.Elems[i>>3] &= ^(uint8(1) << uint8(7-(i%8)))
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// NumTrueBitsBefore returns the number of bits set to true before the
|
||||
// given index. e.g. if bA = _XX__XX, NumOfTrueBitsBefore(4) = 2, since
|
||||
// there are two bits set to true before index 4.
|
||||
func (bA *CompactBitArray) NumTrueBitsBefore(index int) int {
|
||||
numTrueValues := 0
|
||||
for i := 0; i < index; i++ {
|
||||
if bA.GetIndex(i) {
|
||||
numTrueValues++
|
||||
}
|
||||
}
|
||||
return numTrueValues
|
||||
}
|
||||
|
||||
// Copy returns a copy of the provided bit array.
|
||||
func (bA *CompactBitArray) Copy() *CompactBitArray {
|
||||
if bA == nil {
|
||||
return nil
|
||||
}
|
||||
c := make([]byte, len(bA.Elems))
|
||||
copy(c, bA.Elems)
|
||||
return &CompactBitArray{
|
||||
ExtraBitsStored: bA.ExtraBitsStored,
|
||||
Elems: c,
|
||||
}
|
||||
}
|
||||
|
||||
// String returns a string representation of CompactBitArray: BA{<bit-string>},
|
||||
// where <bit-string> is a sequence of 'x' (1) and '_' (0).
|
||||
// The <bit-string> includes spaces and newlines to help people.
|
||||
// For a simple sequence of 'x' and '_' characters with no spaces or newlines,
|
||||
// see the MarshalJSON() method.
|
||||
// Example: "BA{_x_}" or "nil-BitArray" for nil.
|
||||
func (bA *CompactBitArray) String() string {
|
||||
return bA.StringIndented("")
|
||||
}
|
||||
|
||||
// StringIndented returns the same thing as String(), but applies the indent
|
||||
// at every 10th bit, and twice at every 50th bit.
|
||||
func (bA *CompactBitArray) StringIndented(indent string) string {
|
||||
if bA == nil {
|
||||
return "nil-BitArray"
|
||||
}
|
||||
lines := []string{}
|
||||
bits := ""
|
||||
size := bA.Size()
|
||||
for i := 0; i < size; i++ {
|
||||
if bA.GetIndex(i) {
|
||||
bits += "x"
|
||||
} else {
|
||||
bits += "_"
|
||||
}
|
||||
if i%100 == 99 {
|
||||
lines = append(lines, bits)
|
||||
bits = ""
|
||||
}
|
||||
if i%10 == 9 {
|
||||
bits += indent
|
||||
}
|
||||
if i%50 == 49 {
|
||||
bits += indent
|
||||
}
|
||||
}
|
||||
if len(bits) > 0 {
|
||||
lines = append(lines, bits)
|
||||
}
|
||||
return fmt.Sprintf("BA{%v:%v}", size, strings.Join(lines, indent))
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler interface by marshaling bit array
|
||||
// using a custom format: a string of '-' or 'x' where 'x' denotes the 1 bit.
|
||||
func (bA *CompactBitArray) MarshalJSON() ([]byte, error) {
|
||||
if bA == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
|
||||
bits := `"`
|
||||
size := bA.Size()
|
||||
for i := 0; i < size; i++ {
|
||||
if bA.GetIndex(i) {
|
||||
bits += `x`
|
||||
} else {
|
||||
bits += `_`
|
||||
}
|
||||
}
|
||||
bits += `"`
|
||||
return []byte(bits), nil
|
||||
}
|
||||
|
||||
var bitArrayJSONRegexp = regexp.MustCompile(`\A"([_x]*)"\z`)
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface by unmarshaling a custom
|
||||
// JSON description.
|
||||
func (bA *CompactBitArray) UnmarshalJSON(bz []byte) error {
|
||||
b := string(bz)
|
||||
if b == "null" {
|
||||
// This is required e.g. for encoding/json when decoding
|
||||
// into a pointer with pre-allocated BitArray.
|
||||
bA.ExtraBitsStored = 0
|
||||
bA.Elems = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate 'b'.
|
||||
match := bitArrayJSONRegexp.FindStringSubmatch(b)
|
||||
if match == nil {
|
||||
return fmt.Errorf("BitArray in JSON should be a string of format %q but got %s", bitArrayJSONRegexp.String(), b)
|
||||
}
|
||||
bits := match[1]
|
||||
|
||||
// Construct new CompactBitArray and copy over.
|
||||
numBits := len(bits)
|
||||
bA2 := NewCompactBitArray(numBits)
|
||||
for i := 0; i < numBits; i++ {
|
||||
if bits[i] == 'x' {
|
||||
bA2.SetIndex(i, true)
|
||||
}
|
||||
}
|
||||
*bA = *bA2
|
||||
return nil
|
||||
}
|
||||
|
||||
// CompactMarshal is a space efficient encoding for CompactBitArray.
|
||||
// It is not amino compatible.
|
||||
func (bA *CompactBitArray) CompactMarshal() []byte {
|
||||
size := bA.Size()
|
||||
if size <= 0 {
|
||||
return []byte("null")
|
||||
}
|
||||
bz := make([]byte, 0, size/8)
|
||||
// length prefix number of bits, not number of bytes. This difference
|
||||
// takes 3-4 bits in encoding, as opposed to instead encoding the number of
|
||||
// bytes (saving 3-4 bits) and including the offset as a full byte.
|
||||
bz = appendUvarint(bz, uint64(size))
|
||||
bz = append(bz, bA.Elems...)
|
||||
return bz
|
||||
}
|
||||
|
||||
// CompactUnmarshal is a space efficient decoding for CompactBitArray.
|
||||
// It is not amino compatible.
|
||||
func CompactUnmarshal(bz []byte) (*CompactBitArray, error) {
|
||||
if len(bz) < 2 {
|
||||
return nil, errors.New("compact bit array: invalid compact unmarshal size")
|
||||
} else if bytes.Equal(bz, []byte("null")) {
|
||||
return NewCompactBitArray(0), nil
|
||||
}
|
||||
size, n := binary.Uvarint(bz)
|
||||
bz = bz[n:]
|
||||
if len(bz) != int(size+7)/8 {
|
||||
return nil, errors.New("compact bit array: invalid compact unmarshal size")
|
||||
}
|
||||
|
||||
bA := &CompactBitArray{byte(int(size % 8)), bz}
|
||||
return bA, nil
|
||||
}
|
||||
|
||||
func appendUvarint(b []byte, x uint64) []byte {
|
||||
var a [binary.MaxVarintLen64]byte
|
||||
n := binary.PutUvarint(a[:], x)
|
||||
return append(b, a[:n]...)
|
||||
}
|
||||
196
crypto/multisig/bitarray/compact_bit_array_test.go
Normal file
196
crypto/multisig/bitarray/compact_bit_array_test.go
Normal file
@@ -0,0 +1,196 @@
|
||||
package bitarray
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
func randCompactBitArray(bits int) (*CompactBitArray, []byte) {
|
||||
numBytes := (bits + 7) / 8
|
||||
src := cmn.RandBytes((bits + 7) / 8)
|
||||
bA := NewCompactBitArray(bits)
|
||||
|
||||
for i := 0; i < numBytes-1; i++ {
|
||||
for j := uint8(0); j < 8; j++ {
|
||||
bA.SetIndex(i*8+int(j), src[i]&(uint8(1)<<(8-j)) > 0)
|
||||
}
|
||||
}
|
||||
// Set remaining bits
|
||||
for i := uint8(0); i < 8-uint8(bA.ExtraBitsStored); i++ {
|
||||
bA.SetIndex(numBytes*8+int(i), src[numBytes-1]&(uint8(1)<<(8-i)) > 0)
|
||||
}
|
||||
return bA, src
|
||||
}
|
||||
|
||||
func TestNewBitArrayNeverCrashesOnNegatives(t *testing.T) {
|
||||
bitList := []int{-127, -128, -1 << 31}
|
||||
for _, bits := range bitList {
|
||||
bA := NewCompactBitArray(bits)
|
||||
require.Nil(t, bA)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJSONMarshalUnmarshal(t *testing.T) {
|
||||
|
||||
bA1 := NewCompactBitArray(0)
|
||||
bA2 := NewCompactBitArray(1)
|
||||
|
||||
bA3 := NewCompactBitArray(1)
|
||||
bA3.SetIndex(0, true)
|
||||
|
||||
bA4 := NewCompactBitArray(5)
|
||||
bA4.SetIndex(0, true)
|
||||
bA4.SetIndex(1, true)
|
||||
|
||||
bA5 := NewCompactBitArray(9)
|
||||
bA5.SetIndex(0, true)
|
||||
bA5.SetIndex(1, true)
|
||||
bA5.SetIndex(8, true)
|
||||
|
||||
bA6 := NewCompactBitArray(16)
|
||||
bA6.SetIndex(0, true)
|
||||
bA6.SetIndex(1, true)
|
||||
bA6.SetIndex(8, false)
|
||||
bA6.SetIndex(15, true)
|
||||
|
||||
testCases := []struct {
|
||||
bA *CompactBitArray
|
||||
marshalledBA string
|
||||
}{
|
||||
{nil, `null`},
|
||||
{bA1, `null`},
|
||||
{bA2, `"_"`},
|
||||
{bA3, `"x"`},
|
||||
{bA4, `"xx___"`},
|
||||
{bA5, `"xx______x"`},
|
||||
{bA6, `"xx_____________x"`},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.bA.String(), func(t *testing.T) {
|
||||
bz, err := json.Marshal(tc.bA)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.marshalledBA, string(bz))
|
||||
|
||||
var unmarshalledBA *CompactBitArray
|
||||
err = json.Unmarshal(bz, &unmarshalledBA)
|
||||
require.NoError(t, err)
|
||||
|
||||
if tc.bA == nil {
|
||||
require.Nil(t, unmarshalledBA)
|
||||
} else {
|
||||
require.NotNil(t, unmarshalledBA)
|
||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
|
||||
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) {
|
||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompactMarshalUnmarshal(t *testing.T) {
|
||||
bA1 := NewCompactBitArray(0)
|
||||
bA2 := NewCompactBitArray(1)
|
||||
|
||||
bA3 := NewCompactBitArray(1)
|
||||
bA3.SetIndex(0, true)
|
||||
|
||||
bA4 := NewCompactBitArray(5)
|
||||
bA4.SetIndex(0, true)
|
||||
bA4.SetIndex(1, true)
|
||||
|
||||
bA5 := NewCompactBitArray(9)
|
||||
bA5.SetIndex(0, true)
|
||||
bA5.SetIndex(1, true)
|
||||
bA5.SetIndex(8, true)
|
||||
|
||||
bA6 := NewCompactBitArray(16)
|
||||
bA6.SetIndex(0, true)
|
||||
bA6.SetIndex(1, true)
|
||||
bA6.SetIndex(8, false)
|
||||
bA6.SetIndex(15, true)
|
||||
|
||||
testCases := []struct {
|
||||
bA *CompactBitArray
|
||||
marshalledBA []byte
|
||||
}{
|
||||
{nil, []byte("null")},
|
||||
{bA1, []byte("null")},
|
||||
{bA2, []byte{byte(1), byte(0)}},
|
||||
{bA3, []byte{byte(1), byte(128)}},
|
||||
{bA4, []byte{byte(5), byte(192)}},
|
||||
{bA5, []byte{byte(9), byte(192), byte(128)}},
|
||||
{bA6, []byte{byte(16), byte(192), byte(1)}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.bA.String(), func(t *testing.T) {
|
||||
bz := tc.bA.CompactMarshal()
|
||||
|
||||
assert.Equal(t, tc.marshalledBA, bz)
|
||||
|
||||
unmarshalledBA, err := CompactUnmarshal(bz)
|
||||
require.NoError(t, err)
|
||||
if tc.bA == nil {
|
||||
require.Nil(t, unmarshalledBA)
|
||||
} else {
|
||||
require.NotNil(t, unmarshalledBA)
|
||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
|
||||
if assert.EqualValues(t, tc.bA.String(), unmarshalledBA.String()) {
|
||||
assert.EqualValues(t, tc.bA.Elems, unmarshalledBA.Elems)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompactBitArrayNumOfTrueBitsBefore(t *testing.T) {
|
||||
testCases := []struct {
|
||||
marshalledBA string
|
||||
bAIndex []int
|
||||
trueValueIndex []int
|
||||
}{
|
||||
{`"_____"`, []int{0, 1, 2, 3, 4}, []int{0, 0, 0, 0, 0}},
|
||||
{`"x"`, []int{0}, []int{0}},
|
||||
{`"_x"`, []int{1}, []int{0}},
|
||||
{`"x___xxxx"`, []int{0, 4, 5, 6, 7}, []int{0, 1, 2, 3, 4}},
|
||||
{`"__x_xx_x__x_x___"`, []int{2, 4, 5, 7, 10, 12}, []int{0, 1, 2, 3, 4, 5}},
|
||||
{`"______________xx"`, []int{14, 15}, []int{0, 1}},
|
||||
}
|
||||
for tcIndex, tc := range testCases {
|
||||
t.Run(tc.marshalledBA, func(t *testing.T) {
|
||||
var bA *CompactBitArray
|
||||
err := json.Unmarshal([]byte(tc.marshalledBA), &bA)
|
||||
require.NoError(t, err)
|
||||
|
||||
for i := 0; i < len(tc.bAIndex); i++ {
|
||||
require.Equal(t, tc.trueValueIndex[i], bA.NumTrueBitsBefore(tc.bAIndex[i]), "tc %d, i %d", tcIndex, i)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompactBitArrayGetSetIndex(t *testing.T) {
|
||||
r := rand.New(rand.NewSource(100))
|
||||
numTests := 10
|
||||
numBitsPerArr := 100
|
||||
for i := 0; i < numTests; i++ {
|
||||
bits := r.Intn(1000)
|
||||
bA, _ := randCompactBitArray(bits)
|
||||
|
||||
for j := 0; j < numBitsPerArr; j++ {
|
||||
copy := bA.Copy()
|
||||
index := r.Intn(bits)
|
||||
val := (r.Int63() % 2) == 0
|
||||
bA.SetIndex(index, val)
|
||||
require.Equal(t, val, bA.GetIndex(index), "bA.SetIndex(%d, %v) failed on bit array: %s", index, val, copy)
|
||||
}
|
||||
}
|
||||
}
|
||||
70
crypto/multisig/multisignature.go
Normal file
70
crypto/multisig/multisignature.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package multisig
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/multisig/bitarray"
|
||||
)
|
||||
|
||||
// Multisignature is used to represent the signature object used in the multisigs.
|
||||
// Sigs is a list of signatures, sorted by corresponding index.
|
||||
type Multisignature struct {
|
||||
BitArray *bitarray.CompactBitArray
|
||||
Sigs [][]byte
|
||||
}
|
||||
|
||||
// NewMultisig returns a new Multisignature of size n.
|
||||
func NewMultisig(n int) *Multisignature {
|
||||
// Default the signature list to have a capacity of two, since we can
|
||||
// expect that most multisigs will require multiple signers.
|
||||
return &Multisignature{bitarray.NewCompactBitArray(n), make([][]byte, 0, 2)}
|
||||
}
|
||||
|
||||
// GetIndex returns the index of pk in keys. Returns -1 if not found
|
||||
func getIndex(pk crypto.PubKey, keys []crypto.PubKey) int {
|
||||
for i := 0; i < len(keys); i++ {
|
||||
if pk.Equals(keys[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// AddSignature adds a signature to the multisig, at the corresponding index.
|
||||
// If the signature already exists, replace it.
|
||||
func (mSig *Multisignature) AddSignature(sig []byte, index int) {
|
||||
newSigIndex := mSig.BitArray.NumTrueBitsBefore(index)
|
||||
// Signature already exists, just replace the value there
|
||||
if mSig.BitArray.GetIndex(index) {
|
||||
mSig.Sigs[newSigIndex] = sig
|
||||
return
|
||||
}
|
||||
mSig.BitArray.SetIndex(index, true)
|
||||
// Optimization if the index is the greatest index
|
||||
if newSigIndex == len(mSig.Sigs) {
|
||||
mSig.Sigs = append(mSig.Sigs, sig)
|
||||
return
|
||||
}
|
||||
// Expand slice by one with a dummy element, move all elements after i
|
||||
// over by one, then place the new signature in that gap.
|
||||
mSig.Sigs = append(mSig.Sigs, make([]byte, 0))
|
||||
copy(mSig.Sigs[newSigIndex+1:], mSig.Sigs[newSigIndex:])
|
||||
mSig.Sigs[newSigIndex] = sig
|
||||
}
|
||||
|
||||
// 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")
|
||||
}
|
||||
mSig.AddSignature(sig, index)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Marshal the multisignature with amino
|
||||
func (mSig *Multisignature) Marshal() []byte {
|
||||
return cdc.MustMarshalBinaryBare(mSig)
|
||||
}
|
||||
92
crypto/multisig/threshold_pubkey.go
Normal file
92
crypto/multisig/threshold_pubkey.go
Normal file
@@ -0,0 +1,92 @@
|
||||
package multisig
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
)
|
||||
|
||||
// PubKeyMultisigThreshold implements a K of N threshold multisig.
|
||||
type PubKeyMultisigThreshold struct {
|
||||
K uint `json:"threshold"`
|
||||
PubKeys []crypto.PubKey `json:"pubkeys"`
|
||||
}
|
||||
|
||||
var _ crypto.PubKey = &PubKeyMultisigThreshold{}
|
||||
|
||||
// NewPubKeyMultisigThreshold returns a new PubKeyMultisigThreshold.
|
||||
// Panics if len(pubkeys) < k or 0 >= k.
|
||||
func NewPubKeyMultisigThreshold(k int, pubkeys []crypto.PubKey) crypto.PubKey {
|
||||
if k <= 0 {
|
||||
panic("threshold k of n multisignature: k <= 0")
|
||||
}
|
||||
if len(pubkeys) < k {
|
||||
panic("threshold k of n multisignature: len(pubkeys) < k")
|
||||
}
|
||||
return &PubKeyMultisigThreshold{uint(k), pubkeys}
|
||||
}
|
||||
|
||||
// VerifyBytes expects sig to be an amino encoded version of a MultiSignature.
|
||||
// Returns true iff the multisignature contains k or more signatures
|
||||
// for the correct corresponding keys,
|
||||
// and all signatures are valid. (Not just k of the signatures)
|
||||
// The multisig uses a bitarray, so multiple signatures for the same key is not
|
||||
// a concern.
|
||||
func (pk *PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) bool {
|
||||
var sig *Multisignature
|
||||
err := cdc.UnmarshalBinaryBare(marshalledSig, &sig)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
size := sig.BitArray.Size()
|
||||
// ensure bit array is the correct size
|
||||
if len(pk.PubKeys) != size {
|
||||
return false
|
||||
}
|
||||
// ensure size of signature list
|
||||
if len(sig.Sigs) < int(pk.K) || len(sig.Sigs) > size {
|
||||
return false
|
||||
}
|
||||
// ensure at least k signatures are set
|
||||
if sig.BitArray.NumTrueBitsBefore(size) < int(pk.K) {
|
||||
return false
|
||||
}
|
||||
// index in the list of signatures which we are concerned with.
|
||||
sigIndex := 0
|
||||
for i := 0; i < size; i++ {
|
||||
if sig.BitArray.GetIndex(i) {
|
||||
if !pk.PubKeys[i].VerifyBytes(msg, sig.Sigs[sigIndex]) {
|
||||
return false
|
||||
}
|
||||
sigIndex++
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Bytes returns the amino encoded version of the PubKeyMultisigThreshold
|
||||
func (pk *PubKeyMultisigThreshold) Bytes() []byte {
|
||||
return cdc.MustMarshalBinaryBare(pk)
|
||||
}
|
||||
|
||||
// Address returns tmhash(PubKeyMultisigThreshold.Bytes())
|
||||
func (pk *PubKeyMultisigThreshold) Address() crypto.Address {
|
||||
return crypto.Address(tmhash.Sum(pk.Bytes()))
|
||||
}
|
||||
|
||||
// Equals returns true iff pk and other both have the same number of keys, and
|
||||
// all constituent keys are the same, and in the same order.
|
||||
func (pk *PubKeyMultisigThreshold) Equals(other crypto.PubKey) bool {
|
||||
otherKey, sameType := other.(*PubKeyMultisigThreshold)
|
||||
if !sameType {
|
||||
return false
|
||||
}
|
||||
if pk.K != otherKey.K || len(pk.PubKeys) != len(otherKey.PubKeys) {
|
||||
return false
|
||||
}
|
||||
for i := 0; i < len(pk.PubKeys); i++ {
|
||||
if !pk.PubKeys[i].Equals(otherKey.PubKeys[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
112
crypto/multisig/threshold_pubkey_test.go
Normal file
112
crypto/multisig/threshold_pubkey_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package multisig
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// This tests multisig functionality, but it expects the first k signatures to be valid
|
||||
// TODO: Adapt it to give more flexibility about first k signatures being valid
|
||||
func TestThresholdMultisigValidCases(t *testing.T) {
|
||||
pkSet1, sigSet1 := generatePubKeysAndSignatures(5, []byte{1, 2, 3, 4})
|
||||
cases := []struct {
|
||||
msg []byte
|
||||
k int
|
||||
pubkeys []crypto.PubKey
|
||||
signingIndices []int
|
||||
// signatures should be the same size as signingIndices.
|
||||
signatures [][]byte
|
||||
passAfterKSignatures []bool
|
||||
}{
|
||||
{
|
||||
msg: []byte{1, 2, 3, 4},
|
||||
k: 2,
|
||||
pubkeys: pkSet1,
|
||||
signingIndices: []int{0, 3, 1},
|
||||
signatures: sigSet1,
|
||||
passAfterKSignatures: []bool{false},
|
||||
},
|
||||
}
|
||||
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.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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Fully replace this test with table driven tests
|
||||
func TestThresholdMultisigDuplicateSignatures(t *testing.T) {
|
||||
msg := []byte{1, 2, 3, 4, 5}
|
||||
pubkeys, sigs := generatePubKeysAndSignatures(5, msg)
|
||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
||||
multisignature := NewMultisig(5)
|
||||
require.False(t, multisigKey.VerifyBytes(msg, multisignature.Marshal()))
|
||||
multisignature.AddSignatureFromPubKey(sigs[0], pubkeys[0], pubkeys)
|
||||
// Add second signature manually
|
||||
multisignature.Sigs = append(multisignature.Sigs, sigs[0])
|
||||
require.False(t, multisigKey.VerifyBytes(msg, multisignature.Marshal()))
|
||||
}
|
||||
|
||||
// TODO: Fully replace this test with table driven tests
|
||||
func TestMultiSigPubKeyEquality(t *testing.T) {
|
||||
msg := []byte{1, 2, 3, 4}
|
||||
pubkeys, _ := generatePubKeysAndSignatures(5, msg)
|
||||
multisigKey := NewPubKeyMultisigThreshold(2, pubkeys)
|
||||
var unmarshalledMultisig *PubKeyMultisigThreshold
|
||||
cdc.MustUnmarshalBinaryBare(multisigKey.Bytes(), &unmarshalledMultisig)
|
||||
require.True(t, multisigKey.Equals(unmarshalledMultisig))
|
||||
|
||||
// Ensure that reordering pubkeys is treated as a different pubkey
|
||||
pubkeysCpy := make([]crypto.PubKey, 5)
|
||||
copy(pubkeysCpy, pubkeys)
|
||||
pubkeysCpy[4] = pubkeys[3]
|
||||
pubkeysCpy[3] = pubkeys[4]
|
||||
multisigKey2 := NewPubKeyMultisigThreshold(2, pubkeysCpy)
|
||||
require.False(t, multisigKey.Equals(multisigKey2))
|
||||
}
|
||||
|
||||
func generatePubKeysAndSignatures(n int, msg []byte) (pubkeys []crypto.PubKey, signatures [][]byte) {
|
||||
pubkeys = make([]crypto.PubKey, n)
|
||||
signatures = make([][]byte, n)
|
||||
for i := 0; i < n; i++ {
|
||||
var privkey crypto.PrivKey
|
||||
if rand.Int63()%2 == 0 {
|
||||
privkey = ed25519.GenPrivKey()
|
||||
} else {
|
||||
privkey = secp256k1.GenPrivKey()
|
||||
}
|
||||
pubkeys[i] = privkey.PubKey()
|
||||
signatures[i], _ = privkey.Sign(msg)
|
||||
}
|
||||
return
|
||||
}
|
||||
26
crypto/multisig/wire.go
Normal file
26
crypto/multisig/wire.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package multisig
|
||||
|
||||
import (
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
)
|
||||
|
||||
// TODO: Figure out API for others to either add their own pubkey types, or
|
||||
// to make verify / marshal accept a cdc.
|
||||
const (
|
||||
PubKeyMultisigThresholdAminoRoute = "tendermint/PubKeyMultisigThreshold"
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
|
||||
func init() {
|
||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PubKeyMultisigThreshold{},
|
||||
PubKeyMultisigThresholdAminoRoute, nil)
|
||||
cdc.RegisterConcrete(ed25519.PubKeyEd25519{},
|
||||
ed25519.PubKeyAminoRoute, nil)
|
||||
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
||||
secp256k1.PubKeyAminoRoute, nil)
|
||||
}
|
||||
101
crypto/random.go
101
crypto/random.go
@@ -1,7 +1,6 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
crand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
@@ -9,16 +8,29 @@ import (
|
||||
"io"
|
||||
"sync"
|
||||
|
||||
. "github.com/tendermint/tendermint/libs/common"
|
||||
"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{}
|
||||
gRandInfo.MixEntropy(randBytes(32)) // Init
|
||||
|
||||
// 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) {
|
||||
@@ -30,20 +42,28 @@ func randBytes(numBytes int) []byte {
|
||||
b := make([]byte, numBytes)
|
||||
_, err := crand.Read(b)
|
||||
if err != nil {
|
||||
PanicCrisis(err)
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// This only uses the OS's randomness
|
||||
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 {
|
||||
b := make([]byte, numBytes)
|
||||
_, err := gRandInfo.Read(b)
|
||||
if err != nil {
|
||||
PanicCrisis(err)
|
||||
}
|
||||
return b
|
||||
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.
|
||||
//
|
||||
@@ -53,23 +73,29 @@ func CRandHex(numDigits int) string {
|
||||
return hex.EncodeToString(CRandBytes(numDigits / 2))
|
||||
}
|
||||
|
||||
// Returns a crand.Reader.
|
||||
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 [32]byte
|
||||
cipherAES256 cipher.Block
|
||||
streamAES256 cipher.Stream
|
||||
reader io.Reader
|
||||
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
|
||||
// XXX/TODO: review - https://github.com/tendermint/tendermint/issues/2099
|
||||
func (ri *randInfo) MixEntropy(seedBytes []byte) {
|
||||
ri.mtx.Lock()
|
||||
defer ri.mtx.Unlock()
|
||||
@@ -79,30 +105,35 @@ func (ri *randInfo) MixEntropy(seedBytes []byte) {
|
||||
h.Write(seedBytes)
|
||||
h.Write(ri.seedBytes[:])
|
||||
hashBytes := h.Sum(nil)
|
||||
hashBytes32 := [32]byte{}
|
||||
copy(hashBytes32[:], hashBytes)
|
||||
ri.seedBytes = xorBytes32(ri.seedBytes, hashBytes32)
|
||||
// Create new cipher.Block
|
||||
var err error
|
||||
ri.cipherAES256, err = aes.NewCipher(ri.seedBytes[:])
|
||||
copy(ri.seedBytes[:], hashBytes)
|
||||
chacha, err := chacha20poly1305.New(ri.seedBytes[:])
|
||||
if err != nil {
|
||||
PanicSanity("Error creating AES256 cipher: " + err.Error())
|
||||
panic("Initializing chacha20 failed")
|
||||
}
|
||||
// Create new stream
|
||||
ri.streamAES256 = cipher.NewCTR(ri.cipherAES256, randBytes(aes.BlockSize))
|
||||
ri.chacha = chacha
|
||||
// Create new reader
|
||||
ri.reader = &cipher.StreamReader{S: ri.streamAES256, R: crand.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()
|
||||
defer ri.mtx.Unlock()
|
||||
return ri.reader.Read(b)
|
||||
}
|
||||
|
||||
func xorBytes32(bytesA [32]byte, bytesB [32]byte) (res [32]byte) {
|
||||
for i, b := range bytesA {
|
||||
res[i] = b ^ bytesB[i]
|
||||
}
|
||||
return res
|
||||
n, err = ri.reader.Read(b)
|
||||
ri.mtx.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
23
crypto/random_test.go
Normal file
23
crypto/random_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package crypto_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
)
|
||||
|
||||
// the purpose of this test is primarily to ensure that the randomness
|
||||
// generation won't error.
|
||||
func TestRandomConsistency(t *testing.T) {
|
||||
x1 := crypto.CRandBytes(256)
|
||||
x2 := crypto.CRandBytes(256)
|
||||
x3 := crypto.CRandBytes(256)
|
||||
x4 := crypto.CRandBytes(256)
|
||||
x5 := crypto.CRandBytes(256)
|
||||
require.NotEqual(t, x1, x2)
|
||||
require.NotEqual(t, x3, x4)
|
||||
require.NotEqual(t, x4, x5)
|
||||
require.NotEqual(t, x1, x5)
|
||||
}
|
||||
@@ -7,16 +7,17 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
secp256k1 "github.com/btcsuite/btcd/btcec"
|
||||
secp256k1 "github.com/tendermint/btcd/btcec"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"golang.org/x/crypto/ripemd160" // forked to github.com/tendermint/crypto
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
const (
|
||||
Secp256k1PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1"
|
||||
Secp256k1PubKeyAminoRoute = "tendermint/PubKeySecp256k1"
|
||||
PrivKeyAminoRoute = "tendermint/PrivKeySecp256k1"
|
||||
PubKeyAminoRoute = "tendermint/PubKeySecp256k1"
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
@@ -24,11 +25,11 @@ var cdc = amino.NewCodec()
|
||||
func init() {
|
||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PubKeySecp256k1{},
|
||||
Secp256k1PubKeyAminoRoute, nil)
|
||||
PubKeyAminoRoute, nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PrivKeySecp256k1{},
|
||||
Secp256k1PrivKeyAminoRoute, nil)
|
||||
PrivKeyAminoRoute, nil)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -141,10 +142,12 @@ func (pubKey PubKeySecp256k1) VerifyBytes(msg []byte, sig []byte) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
parsedSig, err := secp256k1.ParseDERSignature(sig[:], secp256k1.S256())
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/secp256k1"
|
||||
|
||||
underlyingSecp256k1 "github.com/btcsuite/btcd/btcec"
|
||||
underlyingSecp256k1 "github.com/tendermint/btcd/btcec"
|
||||
)
|
||||
|
||||
type keyData struct {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/chacha20poly1305" // forked to github.com/tendermint/crypto
|
||||
)
|
||||
|
||||
// Implements crypto.AEAD
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user