mirror of
https://github.com/fluencelabs/tendermint
synced 2025-07-16 04:41:59 +00:00
Compare commits
605 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
aa40cfcbb9 | ||
|
6d6d103f15 | ||
|
239ebe2076 | ||
|
0cba0e11b5 | ||
|
d4e6720541 | ||
|
a2a62c9be6 | ||
|
3191ee8bad | ||
|
308b7e3bbe | ||
|
d1afa0ed6c | ||
|
ca00cd6a78 | ||
|
5f93220c61 | ||
|
fc031d980b | ||
|
1895cde590 | ||
|
be00cd1add | ||
|
a6011c007d | ||
|
ef94a322b8 | ||
|
7f607d0ce2 | ||
|
81c51cd4fc | ||
|
51094f9417 | ||
|
7644d27307 | ||
|
764cfe33aa | ||
|
616c3a4bae | ||
|
04e97f599a | ||
|
56a4fb4d72 | ||
|
49017a5787 | ||
|
6a80412a01 | ||
|
2348f38927 | ||
|
41e2eeee9c | ||
|
a88e283a9d | ||
|
1e1ca15bcc | ||
|
c6604b5a9b | ||
|
c510f823e7 | ||
|
daddebac29 | ||
|
30f346fe44 | ||
|
4d8f29f79c | ||
|
2182f6a702 | ||
|
a06912b579 | ||
|
0ff715125b | ||
|
385977d5e8 | ||
|
0138530df2 | ||
|
0533c73a50 | ||
|
1beb45511c | ||
|
4a568fcedb | ||
|
b3141d7d02 | ||
|
9a6dd96cba | ||
|
9fa959619a | ||
|
1f09818770 | ||
|
e4806f980b | ||
|
b53a2712df | ||
|
a75dab492c | ||
|
7c9e767e1f | ||
|
f82a8ff73a | ||
|
ae275d791e | ||
|
f5cca9f121 | ||
|
3fbe9f235a | ||
|
f7e463f6d3 | ||
|
bc2a9b20c0 | ||
|
9e075d8dd5 | ||
|
8003786c9a | ||
|
2594cec116 | ||
|
df32ea4be5 | ||
|
f69e2c6d6c | ||
|
d5d0d2bd77 | ||
|
41eaf0e31d | ||
|
68b467886a | ||
|
2f64717bb5 | ||
|
c4a1cfc5c2 | ||
|
0f96bea41d | ||
|
9c236ffd6c | ||
|
9f8761d105 | ||
|
5413c11150 | ||
|
a14fd8eba0 | ||
|
1bb7e31d63 | ||
|
222b8978c8 | ||
|
d9a1aad5c5 | ||
|
8ef0c2681d | ||
|
c4d93fd27b | ||
|
dc2a338d96 | ||
|
725ed7969a | ||
|
44b769b1ac | ||
|
380afaa678 | ||
|
b30c34e713 | ||
|
4039276085 | ||
|
3f987adc92 | ||
|
b11788d36d | ||
|
9adcfe2804 | ||
|
3d15579e0c | ||
|
4571f0fbe8 | ||
|
8a73feae14 | ||
|
e291fbbebe | ||
|
416d143bf7 | ||
|
7213869fc6 | ||
|
ef9902e602 | ||
|
b771798d48 | ||
|
1abf34aa91 | ||
|
92dc5fc77a | ||
|
bef39f3346 | ||
|
94e63be922 | ||
|
9570ac4d3e | ||
|
99b9c9bf60 | ||
|
47a0669d12 | ||
|
fe3b97fd66 | ||
|
56052c0a87 | ||
|
98e442a8de | ||
|
b12488b5f1 | ||
|
b487feba42 | ||
|
72f86b5192 | ||
|
42592d9ae0 | ||
|
1610a05cbd | ||
|
2d525bf2b8 | ||
|
e9efbfe267 | ||
|
7b883a5457 | ||
|
fd8d1d6b69 | ||
|
5abdd254e7 | ||
|
22dcc92232 | ||
|
ccf6b2b512 | ||
|
1466a2cc9f | ||
|
6168b404a7 | ||
|
e6fc10faf6 | ||
|
60018d6148 | ||
|
0d5e0d2f13 | ||
|
2cfdef6111 | ||
|
b90e06a37c | ||
|
e6a0d098e8 | ||
|
d8ab8509de | ||
|
85bba82154 | ||
|
d5a05eccba | ||
|
a676c71678 | ||
|
c033975a53 | ||
|
06225e332e | ||
|
b646437ec7 | ||
|
be8c2d5018 | ||
|
e93b492efe | ||
|
9973decff9 | ||
|
a0412357c1 | ||
|
a70a53254d | ||
|
1ce24a6282 | ||
|
814a88a69b | ||
|
6353862ac0 | ||
|
3af11c43f2 | ||
|
27fcf96556 | ||
|
bb0e17dbf0 | ||
|
5a6822c8ac | ||
|
fb10209a96 | ||
|
0f793a5a00 | ||
|
80d0a36250 | ||
|
e11699038d | ||
|
533b3cfb73 | ||
|
3ff820bdf4 | ||
|
905abf1388 | ||
|
7a4b62d3be | ||
|
5b19fcf204 | ||
|
1944d8534b | ||
|
13badc1d29 | ||
|
091d2c3e5e | ||
|
d178ea9eaf | ||
|
46d32af055 | ||
|
8b77328313 | ||
|
6e9aee5460 | ||
|
d460df1335 | ||
|
03e42d2e38 | ||
|
b8a9b0bf78 | ||
|
7246ffc48f | ||
|
071ebdd514 | ||
|
8760c5b4f9 | ||
|
59b75d3f28 | ||
|
c086d0a341 | ||
|
322cee9156 | ||
|
80e4fe6c0d | ||
|
fb91ef7462 | ||
|
a22c962e28 | ||
|
1660e30ffe | ||
|
a83c268d7f | ||
|
c5905900eb | ||
|
7a03344480 | ||
|
a530352f61 | ||
|
60437953ac | ||
|
56d7160606 | ||
|
cdc252b818 | ||
|
b24de1c01c | ||
|
b6d5b8b745 | ||
|
a67ae81469 | ||
|
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 | ||
|
9e940b95ad | ||
|
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 | ||
|
013b9cef64 | ||
|
087b657008 | ||
|
fe835cd456 | ||
|
d7035abe73 | ||
|
f2b629680a | ||
|
720ce658f1 | ||
|
309a6772d7 | ||
|
8bd514d9fb | ||
|
f903947ff3 | ||
|
ea67fb55eb | ||
|
e1062a657f | ||
|
4d998b7c03 | ||
|
bec9d5cba9 | ||
|
06a157ad06 | ||
|
d6a666b445 | ||
|
c5c2b9601f | ||
|
7538864c15 | ||
|
279259ec8e | ||
|
ca9d07e5e4 | ||
|
6e3c5e8033 | ||
|
8073e51b04 | ||
|
3161ebbc2f | ||
|
4cbeb30da2 | ||
|
d5b5e5a2e4 | ||
|
6691492540 | ||
|
0f80a7da82 | ||
|
ae2238efe6 | ||
|
2878c7523f | ||
|
b1cff0f9bf | ||
|
d09a3a6d3a | ||
|
87f09adeec | ||
|
b3a3c8a192 | ||
|
96fdec0fca | ||
|
fe5e7808f2 | ||
|
2d1c5a1ce6 | ||
|
00ebdcd581 | ||
|
2487210414 | ||
|
a040c36dfb | ||
|
d579f4c610 | ||
|
b82138b002 | ||
|
8ed99c2c13 | ||
|
4c5a143a70 | ||
|
b33f73eaf1 | ||
|
e719a93d1d | ||
|
eb9b37e196 | ||
|
eaa137512c | ||
|
023bb99eb0 | ||
|
f2f53442c6 | ||
|
24ae878b9f | ||
|
619bb3b2d7 | ||
|
dde96b75ce | ||
|
6fb2f44cc3 | ||
|
08ad162daa | ||
|
a83eed104c | ||
|
be642754f5 | ||
|
2608249e5b | ||
|
62b8ee270d | ||
|
0c7338c5f0 | ||
|
231cdc1320 | ||
|
fef4fe1c66 | ||
|
f00b52b710 | ||
|
188e459273 | ||
|
3d5d254932 | ||
|
ce9ddc7cd7 | ||
|
c03ad56d55 | ||
|
caef5dcd69 | ||
|
7634073718 | ||
|
af2894c0f8 | ||
|
a2debe57c7 | ||
|
5955eddc7d | ||
|
4bab34ae45 | ||
|
fe5b0d7074 | ||
|
96ae535fb8 | ||
|
4be6395ee0 | ||
|
bc526f18a4 | ||
|
40d6dc2ee5 | ||
|
d542d2c394 | ||
|
fd29fd6465 | ||
|
6241e6b927 | ||
|
9f19229791 | ||
|
bdab37a626 | ||
|
7bf28af590 | ||
|
1b04e4e5f1 | ||
|
66fe5b7bae | ||
|
24b94d7aa4 | ||
|
0bd4fb96f0 | ||
|
9cfc47a93b | ||
|
d212292f86 | ||
|
7ad92c44cb | ||
|
d149d8f96d | ||
|
74b6cc9057 | ||
|
60378fd7f9 | ||
|
c248ce5ef6 | ||
|
059a03a66a | ||
|
abe5b63059 | ||
|
a657870b3d | ||
|
7e7473ad41 | ||
|
75a26ebd6d | ||
|
ad580e2734 | ||
|
f6705f02c7 | ||
|
ea31c4836a | ||
|
f1093edbe2 | ||
|
b92860b6c4 | ||
|
54d753e64e | ||
|
e1b48b16c4 | ||
|
7c07235649 | ||
|
b41b89732d | ||
|
9018acde5f | ||
|
5453aa6169 | ||
|
b51ed132f7 | ||
|
8524a8da7f | ||
|
cfcbc61449 | ||
|
9184733261 | ||
|
363146dacf | ||
|
ad1b722898 | ||
|
8163b99a75 | ||
|
835c2ee74a | ||
|
19fc4ac47c | ||
|
acd976ad5b | ||
|
37ef5485b4 | ||
|
7f4498f8b1 | ||
|
538c410bcd | ||
|
c3296f2e01 | ||
|
242a6037e8 | ||
|
bf0ff212b9 | ||
|
a5b7ea93c4 | ||
|
8128627f08 |
1550
.circleci/codecov.sh
Normal file
1550
.circleci/codecov.sh
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,17 @@ version: 2
|
||||
defaults: &defaults
|
||||
working_directory: /go/src/github.com/tendermint/tendermint
|
||||
docker:
|
||||
- image: circleci/golang:1.10.3
|
||||
- image: circleci/golang:1.11.4
|
||||
environment:
|
||||
GOBIN: /tmp/workspace/bin
|
||||
|
||||
docs_update_config: &docs_update_config
|
||||
working_directory: ~/repo
|
||||
docker:
|
||||
- image: tendermint/docs_deployment
|
||||
environment:
|
||||
AWS_REGION: us-east-1
|
||||
|
||||
jobs:
|
||||
setup_dependencies:
|
||||
<<: *defaults
|
||||
@@ -41,10 +48,10 @@ jobs:
|
||||
key: v3-pkg-cache
|
||||
paths:
|
||||
- /go/pkg
|
||||
- save_cache:
|
||||
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
paths:
|
||||
- /go/src/github.com/tendermint/tendermint
|
||||
# - save_cache:
|
||||
# key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
|
||||
# paths:
|
||||
# - /go/src/github.com/tendermint/tendermint
|
||||
|
||||
build_slate:
|
||||
<<: *defaults
|
||||
@@ -53,8 +60,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,14 +91,35 @@ 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
|
||||
make get_dev_tools
|
||||
- run:
|
||||
name: dependencies
|
||||
command: |
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make get_vendor_deps
|
||||
- run: mkdir -p $GOPATH/src/github.com/tendermint
|
||||
- run: ln -sf /home/circleci/project $GOPATH/src/github.com/tendermint/tendermint
|
||||
|
||||
- run:
|
||||
name: metalinter
|
||||
command: |
|
||||
set -ex
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make metalinter
|
||||
- run:
|
||||
name: check_dep
|
||||
command: |
|
||||
set -ex
|
||||
export PATH="$GOBIN:$PATH"
|
||||
make check_dep
|
||||
|
||||
test_abci_apps:
|
||||
<<: *defaults
|
||||
@@ -85,8 +128,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: |
|
||||
@@ -102,8 +159,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: |
|
||||
@@ -117,8 +188,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
|
||||
@@ -132,8 +217,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
|
||||
@@ -141,7 +240,7 @@ jobs:
|
||||
for pkg in $(go list github.com/tendermint/tendermint/... | circleci tests split --split-by=timings); do
|
||||
id=$(basename "$pkg")
|
||||
|
||||
GOCACHE=off go test -v -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
||||
GOCACHE=off go test -timeout 5m -race -coverprofile=/tmp/workspace/profiles/$id.out -covermode=atomic "$pkg" | tee "/tmp/logs/$id-$RANDOM.log"
|
||||
done
|
||||
- persist_to_workspace:
|
||||
root: /tmp/workspace
|
||||
@@ -157,12 +256,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
|
||||
@@ -174,14 +309,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: |
|
||||
@@ -193,12 +344,27 @@ jobs:
|
||||
done
|
||||
- run:
|
||||
name: upload
|
||||
command: bash <(curl -s https://codecov.io/bash) -f coverage.txt
|
||||
command: bash .circleci/codecov.sh -f coverage.txt
|
||||
|
||||
deploy_docs:
|
||||
<<: *docs_update_config
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Trigger website build
|
||||
command: |
|
||||
chamber exec tendermint -- start_website_build
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
test-suite:
|
||||
jobs:
|
||||
- deploy_docs:
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
- setup_dependencies
|
||||
- lint:
|
||||
requires:
|
||||
@@ -218,6 +384,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
|
||||
|
42
.github/ISSUE_TEMPLATE
vendored
42
.github/ISSUE_TEMPLATE
vendored
@@ -1,42 +0,0 @@
|
||||
<!-- Thanks for filing an issue! Before hitting the button, please answer these questions.-->
|
||||
|
||||
**Is this a BUG REPORT or FEATURE REQUEST?** (choose one):
|
||||
|
||||
<!--
|
||||
If this is a BUG REPORT, please:
|
||||
- Fill in as much of the template below as you can.
|
||||
|
||||
If this is a FEATURE REQUEST, please:
|
||||
- Describe *in detail* the feature/behavior/change you'd like to see.
|
||||
|
||||
In both cases, be ready for followup questions, and please respond in a timely
|
||||
manner. We might ask you to provide additional logs and data (tendermint & app)
|
||||
in a case of bug.
|
||||
-->
|
||||
|
||||
**Tendermint version** (use `tendermint version` or `git rev-parse --verify HEAD` if installed from source):
|
||||
|
||||
|
||||
**ABCI app** (name for built-in, URL for self-written if it's publicly available):
|
||||
|
||||
**Environment**:
|
||||
- **OS** (e.g. from /etc/os-release):
|
||||
- **Install tools**:
|
||||
- **Others**:
|
||||
|
||||
|
||||
**What happened**:
|
||||
|
||||
|
||||
**What you expected to happen**:
|
||||
|
||||
|
||||
**How to reproduce it** (as minimally and precisely as possible):
|
||||
|
||||
**Logs (you can paste a small part showing an error or link a pastebin, gist, etc. containing more of the log file)**:
|
||||
|
||||
**Config (you can paste only the changes you've made)**:
|
||||
|
||||
**`/dump_consensus_state` output for consensus bugs**
|
||||
|
||||
**Anything else do we need to know**:
|
42
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
42
.github/ISSUE_TEMPLATE/bug-report.md
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a report to help us squash bugs!
|
||||
|
||||
---
|
||||
<!--
|
||||
Please fill in as much of the template below as you can.
|
||||
|
||||
Be ready for followup questions, and please respond in a timely
|
||||
manner. We might ask you to provide additional logs and data (tendermint & app).
|
||||
-->
|
||||
|
||||
**Tendermint version** (use `tendermint version` or `git rev-parse --verify HEAD` if installed from source):
|
||||
|
||||
|
||||
**ABCI app** (name for built-in, URL for self-written if it's publicly available):
|
||||
|
||||
**Environment**:
|
||||
- **OS** (e.g. from /etc/os-release):
|
||||
- **Install tools**:
|
||||
- **Others**:
|
||||
|
||||
|
||||
**What happened**:
|
||||
|
||||
|
||||
**What you expected to happen**:
|
||||
|
||||
|
||||
**Have you tried the latest version**: yes/no
|
||||
|
||||
**How to reproduce it** (as minimally and precisely as possible):
|
||||
|
||||
**Logs (paste a small part showing an error (< 10 lines) or link a pastebin, gist, etc. containing more of the log file)**:
|
||||
|
||||
**Config (you can paste only the changes you've made)**:
|
||||
|
||||
**node command runtime flags**:
|
||||
|
||||
**`/dump_consensus_state` output for consensus bugs**
|
||||
|
||||
**Anything else we need to know**:
|
13
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Create a proposal to request a feature
|
||||
|
||||
---
|
||||
<!--
|
||||
Please describe *in detail* the feature/behavior/change you'd like to see.
|
||||
|
||||
Be ready for followup questions, and please respond in a timely
|
||||
manner.
|
||||
|
||||
Word of caution: poorly thought out proposals may be rejected without deliberation
|
||||
-->
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -3,4 +3,4 @@
|
||||
* [ ] Updated all relevant documentation in docs
|
||||
* [ ] Updated all code comments where relevant
|
||||
* [ ] Wrote tests
|
||||
* [ ] Updated CHANGELOG.md
|
||||
* [ ] Updated CHANGELOG_PENDING.md
|
||||
|
9
.gitignore
vendored
9
.gitignore
vendored
@@ -14,9 +14,11 @@ test/p2p/data/
|
||||
test/logs
|
||||
coverage.txt
|
||||
docs/_build
|
||||
docs/dist
|
||||
*.log
|
||||
abci-cli
|
||||
docs/node_modules/
|
||||
index.html.md
|
||||
|
||||
scripts/wal2json/wal2json
|
||||
scripts/cutWALUntil/cutWALUntil
|
||||
@@ -24,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
|
||||
|
687
CHANGELOG.md
687
CHANGELOG.md
@@ -1,5 +1,687 @@
|
||||
# Changelog
|
||||
|
||||
## v0.28.0
|
||||
|
||||
*January 16th, 2019*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@fmauricios, @gianfelipe93, @husio, @needkane, @srmo, @yutianwu
|
||||
|
||||
This release is primarily about upgrades to the `privval` system -
|
||||
separating the `priv_validator.json` into distinct config and data files, and
|
||||
refactoring the socket validator to support reconnections.
|
||||
|
||||
**Note:** Please backup your existing `priv_validator.json` before using this
|
||||
version.
|
||||
|
||||
See [UPGRADING.md](UPGRADING.md) for more details.
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
- [cli] Removed `--proxy_app=dummy` option. Use `kvstore` (`persistent_kvstore`) instead.
|
||||
- [cli] Renamed `--proxy_app=nilapp` to `--proxy_app=noop`.
|
||||
- [config] [\#2992](https://github.com/tendermint/tendermint/issues/2992) `allow_duplicate_ip` is now set to false
|
||||
- [privval] [\#1181](https://github.com/tendermint/tendermint/issues/1181) Split `priv_validator.json` into immutable (`config/priv_validator_key.json`) and mutable (`data/priv_validator_state.json`) parts (@yutianwu)
|
||||
- [privval] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Split up `PubKeyMsg` into `PubKeyRequest` and `PubKeyResponse` to be consistent with other message types
|
||||
- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Listen for unix socket connections instead of dialing them
|
||||
|
||||
* Apps
|
||||
|
||||
* Go API
|
||||
- [types] [\#2981](https://github.com/tendermint/tendermint/issues/2981) Remove `PrivValidator.GetAddress()`
|
||||
|
||||
* Blockchain Protocol
|
||||
|
||||
* P2P Protocol
|
||||
|
||||
### FEATURES:
|
||||
- [rpc] [\#3052](https://github.com/tendermint/tendermint/issues/3052) Include peer's remote IP in `/net_info`
|
||||
|
||||
### IMPROVEMENTS:
|
||||
- [consensus] [\#3086](https://github.com/tendermint/tendermint/issues/3086) Log peerID on ignored votes (@srmo)
|
||||
- [docs] [\#3061](https://github.com/tendermint/tendermint/issues/3061) Added specification for signing consensus msgs at
|
||||
./docs/spec/consensus/signing.md
|
||||
- [privval] [\#2948](https://github.com/tendermint/tendermint/issues/2948) Memoize pubkey so it's only requested once on startup
|
||||
- [privval] [\#2923](https://github.com/tendermint/tendermint/issues/2923) Retry RemoteSigner connections on error
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [build] [\#3085](https://github.com/tendermint/tendermint/issues/3085) Fix `Version` field in build scripts (@husio)
|
||||
- [crypto/multisig] [\#3102](https://github.com/tendermint/tendermint/issues/3102) Fix multisig keys address length
|
||||
- [crypto/encoding] [\#3101](https://github.com/tendermint/tendermint/issues/3101) Fix `PubKeyMultisigThreshold` unmarshalling into `crypto.PubKey` interface
|
||||
- [p2p/conn] [\#3111](https://github.com/tendermint/tendermint/issues/3111) Make SecretConnection thread safe
|
||||
- [rpc] [\#3053](https://github.com/tendermint/tendermint/issues/3053) Fix internal error in `/tx_search` when results are empty
|
||||
(@gianfelipe93)
|
||||
- [types] [\#2926](https://github.com/tendermint/tendermint/issues/2926) Do not panic if retrieving the privval's public key fails
|
||||
|
||||
## v0.27.4
|
||||
|
||||
*December 21st, 2018*
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [mempool] [\#3036](https://github.com/tendermint/tendermint/issues/3036) Fix
|
||||
LRU cache by popping the least recently used item when the cache is full,
|
||||
not the most recently used one!
|
||||
|
||||
## v0.27.3
|
||||
|
||||
*December 16th, 2018*
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* Go API
|
||||
- [dep] [\#3027](https://github.com/tendermint/tendermint/issues/3027) Revert to mainline Go crypto library, eliminating the modified
|
||||
`bcrypt.GenerateFromPassword`
|
||||
|
||||
## v0.27.2
|
||||
|
||||
*December 16th, 2018*
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [node] [\#3025](https://github.com/tendermint/tendermint/issues/3025) Validate NodeInfo addresses on startup.
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [p2p] [\#3025](https://github.com/tendermint/tendermint/pull/3025) Revert to using defers in addrbook. Fixes deadlocks in pex and consensus upon invalid ExternalAddr/ListenAddr configuration.
|
||||
|
||||
## v0.27.1
|
||||
|
||||
*December 15th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@danil-lashin, @hleb-albau, @james-ray, @leo-xinwang
|
||||
|
||||
### FEATURES:
|
||||
- [rpc] [\#2964](https://github.com/tendermint/tendermint/issues/2964) Add `UnconfirmedTxs(limit)` and `NumUnconfirmedTxs()` methods to HTTP/Local clients (@danil-lashin)
|
||||
- [docs] [\#3004](https://github.com/tendermint/tendermint/issues/3004) Enable full-text search on docs pages
|
||||
|
||||
### IMPROVEMENTS:
|
||||
- [consensus] [\#2971](https://github.com/tendermint/tendermint/issues/2971) Return error if ValidatorSet is empty after InitChain
|
||||
(@leo-xinwang)
|
||||
- [ci/cd] [\#3005](https://github.com/tendermint/tendermint/issues/3005) Updated CircleCI job to trigger website build when docs are updated
|
||||
- [docs] Various updates
|
||||
|
||||
### BUG FIXES:
|
||||
- [cmd] [\#2983](https://github.com/tendermint/tendermint/issues/2983) `testnet` command always sets `addr_book_strict = false`
|
||||
- [config] [\#2980](https://github.com/tendermint/tendermint/issues/2980) Fix CORS options formatting
|
||||
- [kv indexer] [\#2912](https://github.com/tendermint/tendermint/issues/2912) Don't ignore key when executing CONTAINS
|
||||
- [mempool] [\#2961](https://github.com/tendermint/tendermint/issues/2961) Call `notifyTxsAvailable` if there're txs left after committing a block, but recheck=false
|
||||
- [mempool] [\#2994](https://github.com/tendermint/tendermint/issues/2994) Reject txs with negative GasWanted
|
||||
- [p2p] [\#2990](https://github.com/tendermint/tendermint/issues/2990) Fix a bug where seeds don't disconnect from a peer after 3h
|
||||
- [consensus] [\#3006](https://github.com/tendermint/tendermint/issues/3006) Save state after InitChain only when stateHeight is also 0 (@james-ray)
|
||||
|
||||
## v0.27.0
|
||||
|
||||
*December 5th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@danil-lashin, @srmo
|
||||
|
||||
Special thanks to @dlguddus for discovering a [major
|
||||
issue](https://github.com/tendermint/tendermint/issues/2718#issuecomment-440888677)
|
||||
in the proposer selection algorithm.
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
This release is primarily about fixes to the proposer selection algorithm
|
||||
in preparation for the [Cosmos Game of
|
||||
Stakes](https://blog.cosmos.network/the-game-of-stakes-is-open-for-registration-83a404746ee6).
|
||||
It also makes use of the `ConsensusParams.Validator.PubKeyTypes` to restrict the
|
||||
key types that can be used by validators, and removes the `Heartbeat` consensus
|
||||
message.
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
- [rpc] [\#2932](https://github.com/tendermint/tendermint/issues/2932) Rename `accum` to `proposer_priority`
|
||||
|
||||
* Go API
|
||||
- [db] [\#2913](https://github.com/tendermint/tendermint/pull/2913)
|
||||
ReverseIterator API change: start < end, and end is exclusive.
|
||||
- [types] [\#2932](https://github.com/tendermint/tendermint/issues/2932) Rename `Validator.Accum` to `Validator.ProposerPriority`
|
||||
|
||||
* Blockchain Protocol
|
||||
- [state] [\#2714](https://github.com/tendermint/tendermint/issues/2714) Validators can now only use pubkeys allowed within
|
||||
ConsensusParams.Validator.PubKeyTypes
|
||||
|
||||
* P2P Protocol
|
||||
- [consensus] [\#2871](https://github.com/tendermint/tendermint/issues/2871)
|
||||
Remove *ProposalHeartbeat* message as it serves no real purpose (@srmo)
|
||||
- [state] Fixes for proposer selection:
|
||||
- [\#2785](https://github.com/tendermint/tendermint/issues/2785) Accum for new validators is `-1.125*totalVotingPower` instead of 0
|
||||
- [\#2941](https://github.com/tendermint/tendermint/issues/2941) val.Accum is preserved during ValidatorSet.Update to avoid being
|
||||
reset to 0
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [state] [\#2929](https://github.com/tendermint/tendermint/issues/2929) Minor refactor of updateState logic (@danil-lashin)
|
||||
- [node] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Allow node to start even if software's BlockProtocol is
|
||||
different from state's BlockProtocol
|
||||
- [pex] [\#2959](https://github.com/tendermint/tendermint/issues/2959) Pex reactor logger uses `module=pex`
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [p2p] [\#2968](https://github.com/tendermint/tendermint/issues/2968) Panic on transport error rather than continuing to run but not
|
||||
accept new connections
|
||||
- [p2p] [\#2969](https://github.com/tendermint/tendermint/issues/2969) Fix mismatch in peer count between `/net_info` and the prometheus
|
||||
metrics
|
||||
- [rpc] [\#2408](https://github.com/tendermint/tendermint/issues/2408) `/broadcast_tx_commit`: Fix "interface conversion: interface {} in nil, not EventDataTx" panic (could happen if somebody sent a tx using `/broadcast_tx_commit` while Tendermint was being stopped)
|
||||
- [state] [\#2785](https://github.com/tendermint/tendermint/issues/2785) Fix accum for new validators to be `-1.125*totalVotingPower`
|
||||
instead of 0, forcing them to wait before becoming the proposer. Also:
|
||||
- do not batch clip
|
||||
- keep accums averaged near 0
|
||||
- [txindex/kv] [\#2925](https://github.com/tendermint/tendermint/issues/2925) Don't return false positives when range searching for a prefix of a tag value
|
||||
- [types] [\#2938](https://github.com/tendermint/tendermint/issues/2938) Fix regression in v0.26.4 where we panic on empty
|
||||
genDoc.Validators
|
||||
- [types] [\#2941](https://github.com/tendermint/tendermint/issues/2941) Preserve val.Accum during ValidatorSet.Update to avoid it being
|
||||
reset to 0 every time a validator is updated
|
||||
|
||||
## v0.26.4
|
||||
|
||||
*November 27th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@ackratos, @goolAdapter, @james-ray, @joe-bowman, @kostko,
|
||||
@nagarajmanjunath, @tomtau
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [rpc] [\#2747](https://github.com/tendermint/tendermint/issues/2747) Enable subscription to tags emitted from `BeginBlock`/`EndBlock` (@kostko)
|
||||
- [types] [\#2747](https://github.com/tendermint/tendermint/issues/2747) Add `ResultBeginBlock` and `ResultEndBlock` fields to `EventDataNewBlock`
|
||||
and `EventDataNewBlockHeader` to support subscriptions (@kostko)
|
||||
- [types] [\#2918](https://github.com/tendermint/tendermint/issues/2918) Add Marshal, MarshalTo, Unmarshal methods to various structs
|
||||
to support Protobuf compatibility (@nagarajmanjunath)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [config] [\#2877](https://github.com/tendermint/tendermint/issues/2877) Add `blocktime_iota` to the config.toml (@ackratos)
|
||||
- NOTE: this should be a ConsensusParam, not part of the config, and will be
|
||||
removed from the config at a later date
|
||||
([\#2920](https://github.com/tendermint/tendermint/issues/2920).
|
||||
- [mempool] [\#2882](https://github.com/tendermint/tendermint/issues/2882) Add txs from Update to cache
|
||||
- [mempool] [\#2891](https://github.com/tendermint/tendermint/issues/2891) Remove local int64 counter from being stored in every tx
|
||||
- [node] [\#2866](https://github.com/tendermint/tendermint/issues/2866) Add ability to instantiate IPCVal (@joe-bowman)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [blockchain] [\#2731](https://github.com/tendermint/tendermint/issues/2731) Retry both blocks if either is bad to avoid getting stuck during fast sync (@goolAdapter)
|
||||
- [consensus] [\#2893](https://github.com/tendermint/tendermint/issues/2893) Use genDoc.Validators instead of state.NextValidators on replay when appHeight==0 (@james-ray)
|
||||
- [log] [\#2868](https://github.com/tendermint/tendermint/issues/2868) Fix `module=main` setting overriding all others
|
||||
- NOTE: this changes the default logging behaviour to be much less verbose.
|
||||
Set `log_level="info"` to restore the previous behaviour.
|
||||
- [rpc] [\#2808](https://github.com/tendermint/tendermint/issues/2808) Fix `accum` field in `/validators` by calling `IncrementAccum` if necessary
|
||||
- [rpc] [\#2811](https://github.com/tendermint/tendermint/issues/2811) Allow integer IDs in JSON-RPC requests (@tomtau)
|
||||
- [txindex/kv] [\#2759](https://github.com/tendermint/tendermint/issues/2759) Fix tx.height range queries
|
||||
- [txindex/kv] [\#2775](https://github.com/tendermint/tendermint/issues/2775) Order tx results by index if height is the same
|
||||
- [txindex/kv] [\#2908](https://github.com/tendermint/tendermint/issues/2908) Don't return false positives when searching for a prefix of a tag value
|
||||
|
||||
## v0.26.3
|
||||
|
||||
*November 17th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@danil-lashin, @kevlubkcm, @krhubert, @srmo
|
||||
|
||||
Friendly reminder, we have a [bug bounty
|
||||
program](https://hackerone.com/tendermint).
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* Go API
|
||||
- [rpc] [\#2791](https://github.com/tendermint/tendermint/issues/2791) Functions that start HTTP servers are now blocking:
|
||||
- Impacts `StartHTTPServer`, `StartHTTPAndTLSServer`, and `StartGRPCServer`
|
||||
- These functions now take a `net.Listener` instead of an address
|
||||
- [rpc] [\#2767](https://github.com/tendermint/tendermint/issues/2767) Subscribing to events
|
||||
`NewRound` and `CompleteProposal` return new types `EventDataNewRound` and
|
||||
`EventDataCompleteProposal`, respectively, instead of the generic `EventDataRoundState`. (@kevlubkcm)
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [log] [\#2843](https://github.com/tendermint/tendermint/issues/2843) New `log_format` config option, which can be set to 'plain' for colored
|
||||
text or 'json' for JSON output
|
||||
- [types] [\#2767](https://github.com/tendermint/tendermint/issues/2767) New event types EventDataNewRound (with ProposerInfo) and EventDataCompleteProposal (with BlockID). (@kevlubkcm)
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [dep] [\#2844](https://github.com/tendermint/tendermint/issues/2844) Dependencies are no longer pinned to an exact version in the
|
||||
Gopkg.toml:
|
||||
- Serialization libs are allowed to vary by patch release
|
||||
- Other libs are allowed to vary by minor release
|
||||
- [p2p] [\#2857](https://github.com/tendermint/tendermint/issues/2857) "Send failed" is logged at debug level instead of error.
|
||||
- [rpc] [\#2780](https://github.com/tendermint/tendermint/issues/2780) Add read and write timeouts to HTTP servers
|
||||
- [state] [\#2848](https://github.com/tendermint/tendermint/issues/2848) Make "Update to validators" msg value pretty (@danil-lashin)
|
||||
|
||||
### BUG FIXES:
|
||||
- [consensus] [\#2819](https://github.com/tendermint/tendermint/issues/2819) Don't send proposalHearbeat if not a validator
|
||||
- [docs] [\#2859](https://github.com/tendermint/tendermint/issues/2859) Fix ConsensusParams details in spec
|
||||
- [libs/autofile] [\#2760](https://github.com/tendermint/tendermint/issues/2760) Comment out autofile permissions check - should fix
|
||||
running Tendermint on Windows
|
||||
- [p2p] [\#2869](https://github.com/tendermint/tendermint/issues/2869) Set connection config properly instead of always using default
|
||||
- [p2p/pex] [\#2802](https://github.com/tendermint/tendermint/issues/2802) Seed mode fixes:
|
||||
- Only disconnect from inbound peers
|
||||
- Use FlushStop instead of Sleep to ensure all messages are sent before
|
||||
disconnecting
|
||||
|
||||
## v0.26.2
|
||||
|
||||
*November 15th, 2018*
|
||||
|
||||
Special thanks to external contributors on this release: @hleb-albau, @zhuzeyu
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### FEATURES:
|
||||
|
||||
- [rpc] [\#2582](https://github.com/tendermint/tendermint/issues/2582) Enable CORS on RPC API (@hleb-albau)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [abci] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Unlock mutex in localClient so even when app panics (e.g. during CheckTx), consensus continue working
|
||||
- [abci] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Fix DATA RACE in localClient
|
||||
- [amino] [\#2822](https://github.com/tendermint/tendermint/issues/2822) Update to v0.14.1 to support compiling on 32-bit platforms
|
||||
- [rpc] [\#2748](https://github.com/tendermint/tendermint/issues/2748) Drain channel before calling Unsubscribe(All) in `/broadcast_tx_commit`
|
||||
|
||||
## v0.26.1
|
||||
|
||||
*November 11, 2018*
|
||||
|
||||
Special thanks to external contributors on this release: @katakonst
|
||||
|
||||
Friendly reminder, we have a [bug bounty program](https://hackerone.com/tendermint).
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
- [consensus] [\#2704](https://github.com/tendermint/tendermint/issues/2704) Simplify valid POL round logic
|
||||
- [docs] [\#2749](https://github.com/tendermint/tendermint/issues/2749) Deduplicate some ABCI docs
|
||||
- [mempool] More detailed log messages
|
||||
- [\#2724](https://github.com/tendermint/tendermint/issues/2724)
|
||||
- [\#2762](https://github.com/tendermint/tendermint/issues/2762)
|
||||
|
||||
### BUG FIXES:
|
||||
|
||||
- [autofile] [\#2703](https://github.com/tendermint/tendermint/issues/2703) Do not panic when checking Head size
|
||||
- [crypto/merkle] [\#2756](https://github.com/tendermint/tendermint/issues/2756) Fix crypto/merkle ProofOperators.Verify to check bounds on keypath parts.
|
||||
- [mempool] fix a bug where we create a WAL despite `wal_dir` being empty
|
||||
- [p2p] [\#2771](https://github.com/tendermint/tendermint/issues/2771) Fix `peer-id` label name to `peer_id` in prometheus metrics
|
||||
- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) Fix IDs in peer NodeInfo and require them for addresses
|
||||
in AddressBook
|
||||
- [p2p] [\#2797](https://github.com/tendermint/tendermint/pull/2797) Do not close conn immediately after sending pex addrs in seed mode. Partial fix for [\#2092](https://github.com/tendermint/tendermint/issues/2092).
|
||||
|
||||
## v0.26.0
|
||||
|
||||
*November 2, 2018*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
@bradyjoestar, @connorwstein, @goolAdapter, @HaoyangLiu,
|
||||
@james-ray, @overbool, @phymbert, @Slamper, @Uzair1995, @yutianwu.
|
||||
|
||||
Special thanks to @Slamper for a series of bug reports in our [bug bounty
|
||||
program](https://hackerone.com/tendermint) which are fixed in this release.
|
||||
|
||||
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, and
|
||||
changes the length of hashes used for hashing data structures from 20 to 32
|
||||
bytes.
|
||||
|
||||
See the [UPGRADING.md](UPGRADING.md#v0.26.0) for details on upgrading to the new
|
||||
version.
|
||||
|
||||
Please note that we are still making breaking changes to the protocols.
|
||||
While the new Version fields should help us to keep the software backwards compatible
|
||||
even while upgrading the protocols, we cannot guarantee that new releases will
|
||||
be compatible with old chains just yet. We expect there will be another breaking
|
||||
release or two before the Cosmos Hub launch, but we will otherwise be paying
|
||||
increasing attention to backwards compatibility. Thanks for bearing with us!
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Timeouts are now strings like "3s" and "100ms", 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`
|
||||
* [rpc] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Remove
|
||||
`_params` suffix from fields in `consensus_params`.
|
||||
|
||||
* 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`
|
||||
* [abci] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Updates to ConsensusParams
|
||||
* Remove `Params` suffix from field names
|
||||
* Add `Params` suffix to message types
|
||||
* Add new field and type, `Validator ValidatorParams`, to control what types of validator keys are allowed.
|
||||
|
||||
* Go API
|
||||
* [config] [\#2232](https://github.com/tendermint/tendermint/issues/2232) Timeouts are 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`.
|
||||
* [types] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Rename fields in ConsensusParams to remove `Params` suffixes
|
||||
* [types] [\#2735](https://github.com/tendermint/tendermint/issues/2735) Simplify Proposal message to align with spec
|
||||
|
||||
* Blockchain Protocol
|
||||
* [crypto/tmhash] [\#2732](https://github.com/tendermint/tendermint/issues/2732) TMHASH is now full 32-byte SHA256
|
||||
* All hashes in the block header and Merkle trees are now 32-bytes
|
||||
* PubKey Addresses are still only 20-bytes
|
||||
* [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] 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 from `string` to `byte` and use new
|
||||
`SignedMsgType` to enumerate.
|
||||
* [types] [\#2730](https://github.com/tendermint/tendermint/issues/2730) Use
|
||||
same order for fields in `Vote` as in the SignBytes
|
||||
* [types] [\#2732](https://github.com/tendermint/tendermint/issues/2732) Remove the address 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
|
||||
* [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
|
||||
* [types] [\#2682](https://github.com/tendermint/tendermint/issues/2682) Use proto3 `varint` encoding for ints that are usually unsigned (instead of zigzag encoding).
|
||||
* [types] [\#2636](https://github.com/tendermint/tendermint/issues/2636) Add Validator field to ConsensusParams
|
||||
(Used to control which pubkey types validators can use, by abci type).
|
||||
|
||||
* P2P Protocol
|
||||
* [consensus] [\#2652](https://github.com/tendermint/tendermint/issues/2652)
|
||||
Replace `CommitStepMessage` with `NewValidBlockMessage`
|
||||
* [consensus] [\#2735](https://github.com/tendermint/tendermint/issues/2735) Simplify `Proposal` message to align with spec
|
||||
* [consensus] [\#2730](https://github.com/tendermint/tendermint/issues/2730)
|
||||
Add `Type` field to `Proposal` and use same order of fields as in the
|
||||
SignBytes for both `Proposal` and `Vote`
|
||||
* [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
|
||||
- [docs/architecture] [\#1181](https://github.com/tendermint/tendermint/issues/1181) S
|
||||
plit immutable and mutable parts of priv_validator.json
|
||||
|
||||
### 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
|
||||
- [libs/log] [\#2707](https://github.com/tendermint/tendermint/issues/2707) Add year to log format (@yutianwu)
|
||||
- [tools] [\#2238](https://github.com/tendermint/tendermint/issues/2238) Binary dependencies are now locked to a specific git commit
|
||||
|
||||
### BUG FIXES:
|
||||
- [\#2711](https://github.com/tendermint/tendermint/issues/2711) Validate all incoming reactor messages. Fixes various bugs due to negative ints.
|
||||
- [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
|
||||
- [consensus] [\#2652](https://github.com/tendermint/tendermint/issues/2652) Ensure valid block property with faulty proposer
|
||||
- [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
|
||||
|
||||
## 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*
|
||||
|
||||
BUG FIXES:
|
||||
- [libs/autofile] [\#2261](https://github.com/tendermint/tendermint/issues/2261) Fix log rotation so it actually happens.
|
||||
- Fixes issues with consensus WAL growing unbounded ala [\#2259](https://github.com/tendermint/tendermint/issues/2259)
|
||||
|
||||
## 0.23.0
|
||||
|
||||
*August 5th, 2018*
|
||||
|
||||
This release includes breaking upgrades in our P2P encryption,
|
||||
some ABCI messages, and how we encode time and signatures.
|
||||
|
||||
A few more changes are still coming to the Header, ABCI,
|
||||
and validator set handling to better support light clients, BFT time, and
|
||||
upgrades. Most notably, validator set changes will be delayed by one block (see
|
||||
[#1815][i1815]).
|
||||
|
||||
We also removed `make ensure_deps` in favour of `make get_vendor_deps`.
|
||||
|
||||
BREAKING CHANGES:
|
||||
- [abci] Changed time format from int64 to google.protobuf.Timestamp
|
||||
- [abci] Changed Validators to LastCommitInfo in RequestBeginBlock
|
||||
- [abci] Removed Fee from ResponseDeliverTx and ResponseCheckTx
|
||||
- [crypto] Switch crypto.Signature from interface to []byte for space efficiency
|
||||
[#2128](https://github.com/tendermint/tendermint/pull/2128)
|
||||
- NOTE: this means signatures no longer have the prefix bytes in Amino
|
||||
binary nor the `type` field in Amino JSON. They're just bytes.
|
||||
- [p2p] Remove salsa and ripemd primitives, in favor of using chacha as a stream cipher, and hkdf [#2054](https://github.com/tendermint/tendermint/pull/2054)
|
||||
- [tools] Removed `make ensure_deps` in favor of `make get_vendor_deps`
|
||||
- [types] CanonicalTime uses nanoseconds instead of clipping to ms
|
||||
- breaks serialization/signing of all messages with a timestamp
|
||||
|
||||
FEATURES:
|
||||
- [tools] Added `make check_dep`
|
||||
- ensures gopkg.lock is synced with gopkg.toml
|
||||
- ensures no branches are used in the gopkg.toml
|
||||
|
||||
IMPROVEMENTS:
|
||||
- [blockchain] Improve fast-sync logic
|
||||
[#1805](https://github.com/tendermint/tendermint/pull/1805)
|
||||
- tweak params
|
||||
- only process one block at a time to avoid starving
|
||||
- [common] bit array functions which take in another parameter are now thread safe
|
||||
- [crypto] Switch hkdfchachapoly1305 to xchachapoly1305
|
||||
- [p2p] begin connecting to peers as soon a seed node provides them to you ([#2093](https://github.com/tendermint/tendermint/issues/2093))
|
||||
|
||||
BUG FIXES:
|
||||
- [common] Safely handle cases where atomic write files already exist [#2109](https://github.com/tendermint/tendermint/issues/2109)
|
||||
- [privval] fix a deadline for accepting new connections in socket private
|
||||
validator.
|
||||
- [p2p] Allow startup if a configured seed node's IP can't be resolved ([#1716](https://github.com/tendermint/tendermint/issues/1716))
|
||||
- [node] Fully exit when CTRL-C is pressed even if consensus state panics [#2072](https://github.com/tendermint/tendermint/issues/2072)
|
||||
|
||||
[i1815]: https://github.com/tendermint/tendermint/pull/1815
|
||||
|
||||
## 0.22.8
|
||||
|
||||
*July 26th, 2018*
|
||||
|
||||
BUG FIXES
|
||||
|
||||
- [consensus, blockchain] Fix 0.22.7 below.
|
||||
|
||||
## 0.22.7
|
||||
|
||||
*July 26th, 2018*
|
||||
@@ -36,9 +718,10 @@ BREAKING CHANGES:
|
||||
IMPROVEMENTS:
|
||||
- [abci, libs/common] Generated gogoproto static marshaller methods
|
||||
- [config] Increase default send/recv rates to 5 mB/s
|
||||
- [p2p] reject addresses coming from private peers
|
||||
- [p2p] allow persistent peers to be private
|
||||
|
||||
BUG FIXES
|
||||
BUG FIXES:
|
||||
- [mempool] fixed a race condition when `create_empty_blocks=false` where a
|
||||
transaction is published at an old height.
|
||||
- [p2p] dial external IP setup by `persistent_peers`, not internal NAT IP
|
||||
@@ -579,7 +1262,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.
|
||||
|
23
CHANGELOG_PENDING.md
Normal file
23
CHANGELOG_PENDING.md
Normal file
@@ -0,0 +1,23 @@
|
||||
## v0.29.0
|
||||
|
||||
*TBD*
|
||||
|
||||
Special thanks to external contributors on this release:
|
||||
|
||||
### BREAKING CHANGES:
|
||||
|
||||
* CLI/RPC/Config
|
||||
|
||||
* Apps
|
||||
|
||||
* Go API
|
||||
|
||||
* Blockchain Protocol
|
||||
|
||||
* P2P Protocol
|
||||
|
||||
### FEATURES:
|
||||
|
||||
### IMPROVEMENTS:
|
||||
|
||||
### BUG FIXES:
|
@@ -6,7 +6,7 @@ This code of conduct applies to all projects run by the Tendermint/COSMOS team a
|
||||
|
||||
|
||||
# Conduct
|
||||
## Contact: adrian@tendermint.com
|
||||
## Contact: conduct@tendermint.com
|
||||
|
||||
* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic.
|
||||
|
||||
|
@@ -27,8 +27,8 @@ Of course, replace `ebuchman` with your git handle.
|
||||
|
||||
To pull in updates from the origin repo, run
|
||||
|
||||
* `git fetch upstream`
|
||||
* `git rebase upstream/master` (or whatever branch you want)
|
||||
* `git fetch upstream`
|
||||
* `git rebase upstream/master` (or whatever branch you want)
|
||||
|
||||
Please don't make Pull Requests to `master`.
|
||||
|
||||
@@ -50,6 +50,11 @@ as apps, tools, and the core, should use dep.
|
||||
Run `dep status` to get a list of vendor dependencies that may not be
|
||||
up-to-date.
|
||||
|
||||
When updating dependencies, please only update the particular dependencies you
|
||||
need. Instead of running `dep ensure -update`, which will update anything,
|
||||
specify exactly the dependency you want to update, eg.
|
||||
`dep ensure -update github.com/tendermint/go-amino`.
|
||||
|
||||
## Vagrant
|
||||
|
||||
If you are a [Vagrant](https://www.vagrantup.com/) user, you can get started
|
||||
@@ -64,43 +69,74 @@ vagrant ssh
|
||||
make test
|
||||
```
|
||||
|
||||
## Testing
|
||||
## Changelog
|
||||
|
||||
All repos should be hooked up to [CircleCI](https://circleci.com/).
|
||||
Every fix, improvement, feature, or breaking change should be made in a
|
||||
pull-request that includes an update to the `CHANGELOG_PENDING.md` file.
|
||||
|
||||
If they have `.go` files in the root directory, they will be automatically
|
||||
tested by circle using `go test -v -race ./...`. If not, they will need a
|
||||
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
|
||||
includes its continuous integration status using a badge in the `README.md`.
|
||||
Changelog entries should be formatted as follows:
|
||||
|
||||
```
|
||||
- [module] \#xxx Some description about the change (@contributor)
|
||||
```
|
||||
|
||||
Here, `module` is the part of the code that changed (typically a
|
||||
top-level Go package), `xxx` is the pull-request number, and `contributor`
|
||||
is the author/s of the change.
|
||||
|
||||
It's also acceptable for `xxx` to refer to the relevent issue number, but pull-request
|
||||
numbers are preferred.
|
||||
Note this means pull-requests should be opened first so the changelog can then
|
||||
be updated with the pull-request's number.
|
||||
There is no need to include the full link, as this will be added
|
||||
automatically during release. But please include the backslash and pound, eg. `\#2313`.
|
||||
|
||||
Changelog entries should be ordered alphabetically according to the
|
||||
`module`, and numerically according to the pull-request number.
|
||||
|
||||
Changes with multiple classifications should be doubly included (eg. a bug fix
|
||||
that is also a breaking change should be recorded under both).
|
||||
|
||||
Breaking changes are further subdivided according to the APIs/users they impact.
|
||||
Any change that effects multiple APIs/users should be recorded multiply - for
|
||||
instance, a change to the `Blockchain Protocol` that removes a field from the
|
||||
header should also be recorded under `CLI/RPC/Config` since the field will be
|
||||
removed from the header in rpc responses as well.
|
||||
|
||||
## Branching Model and Release
|
||||
|
||||
User-facing repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/.
|
||||
That is, these repos should be well versioned, and any merge to master requires a version bump and tagged release.
|
||||
|
||||
Libraries need not follow the model strictly, but would be wise to,
|
||||
especially `go-p2p` and `go-rpc`, as their versions are referenced in tendermint core.
|
||||
All repos should adhere to the branching model: http://nvie.com/posts/a-successful-git-branching-model/.
|
||||
This means that all pull-requests should be made against develop. Any merge to
|
||||
master constitutes a tagged release.
|
||||
|
||||
### Development Procedure:
|
||||
- the latest state of development is on `develop`
|
||||
- `develop` must never fail `make test`
|
||||
- no --force onto `develop` (except when reverting a broken commit, which should seldom happen)
|
||||
- never --force onto `develop` (except when reverting a broken commit, which should seldom happen)
|
||||
- create a development branch either on github.com/tendermint/tendermint, or your fork (using `git remote add origin`)
|
||||
- before submitting a pull request, begin `git rebase` on top of `develop`
|
||||
- make changes and update the `CHANGELOG_PENDING.md` to record your change
|
||||
- before submitting a pull request, run `git rebase` on top of the latest `develop`
|
||||
|
||||
### Pull Merge Procedure:
|
||||
- ensure pull branch is rebased on develop
|
||||
- ensure pull branch is based on a recent develop
|
||||
- run `make test` to ensure that all tests pass
|
||||
- merge pull request
|
||||
- the `unstable` branch may be used to aggregate pull merges before testing once
|
||||
- push master may request that pull requests be rebased on top of `unstable`
|
||||
- the `unstable` branch may be used to aggregate pull merges before fixing tests
|
||||
|
||||
### Release Procedure:
|
||||
- start on `develop`
|
||||
- run integration tests (see `test_integrations` in Makefile)
|
||||
- prepare changelog/release issue
|
||||
- prepare changelog:
|
||||
- copy `CHANGELOG_PENDING.md` to top of `CHANGELOG.md`
|
||||
- run `python ./scripts/linkify_changelog.py CHANGELOG.md` to add links for
|
||||
all issues
|
||||
- run `bash ./scripts/authors.sh` to get a list of authors since the latest
|
||||
release, and add the github aliases of external contributors to the top of
|
||||
the changelog. To lookup an alias from an email, try `bash
|
||||
./scripts/authors.sh <email>`
|
||||
- reset the `CHANGELOG_PENDING.md`
|
||||
- bump versions
|
||||
- push to release-vX.X.X to run the extended integration tests on the CI
|
||||
- push to release/vX.X.X to run the extended integration tests on the CI
|
||||
- merge to master
|
||||
- merge master back to develop
|
||||
|
||||
@@ -115,3 +151,13 @@ especially `go-p2p` and `go-rpc`, as their versions are referenced in tendermint
|
||||
- merge hotfix-vX.X.X to master
|
||||
- merge hotfix-vX.X.X to develop
|
||||
- delete the hotfix-vX.X.X branch
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
All repos should be hooked up to [CircleCI](https://circleci.com/).
|
||||
|
||||
If they have `.go` files in the root directory, they will be automatically
|
||||
tested by circle using `go test -v -race ./...`. If not, they will need a
|
||||
`circle.yml`. Ideally, every repo has a `Makefile` that defines `make test` and
|
||||
includes its continuous integration status using a badge in the `README.md`.
|
||||
|
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
# Get the tag from the version, or try to figure it out.
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
|
||||
TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go)
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Please specify a tag."
|
||||
|
@@ -3,7 +3,7 @@ set -e
|
||||
|
||||
# Get the tag from the version, or try to figure it out.
|
||||
if [ -z "$TAG" ]; then
|
||||
TAG=$(awk -F\" '/Version =/ { print $2; exit }' < ../version/version.go)
|
||||
TAG=$(awk -F\" '/TMCoreSemVer =/ { print $2; exit }' < ../version/version.go)
|
||||
fi
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "Please specify a tag."
|
||||
|
254
Gopkg.lock
generated
254
Gopkg.lock
generated
@@ -3,48 +3,56 @@
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
|
||||
name = "github.com/beorn7/perks"
|
||||
packages = ["quantile"]
|
||||
pruneopts = "UT"
|
||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c0decf632843204d2b8781de7b26e7038584e2dcccc7e2f401e88ae85b1df2b7"
|
||||
name = "github.com/btcsuite/btcd"
|
||||
packages = ["btcec"]
|
||||
revision = "f673a4b563b57b9a95832545c878669a7fa801d9"
|
||||
pruneopts = "UT"
|
||||
revision = "67e573d211ace594f1366b4ce9d39726c4b19bd0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:1d8e1cb71c33a9470bbbae09bfec09db43c6bf358dfcae13cd8807c4e2a9a2bf"
|
||||
name = "github.com/btcsuite/btcutil"
|
||||
packages = [
|
||||
"base58",
|
||||
"bech32"
|
||||
"bech32",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
|
||||
name = "github.com/davecgh/go-spew"
|
||||
packages = ["spew"]
|
||||
revision = "346938d642f2ec3594ed81d874461961cd0faa76"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
name = "github.com/ebuchman/fail-test"
|
||||
packages = ["."]
|
||||
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
|
||||
pruneopts = "UT"
|
||||
revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:544229a3ca0fb2dd5ebc2896d3d2ff7ce096d9751635301e44e37e761349ee70"
|
||||
name = "github.com/fortytw2/leaktest"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "a5ef70473c97b71626b9abeda80ee92ba2a7de9e"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:abeb38ade3f32a92943e5be54f55ed6d6e3b6602761d74b4aab4c9dd45c18abd"
|
||||
name = "github.com/fsnotify/fsnotify"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9"
|
||||
version = "v1.4.7"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:fdf5169073fb0ad6dc12a70c249145e30f4058647bea25f0abd48b6d9f228a11"
|
||||
name = "github.com/go-kit/kit"
|
||||
packages = [
|
||||
"log",
|
||||
@@ -53,24 +61,30 @@
|
||||
"metrics",
|
||||
"metrics/discard",
|
||||
"metrics/internal/lv",
|
||||
"metrics/prometheus"
|
||||
"metrics/prometheus",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "4dc7be5d2d12881735283bcab7352178e190fc71"
|
||||
version = "v0.6.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:31a18dae27a29aa074515e43a443abfd2ba6deb6d69309d8d7ce789c45f34659"
|
||||
name = "github.com/go-logfmt/logfmt"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "390ab7935ee28ec6b286364bba9b4dd6410cb3d5"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:586ea76dbd0374d6fb649a91d70d652b7fe0ccffb8910a77468e7702e7901f3d"
|
||||
name = "github.com/go-stack/stack"
|
||||
packages = ["."]
|
||||
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
|
||||
version = "v1.7.0"
|
||||
pruneopts = "UT"
|
||||
revision = "2fee6af1a9795aafbe0253a0cfbdf668e1fb8a9a"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:35621fe20f140f05a0c4ef662c26c0ab4ee50bca78aa30fe87d33120bd28165e"
|
||||
name = "github.com/gogo/protobuf"
|
||||
packages = [
|
||||
"gogoproto",
|
||||
@@ -78,37 +92,44 @@
|
||||
"proto",
|
||||
"protoc-gen-gogo/descriptor",
|
||||
"sortkeys",
|
||||
"types"
|
||||
"types",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "636bf0302bc95575d69441b25a2603156ffdddf1"
|
||||
version = "v1.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:17fe264ee908afc795734e8c4e63db2accabaf57326dbf21763a7d6b86096260"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp"
|
||||
"ptypes/timestamp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265"
|
||||
version = "v1.1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:4a0c6bb4805508a6287675fac876be2ac1182539ca8a32468d8128882e9d5009"
|
||||
name = "github.com/golang/snappy"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:43dd08a10854b2056e615d1b1d22ac94559d822e1f8b6fcc92c1a1057e85188e"
|
||||
name = "github.com/gorilla/websocket"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ea40c24cdbacd054a6ae9de03e62c5f252479b96c716375aace5c120d68647c8"
|
||||
name = "github.com/hashicorp/hcl"
|
||||
packages = [
|
||||
".",
|
||||
@@ -119,154 +140,208 @@
|
||||
"hcl/token",
|
||||
"json/parser",
|
||||
"json/scanner",
|
||||
"json/token"
|
||||
"json/token",
|
||||
]
|
||||
revision = "ef8a98b0bbce4a65b5aa4c368430a80ddc533168"
|
||||
pruneopts = "UT"
|
||||
revision = "8cb6e5b959231cc1119e43259c4a608f9c51a241"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
|
||||
name = "github.com/inconshreveable/mousetrap"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75"
|
||||
version = "v1.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:39b27d1381a30421f9813967a5866fba35dc1d4df43a6eefe3b7a5444cb07214"
|
||||
name = "github.com/jmhodges/levigo"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a64e323dc06b73892e5bb5d040ced475c4645d456038333883f58934abbf6f72"
|
||||
name = "github.com/kr/logfmt"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "b84e30acd515aadc4b783ad4ff83aff3299bdfe0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c568d7727aa262c32bdf8a3f7db83614f7af0ed661474b24588de635c20024c7"
|
||||
name = "github.com/magiconair/properties"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c2353362d570a7bfa228149c62842019201cfb71"
|
||||
version = "v1.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
|
||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||
packages = ["pbutil"]
|
||||
pruneopts = "UT"
|
||||
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:53bc4cd4914cd7cd52139990d5170d6dc99067ae31c56530621b18b35fc30318"
|
||||
name = "github.com/mitchellh/mapstructure"
|
||||
packages = ["."]
|
||||
revision = "f15292f7a699fcc1a38a80977f80a046874ba8ac"
|
||||
pruneopts = "UT"
|
||||
revision = "3536a929edddb9a5b34bd6861dc4a9647cb459fe"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:95741de3af260a92cc5c7f3f3061e85273f5a81b5db20d4bd68da74bd521675e"
|
||||
name = "github.com/pelletier/go-toml"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "c01d1270ff3e442a8a57cddc1c92dc1138598194"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747"
|
||||
name = "github.com/pkg/errors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
|
||||
name = "github.com/pmezard/go-difflib"
|
||||
packages = ["difflib"]
|
||||
pruneopts = "UT"
|
||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:26663fafdea73a38075b07e8e9d82fc0056379d2be8bb4e13899e8fda7c7dd23"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
"prometheus/promhttp"
|
||||
"prometheus/internal",
|
||||
"prometheus/promhttp",
|
||||
]
|
||||
revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632"
|
||||
pruneopts = "UT"
|
||||
revision = "abad2d1bd44235a26707c172eab6bca5bf2dbad3"
|
||||
version = "v0.9.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
|
||||
name = "github.com/prometheus/client_model"
|
||||
packages = ["go"]
|
||||
pruneopts = "UT"
|
||||
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:db712fde5d12d6cdbdf14b777f0c230f4ff5ab0be8e35b239fc319953ed577a4"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
"internal/bitbucket.org/ww/goautoneg",
|
||||
"model"
|
||||
"model",
|
||||
]
|
||||
revision = "7600349dcfe1abd18d72d3a1770870d9800a7801"
|
||||
pruneopts = "UT"
|
||||
revision = "7e9e6cabbd393fc208072eedef99188d0ce788b6"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:ef74914912f99c79434d9c09658274678bc85080ebe3ab32bec3940ebce5e1fc"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
"internal/util",
|
||||
"nfs",
|
||||
"xfs"
|
||||
"xfs",
|
||||
]
|
||||
revision = "ae68e2d4c00fed4943b5f6698d504a5fe083da8a"
|
||||
pruneopts = "UT"
|
||||
revision = "185b4288413d2a0dd0806f78c90dde719829e5ae"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c4556a44e350b50a490544d9b06e9fba9c286c21d6c0e47f54f3a9214597298c"
|
||||
name = "github.com/rcrowley/go-metrics"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "e2704e165165ec55d062f5919b4b29494e9fa790"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:b0c25f00bad20d783d259af2af8666969e2fc343fa0dc9efe52936bbd67fb758"
|
||||
name = "github.com/rs/cors"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "9a47f48565a795472d43519dd49aac781f3034fb"
|
||||
version = "v1.6.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:6a4a11ba764a56d2758899ec6f3848d24698d48442ebce85ee7a3f63284526cd"
|
||||
name = "github.com/spf13/afero"
|
||||
packages = [
|
||||
".",
|
||||
"mem"
|
||||
"mem",
|
||||
]
|
||||
revision = "787d034dfe70e44075ccc060d346146ef53270ad"
|
||||
version = "v1.1.1"
|
||||
pruneopts = "UT"
|
||||
revision = "d40851caa0d747393da1ffb28f7f9d8b4eeffebd"
|
||||
version = "v1.1.2"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:08d65904057412fc0270fc4812a1c90c594186819243160dc779a402d4b6d0bc"
|
||||
name = "github.com/spf13/cast"
|
||||
packages = ["."]
|
||||
revision = "8965335b8c7107321228e3e3702cab9832751bac"
|
||||
version = "v1.2.0"
|
||||
pruneopts = "UT"
|
||||
revision = "8c9545af88b134710ab1cd196795e7f2388358d7"
|
||||
version = "v1.3.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7ffc0983035bc7e297da3688d9fe19d60a420e9c38bef23f845c53788ed6a05e"
|
||||
name = "github.com/spf13/cobra"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b"
|
||||
version = "v0.0.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:68ea4e23713989dc20b1bded5d9da2c5f9be14ff9885beef481848edd18c26cb"
|
||||
name = "github.com/spf13/jwalterweatherman"
|
||||
packages = ["."]
|
||||
revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394"
|
||||
pruneopts = "UT"
|
||||
revision = "4a4406e478ca629068e7768fc33f3f044173c0a6"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:c1b1102241e7f645bc8e0c22ae352e8f0dc6484b6cb4d132fa9f24174e0119e2"
|
||||
name = "github.com/spf13/pflag"
|
||||
packages = ["."]
|
||||
revision = "583c0c0531f06d5278b7d917446061adc344b5cd"
|
||||
version = "v1.0.1"
|
||||
pruneopts = "UT"
|
||||
revision = "298182f68c66c05229eb03ac171abe6e309ee79a"
|
||||
version = "v1.0.3"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:f8e1a678a2571e265f4bf91a3e5e32aa6b1474a55cb0ea849750cc177b664d96"
|
||||
name = "github.com/spf13/viper"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7"
|
||||
version = "v1.0.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:7e8d267900c7fa7f35129a2a37596e38ed0f11ca746d6d9ba727980ee138f9f6"
|
||||
name = "github.com/stretchr/testify"
|
||||
packages = [
|
||||
"assert",
|
||||
"require"
|
||||
"require",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71"
|
||||
version = "v1.2.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:59483b8e8183f10ab21a85ba1f4cbb4a2335d48891801f79ed7b9499f44d383c"
|
||||
name = "github.com/syndtr/goleveldb"
|
||||
packages = [
|
||||
"leveldb",
|
||||
@@ -280,34 +355,36 @@
|
||||
"leveldb/opt",
|
||||
"leveldb/storage",
|
||||
"leveldb/table",
|
||||
"leveldb/util"
|
||||
"leveldb/util",
|
||||
]
|
||||
revision = "c4c61651e9e37fa117f53c5a906d3b63090d8445"
|
||||
pruneopts = "UT"
|
||||
revision = "6b91fda63f2e36186f1c9d0e48578defb69c5d43"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "github.com/tendermint/ed25519"
|
||||
packages = [
|
||||
".",
|
||||
"edwards25519",
|
||||
"extra25519"
|
||||
]
|
||||
revision = "d8387025d2b9d158cf4efb07e7ebf814bcce2057"
|
||||
digest = "1:605b6546f3f43745695298ec2d342d3e952b6d91cdf9f349bea9315f677d759f"
|
||||
name = "github.com/tendermint/btcd"
|
||||
packages = ["btcec"]
|
||||
pruneopts = "UT"
|
||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ad9c4c1a4e7875330b1f62906f2830f043a23edb5db997e3a5ac5d3e6eadf80a"
|
||||
name = "github.com/tendermint/go-amino"
|
||||
packages = ["."]
|
||||
revision = "2106ca61d91029c931fd54968c2bb02dc96b1412"
|
||||
version = "0.10.1"
|
||||
pruneopts = "UT"
|
||||
revision = "dc14acf9ef15f85828bfbc561ed9dd9d2a284885"
|
||||
version = "v0.14.1"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:00d2b3e64cdc3fa69aa250dfbe4cc38c4837d4f37e62279be2ae52107ffbbb44"
|
||||
name = "golang.org/x/crypto"
|
||||
packages = [
|
||||
"bcrypt",
|
||||
"blowfish",
|
||||
"chacha20poly1305",
|
||||
"curve25519",
|
||||
"ed25519",
|
||||
"ed25519/internal/edwards25519",
|
||||
"hkdf",
|
||||
"internal/chacha20",
|
||||
"internal/subtle",
|
||||
@@ -317,11 +394,13 @@
|
||||
"openpgp/errors",
|
||||
"poly1305",
|
||||
"ripemd160",
|
||||
"salsa20/salsa"
|
||||
"salsa20/salsa",
|
||||
]
|
||||
revision = "a2144134853fc9a27a7b1e3eb4f19f1a76df13c9"
|
||||
pruneopts = "UT"
|
||||
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d36f55a999540d29b6ea3c2ea29d71c76b1d9853fdcd3e5c5cb4836f2ba118f1"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
@@ -331,20 +410,24 @@
|
||||
"idna",
|
||||
"internal/timeseries",
|
||||
"netutil",
|
||||
"trace"
|
||||
"trace",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "292b43bbf7cb8d35ddf40f8d5100ef3837cced3f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:6f86e2f2e2217cd4d74dec6786163cf80e4d2b99adb341ecc60a45113b844dca"
|
||||
name = "golang.org/x/sys"
|
||||
packages = [
|
||||
"cpu",
|
||||
"unix"
|
||||
"unix",
|
||||
]
|
||||
revision = "ac767d655b305d4e9612f5f6e33120b9176c4ad4"
|
||||
pruneopts = "UT"
|
||||
revision = "7e31e0c00fa05cb5fbf4347b585621d6709e19a4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
@@ -360,17 +443,22 @@
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable"
|
||||
"unicode/rangetable",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:56b0bca90b7e5d1facf5fbdacba23e4e0ce069d25381b8e2f70ef1e7ebfb9c1a"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
||||
pruneopts = "UT"
|
||||
revision = "b69ba1387ce2108ac9bc8e8e5e5a46e7d5c72313"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:2dab32a43451e320e49608ff4542fdfc653c95dcc35d0065ec9c6c3dd540ed74"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
@@ -397,20 +485,70 @@
|
||||
"stats",
|
||||
"status",
|
||||
"tap",
|
||||
"transport"
|
||||
"transport",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "168a6198bcb0ef175f7dacec0b8691fc141dc9b8"
|
||||
version = "v1.13.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
|
||||
name = "gopkg.in/yaml.v2"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183"
|
||||
version = "v2.2.1"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "8e519c3716c259c6ecdb052889dd3602539fd98a3650dfbf50a4023e087a5d53"
|
||||
input-imports = [
|
||||
"github.com/btcsuite/btcutil/base58",
|
||||
"github.com/btcsuite/btcutil/bech32",
|
||||
"github.com/fortytw2/leaktest",
|
||||
"github.com/go-kit/kit/log",
|
||||
"github.com/go-kit/kit/log/level",
|
||||
"github.com/go-kit/kit/log/term",
|
||||
"github.com/go-kit/kit/metrics",
|
||||
"github.com/go-kit/kit/metrics/discard",
|
||||
"github.com/go-kit/kit/metrics/prometheus",
|
||||
"github.com/go-logfmt/logfmt",
|
||||
"github.com/gogo/protobuf/gogoproto",
|
||||
"github.com/gogo/protobuf/jsonpb",
|
||||
"github.com/gogo/protobuf/proto",
|
||||
"github.com/gogo/protobuf/types",
|
||||
"github.com/golang/protobuf/proto",
|
||||
"github.com/golang/protobuf/ptypes/timestamp",
|
||||
"github.com/gorilla/websocket",
|
||||
"github.com/jmhodges/levigo",
|
||||
"github.com/pkg/errors",
|
||||
"github.com/prometheus/client_golang/prometheus",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"github.com/rcrowley/go-metrics",
|
||||
"github.com/rs/cors",
|
||||
"github.com/spf13/cobra",
|
||||
"github.com/spf13/viper",
|
||||
"github.com/stretchr/testify/assert",
|
||||
"github.com/stretchr/testify/require",
|
||||
"github.com/syndtr/goleveldb/leveldb",
|
||||
"github.com/syndtr/goleveldb/leveldb/errors",
|
||||
"github.com/syndtr/goleveldb/leveldb/iterator",
|
||||
"github.com/syndtr/goleveldb/leveldb/opt",
|
||||
"github.com/tendermint/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",
|
||||
"golang.org/x/crypto/openpgp/armor",
|
||||
"golang.org/x/crypto/ripemd160",
|
||||
"golang.org/x/net/context",
|
||||
"golang.org/x/net/netutil",
|
||||
"google.golang.org/grpc",
|
||||
"google.golang.org/grpc/credentials",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
||||
|
75
Gopkg.toml
75
Gopkg.toml
@@ -10,11 +10,6 @@
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
@@ -25,85 +20,81 @@
|
||||
# unused-packages = true
|
||||
#
|
||||
###########################################################
|
||||
# NOTE: All packages should be pinned to specific versions.
|
||||
# Packages without releases must pin to a commit.
|
||||
|
||||
|
||||
# Allow only patch releases for serialization libraries
|
||||
[[constraint]]
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "=0.6.0"
|
||||
name = "github.com/tendermint/go-amino"
|
||||
version = "~0.14.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gogo/protobuf"
|
||||
version = "=1.1.1"
|
||||
version = "~1.1.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "=1.1.0"
|
||||
version = "~1.1.0"
|
||||
|
||||
# Allow only minor releases for other libraries
|
||||
[[constraint]]
|
||||
name = "github.com/go-kit/kit"
|
||||
version = "^0.6.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/gorilla/websocket"
|
||||
version = "=1.2.0"
|
||||
version = "^1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rs/cors"
|
||||
version = "^1.6.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/pkg/errors"
|
||||
version = "=0.8.0"
|
||||
version = "^0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/cobra"
|
||||
version = "=0.0.1"
|
||||
version = "^0.0.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/spf13/viper"
|
||||
version = "=1.0.0"
|
||||
version = "^1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/stretchr/testify"
|
||||
version = "=1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/tendermint/go-amino"
|
||||
version = "=v0.10.1"
|
||||
version = "^1.2.1"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "=1.13.0"
|
||||
version = "^1.13.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/fortytw2/leaktest"
|
||||
version = "=1.2.0"
|
||||
version = "^1.2.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
version = "^0.9.1"
|
||||
|
||||
###################################
|
||||
## Some repos dont have releases.
|
||||
## Pin to revision
|
||||
|
||||
## We can remove this one by updating protobuf to v1.1.0
|
||||
## but then the grpc tests break with
|
||||
#--- FAIL: TestBroadcastTx (0.01s)
|
||||
#panic: message/group field common.KVPair:bytes without pointer [recovered]
|
||||
# panic: message/group field common.KVPair:bytes without pointer
|
||||
#
|
||||
# ...
|
||||
#
|
||||
# github.com/tendermint/tendermint/rpc/grpc_test.TestBroadcastTx(0xc420a5ab40)
|
||||
# /go/src/github.com/tendermint/tendermint/rpc/grpc/grpc_test.go:29 +0x141
|
||||
[[override]]
|
||||
name = "google.golang.org/genproto"
|
||||
revision = "7fd901a49ba6a7f87732eb344f6e3c5b19d1b200"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/ebuchman/fail-test"
|
||||
revision = "95f809107225be108efcf10a3509e4ea6ceef3c4"
|
||||
name = "golang.org/x/crypto"
|
||||
revision = "505ab145d0a99da450461ae2c1a9f6cd10d1f447"
|
||||
|
||||
[[override]]
|
||||
name = "github.com/jmhodges/levigo"
|
||||
revision = "c42d9e0ca023e2198120196f842701bb4c55d7b9"
|
||||
|
||||
# last revision used by go-crypto
|
||||
[[constraint]]
|
||||
name = "github.com/btcsuite/btcutil"
|
||||
revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4"
|
||||
|
||||
# Haven't made a release since 2016.
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
revision = "ae27198cdd90bf12cd134ad79d1366a6cf49f632"
|
||||
name = "github.com/tendermint/btcd"
|
||||
revision = "e5840949ff4fff0c56f9b6a541e22b63581ea9df"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/rcrowley/go-metrics"
|
||||
|
94
Makefile
94
Makefile
@@ -1,50 +1,61 @@
|
||||
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_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 ensure_deps
|
||||
|
||||
check: check_tools get_vendor_deps
|
||||
|
||||
########################################
|
||||
### Build Tendermint
|
||||
|
||||
build:
|
||||
CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/
|
||||
CGO_ENABLED=0 go build $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o 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
|
||||
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
|
||||
|
||||
install_c:
|
||||
CGO_ENABLED=1 go install $(BUILD_FLAGS) -tags "$(BUILD_TAGS) gcc" ./cmd/tendermint
|
||||
|
||||
########################################
|
||||
### Protobuf
|
||||
|
||||
protoc_all: protoc_libs protoc_abci protoc_grpc
|
||||
protoc_all: protoc_libs protoc_merkle protoc_abci protoc_grpc protoc_proto3types
|
||||
|
||||
%.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
|
||||
protoc $(INCLUDE) $< --gogo_out=plugins=grpc:.
|
||||
@echo "--> adding nolint declarations to protobuf generated files"
|
||||
@awk -i inplace '/^\s*package \w+/ { print "//nolint" }1' $@
|
||||
## 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:.
|
||||
|
||||
########################################
|
||||
### Build ABCI
|
||||
|
||||
# see protobuf section above
|
||||
protoc_abci: abci/types/types.pb.go
|
||||
|
||||
protoc_proto3types: types/proto3/block.pb.go
|
||||
|
||||
build_abci:
|
||||
@go build -i ./abci/cmd/...
|
||||
|
||||
@@ -57,7 +68,7 @@ install_abci:
|
||||
# dist builds binaries for all platforms and packages them for distribution
|
||||
# TODO add abci to these scripts
|
||||
dist:
|
||||
@BUILD_TAGS='$(BUILD_TAGS)' sh -c "'$(CURDIR)/scripts/dist.sh'"
|
||||
@BUILD_TAGS=$(BUILD_TAGS) sh -c "'$(CURDIR)/scripts/dist.sh'"
|
||||
|
||||
########################################
|
||||
### Tools & dependencies
|
||||
@@ -69,36 +80,33 @@ check_tools:
|
||||
|
||||
get_tools:
|
||||
@echo "--> Installing tools"
|
||||
go get -u -v $(GOTOOLS)
|
||||
@gometalinter.v2 --install
|
||||
./scripts/get_tools.sh
|
||||
|
||||
get_dev_tools:
|
||||
@echo "--> Downloading linters (this may take awhile)"
|
||||
$(GOPATH)/src/github.com/alecthomas/gometalinter/scripts/install.sh -b $(GOBIN)
|
||||
|
||||
update_tools:
|
||||
@echo "--> Updating tools"
|
||||
@go get -u $(GOTOOLS)
|
||||
./scripts/get_tools.sh
|
||||
|
||||
#Run this from CI
|
||||
#Update dependencies
|
||||
get_vendor_deps:
|
||||
@rm -rf vendor/
|
||||
@echo "--> Running dep"
|
||||
@dep ensure -vendor-only
|
||||
|
||||
|
||||
#Run this locally.
|
||||
ensure_deps:
|
||||
@rm -rf vendor/
|
||||
@echo "--> Running dep"
|
||||
@dep ensure
|
||||
|
||||
#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
|
||||
@@ -107,7 +115,7 @@ draw_deps:
|
||||
|
||||
get_deps_bin_size:
|
||||
@# Copy of build recipe with additional flags to perform binary size analysis
|
||||
$(eval $(shell go build -work -a $(BUILD_FLAGS) -tags '$(BUILD_TAGS)' -o build/tendermint ./cmd/tendermint/ 2>&1))
|
||||
$(eval $(shell go build -work -a $(BUILD_FLAGS) -tags $(BUILD_TAGS) -o build/tendermint ./cmd/tendermint/ 2>&1))
|
||||
@find $(WORK) -type f -name "*.a" | xargs -I{} du -hxs "{}" | sort -rh | sed -e s:${WORK}/::g > deps_bin_size.log
|
||||
@echo "Results can be found here: $(CURDIR)/deps_bin_size.log"
|
||||
|
||||
@@ -137,6 +145,8 @@ grpc_dbserver:
|
||||
|
||||
protoc_grpc: rpc/grpc/types.pb.go
|
||||
|
||||
protoc_merkle: crypto/merkle/merkle.pb.go
|
||||
|
||||
########################################
|
||||
### Testing
|
||||
|
||||
@@ -180,6 +190,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
|
||||
@@ -207,11 +220,11 @@ vagrant_test:
|
||||
### go tests
|
||||
test:
|
||||
@echo "--> Running go test"
|
||||
@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)
|
||||
|
||||
|
||||
########################################
|
||||
@@ -222,7 +235,7 @@ fmt:
|
||||
|
||||
metalinter:
|
||||
@echo "--> Running linter"
|
||||
@gometalinter.v2 --vendor --deadline=600s --disable-all \
|
||||
@gometalinter $(LINT_FLAGS) --disable-all \
|
||||
--enable=deadcode \
|
||||
--enable=gosimple \
|
||||
--enable=misspell \
|
||||
@@ -251,7 +264,17 @@ 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
|
||||
|
||||
rpc-docs:
|
||||
cat rpc/core/slate_header.txt > $(DESTINATION)
|
||||
godoc2md -template rpc/core/doc_template.txt github.com/tendermint/tendermint/rpc/core | grep -v -e "pipe.go" -e "routes.go" -e "dev.go" | sed 's,/src/target,https://github.com/tendermint/tendermint/tree/master/rpc/core,' >> $(DESTINATION)
|
||||
|
||||
check_dep:
|
||||
dep status >> /dev/null
|
||||
!(grep -n branch Gopkg.toml)
|
||||
|
||||
###########################################################
|
||||
### Docker image
|
||||
@@ -271,6 +294,7 @@ build-linux:
|
||||
build-docker-localnode:
|
||||
cd networks/local
|
||||
make
|
||||
cd -
|
||||
|
||||
# Run a 4-node testnet locally
|
||||
localnet-start: localnet-stop
|
||||
@@ -308,4 +332,4 @@ build-slate:
|
||||
# To avoid unintended conflicts with file names, always add to .PHONY
|
||||
# unless there is a reason not to.
|
||||
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
|
||||
.PHONY: check build build_race build_abci dist install install_abci check_tools get_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all
|
||||
.PHONY: check build build_race build_abci dist install install_abci check_dep check_tools get_tools get_dev_tools update_tools get_vendor_deps draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c
|
||||
|
124
README.md
124
README.md
@@ -1,14 +1,14 @@
|
||||
# Tendermint
|
||||
|
||||
[Byzantine-Fault Tolerant](https://en.wikipedia.org/wiki/Byzantine_fault_tolerance)
|
||||
[State Machine Replication](https://en.wikipedia.org/wiki/State_machine_replication).
|
||||
Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)) for short.
|
||||
[State Machines](https://en.wikipedia.org/wiki/State_machine_replication).
|
||||
Or [Blockchain](https://en.wikipedia.org/wiki/Blockchain_(database)), for short.
|
||||
|
||||
[](https://github.com/tendermint/tendermint/releases/latest)
|
||||
[](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)
|
||||
@@ -24,21 +24,24 @@ and securely replicates it on many machines.
|
||||
|
||||
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
|
||||
|
||||
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) :)
|
||||
please [contact us](mailto:partners@tendermint.com) and [join the chat](https://riot.im/app/#/room/#tendermint:matrix.org).
|
||||
|
||||
## Security
|
||||
|
||||
To report a security vulnerability, see our [bug bounty
|
||||
program](https://tendermint.com/security).
|
||||
program](https://hackerone.com/tendermint)
|
||||
|
||||
For examples of the kinds of bugs we're looking for, see [SECURITY.md](SECURITY.md)
|
||||
|
||||
@@ -46,60 +49,43 @@ 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.11.4 or higher
|
||||
|
||||
## Install
|
||||
## Documentation
|
||||
|
||||
Complete documentation can be found on the [website](https://tendermint.com/docs/).
|
||||
|
||||
### Install
|
||||
|
||||
See the [install instructions](/docs/introduction/install.md)
|
||||
|
||||
## Quick Start
|
||||
### Quick Start
|
||||
|
||||
- [Single node](/docs/using-tendermint.md)
|
||||
- [Local cluster using docker-compose](/networks/local)
|
||||
- [Single node](/docs/introduction/quick-start.md)
|
||||
- [Local cluster using docker-compose](/docs/networks/docker-compose.md)
|
||||
- [Remote cluster using terraform and ansible](/docs/networks/terraform-and-ansible.md)
|
||||
- [Join the public testnet](https://cosmos.network/testnet)
|
||||
|
||||
## Resources
|
||||
|
||||
### Tendermint Core
|
||||
|
||||
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.
|
||||
|
||||
|
||||
### 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)
|
||||
|
||||
### More
|
||||
|
||||
* [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)
|
||||
- [Join the Cosmos testnet](https://cosmos.network/testnet)
|
||||
|
||||
## Contributing
|
||||
|
||||
Yay open source! Please see our [contributing guidelines](CONTRIBUTING.md).
|
||||
Please abide by the [Code of Conduct](CODE_OF_CONDUCT.md) in all interactions,
|
||||
and the [contributing guidelines](CONTRIBUTING.md) when submitting code.
|
||||
|
||||
Join the larger community on the [forum](https://forum.cosmos.network/) and the [chat](https://riot.im/app/#/room/#tendermint:matrix.org).
|
||||
|
||||
To learn more about the structure of the software, watch the [Developer
|
||||
Sessions](https://www.youtube.com/playlist?list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv)
|
||||
and read some [Architectural
|
||||
Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
|
||||
|
||||
Learn more by reading the code and comparing it to the
|
||||
[specification](https://github.com/tendermint/tendermint/tree/develop/docs/spec).
|
||||
|
||||
## Versioning
|
||||
|
||||
### SemVer
|
||||
### Semantic Versioning
|
||||
|
||||
Tendermint uses [SemVer](http://semver.org/) to determine when and how the version changes.
|
||||
Tendermint uses [Semantic Versioning](http://semver.org/) to determine when and how the version changes.
|
||||
According to SemVer, anything in the public API can change at any time before version 1.0.0
|
||||
|
||||
To provide some stability to Tendermint users in these 0.X.X days, the MINOR version is used
|
||||
@@ -114,6 +100,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 +122,40 @@ 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)).
|
||||
|
||||
## Code of Conduct
|
||||
For more information on upgrading, see [UPGRADING.md](./UPGRADING.md)
|
||||
|
||||
## Resources
|
||||
|
||||
### Tendermint Core
|
||||
|
||||
For details about the blockchain data structures and the p2p protocols, see the
|
||||
[Tendermint specification](/docs/spec).
|
||||
|
||||
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), reflection-based proto3, with
|
||||
interfaces
|
||||
* [IAVL](http://github.com/tendermint/iavl), Merkleized IAVL+ Tree implementation
|
||||
|
||||
### Applications
|
||||
|
||||
* [Cosmos SDK](http://github.com/cosmos/cosmos-sdk); a cryptocurrency application framework
|
||||
* [Ethermint](http://github.com/cosmos/ethermint); Ethereum on Tendermint
|
||||
* [Many more](https://tendermint.com/ecosystem)
|
||||
|
||||
### Research
|
||||
|
||||
* [The latest gossip on BFT consensus](https://arxiv.org/abs/1807.04938)
|
||||
* [Master's Thesis on Tendermint](https://atrium.lib.uoguelph.ca/xmlui/handle/10214/9769)
|
||||
* [Original Whitepaper](https://tendermint.com/static/docs/tendermint.pdf)
|
||||
* [Blog](https://blog.cosmos.network/tendermint/home)
|
||||
|
||||
Please read, understand and adhere to our [code of conduct](CODE_OF_CONDUCT.md).
|
||||
|
@@ -1,7 +1,8 @@
|
||||
# Security
|
||||
|
||||
As part of our [Coordinated Vulnerability Disclosure
|
||||
Policy](https://tendermint.com/security), we operate a bug bounty.
|
||||
Policy](https://tendermint.com/security), we operate a [bug
|
||||
bounty](https://hackerone.com/tendermint).
|
||||
See the policy for more details on submissions and rewards.
|
||||
|
||||
Here is a list of examples of the kinds of bugs we're most interested in:
|
||||
|
242
UPGRADING.md
Normal file
242
UPGRADING.md
Normal file
@@ -0,0 +1,242 @@
|
||||
# Upgrading Tendermint Core
|
||||
|
||||
This guide provides steps to be followed when you upgrade your applications to
|
||||
a newer version of Tendermint Core.
|
||||
|
||||
## v0.28.0
|
||||
|
||||
This release breaks the format for the `priv_validator.json` file
|
||||
and the protocol used for the external validator process.
|
||||
It is compatible with v0.27.0 blockchains (neither the BlockProtocol nor the
|
||||
P2PProtocol have changed).
|
||||
|
||||
Please read carefully for details about upgrading.
|
||||
|
||||
**Note:** Backup your `config/priv_validator.json`
|
||||
before proceeding.
|
||||
|
||||
### `priv_validator.json`
|
||||
|
||||
The `config/priv_validator.json` is now two files:
|
||||
`config/priv_validator_key.json` and `data/priv_validator_state.json`.
|
||||
The former contains the key material, the later contains the details on the last
|
||||
message signed.
|
||||
|
||||
When running v0.28.0 for the first time, it will back up any pre-existing
|
||||
`priv_validator.json` file and proceed to split it into the two new files.
|
||||
Upgrading should happen automatically without problem.
|
||||
|
||||
To upgrade manually, use the provided `privValUpgrade.go` script, with exact paths for the old
|
||||
`priv_validator.json` and the locations for the two new files. It's recomended
|
||||
to use the default paths, of `config/priv_validator_key.json` and
|
||||
`data/priv_validator_state.json`, respectively:
|
||||
|
||||
```
|
||||
go run scripts/privValUpgrade.go <old-path> <new-key-path> <new-state-path>
|
||||
```
|
||||
|
||||
### External validator signers
|
||||
|
||||
The Unix and TCP implementations of the remote signing validator
|
||||
have been consolidated into a single implementation.
|
||||
Thus in both cases, the external process is expected to dial
|
||||
Tendermint. This is different from how Unix sockets used to work, where
|
||||
Tendermint dialed the external process.
|
||||
|
||||
The `PubKeyMsg` was also split into separate `Request` and `Response` types
|
||||
for consistency with other messages.
|
||||
|
||||
Note that the TCP sockets don't yet use a persistent key,
|
||||
so while they're encrypted, they can't yet be properly authenticated.
|
||||
See [#3105](https://github.com/tendermint/tendermint/issues/3105).
|
||||
Note the Unix socket has neither encryption nor authentication, but will
|
||||
add a shared-secret in [#3099](https://github.com/tendermint/tendermint/issues/3099).
|
||||
|
||||
## v0.27.0
|
||||
|
||||
This release contains some breaking changes to the block and p2p protocols,
|
||||
but does not change any core data structures, so it should be compatible with
|
||||
existing blockchains from the v0.26 series that only used Ed25519 validator keys.
|
||||
Blockchains using Secp256k1 for validators will not be compatible. This is due
|
||||
to the fact that we now enforce which key types validators can use as a
|
||||
consensus param. The default is Ed25519, and Secp256k1 must be activated
|
||||
explicitly.
|
||||
|
||||
It is recommended to upgrade all nodes at once to avoid incompatibilities at the
|
||||
peer layer - namely, the heartbeat consensus message has been removed (only
|
||||
relevant if `create_empty_blocks=false` or `create_empty_blocks_interval > 0`),
|
||||
and the proposer selection algorithm has changed. Since proposer information is
|
||||
never included in the blockchain, this change only affects the peer layer.
|
||||
|
||||
### Go API Changes
|
||||
|
||||
#### libs/db
|
||||
|
||||
The ReverseIterator API has changed the meaning of `start` and `end`.
|
||||
Before, iteration was from `start` to `end`, where
|
||||
`start > end`. Now, iteration is from `end` to `start`, where `start < end`.
|
||||
The iterator also excludes `end`. This change allows a simplified and more
|
||||
intuitive logic, aligning the semantic meaning of `start` and `end` in the
|
||||
`Iterator` and `ReverseIterator`.
|
||||
|
||||
### Applications
|
||||
|
||||
This release enforces a new consensus parameter, the
|
||||
ValidatorParams.PubKeyTypes. Applications must ensure that they only return
|
||||
validator updates with the allowed PubKeyTypes. If a validator update includes a
|
||||
pubkey type that is not included in the ConsensusParams.Validator.PubKeyTypes,
|
||||
block execution will fail and the consensus will halt.
|
||||
|
||||
By default, only Ed25519 pubkeys may be used for validators. Enabling
|
||||
Secp256k1 requires explicit modification of the ConsensusParams.
|
||||
Please update your application accordingly (ie. restrict validators to only be
|
||||
able to use Ed25519 keys, or explicitly add additional key types to the genesis
|
||||
file).
|
||||
|
||||
## v0.26.0
|
||||
|
||||
This release contains a lot of changes to core data types and protocols. 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 functionality, 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).
|
||||
|
||||
### Other
|
||||
|
||||
All hashes, except for public key addresses, are now 32-bytes.
|
||||
|
||||
## 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.
|
8
Vagrantfile
vendored
8
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
|
||||
@@ -53,6 +53,6 @@ Vagrant.configure("2") do |config|
|
||||
|
||||
# get all deps and tools, ready to install/test
|
||||
su - vagrant -c 'source /home/vagrant/.bash_profile'
|
||||
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_vendor_deps'
|
||||
su - vagrant -c 'cd /home/vagrant/go/src/github.com/tendermint/tendermint && make get_tools && make get_dev_tools && make get_vendor_deps'
|
||||
SHELL
|
||||
end
|
||||
|
155
abci/README.md
155
abci/README.md
@@ -1,7 +1,5 @@
|
||||
# Application BlockChain Interface (ABCI)
|
||||
|
||||
[](https://circleci.com/gh/tendermint/abci)
|
||||
|
||||
Blockchains are systems for multi-master state machine replication.
|
||||
**ABCI** is an interface that defines the boundary between the replication engine (the blockchain),
|
||||
and the state machine (the application).
|
||||
@@ -12,157 +10,28 @@ Previously, the ABCI was referred to as TMSP.
|
||||
|
||||
The community has provided a number of addtional implementations, see the [Tendermint Ecosystem](https://tendermint.com/ecosystem)
|
||||
|
||||
|
||||
## Installation & Usage
|
||||
|
||||
To get up and running quickly, see the [getting started guide](../docs/app-dev/getting-started.md) along with the [abci-cli documentation](../docs/app-dev/abci-cli.md) which will go through the examples found in the [examples](./example/) directory.
|
||||
|
||||
## Specification
|
||||
|
||||
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).
|
||||
- [The main spec](../docs/spec/abci/abci.md)
|
||||
- [A protobuf file](./types/types.proto)
|
||||
- [A Go interface](./types/application.go)
|
||||
|
||||
For more background information on ABCI, motivations, and tendermint, please visit [the documentation](http://tendermint.readthedocs.io/en/master/).
|
||||
The two guides to focus on are the `Application Development Guide` and `Using ABCI-CLI`.
|
||||
## Protocol Buffers
|
||||
|
||||
|
||||
## Protocl Buffers
|
||||
|
||||
To compile the protobuf file, run:
|
||||
To compile the protobuf file, run (from the root of the repo):
|
||||
|
||||
```
|
||||
make protoc
|
||||
make protoc_abci
|
||||
```
|
||||
|
||||
See `protoc --help` and [the Protocol Buffers site](https://developers.google.com/protocol-buffers)
|
||||
for details on compiling for other languages. Note we also include a [GRPC](http://www.grpc.io/docs)
|
||||
for details on compiling for other languages. Note we also include a [GRPC](https://www.grpc.io/docs)
|
||||
service definition.
|
||||
|
||||
## Install ABCI-CLI
|
||||
|
||||
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
|
||||
make get_vendor_deps
|
||||
make install
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
We provide three implementations of the ABCI in Go:
|
||||
|
||||
- Golang in-process
|
||||
- ABCI-socket
|
||||
- GRPC
|
||||
|
||||
Note the GRPC version is maintained primarily to simplify onboarding and prototyping and is not receiving the same
|
||||
attention to security and performance as the others
|
||||
|
||||
### In Process
|
||||
|
||||
The simplest implementation just uses function calls within Go.
|
||||
This means ABCI applications written in Golang can be compiled with TendermintCore and run as a single binary.
|
||||
|
||||
See the [examples](#examples) below for more information.
|
||||
|
||||
### Socket (TSP)
|
||||
|
||||
ABCI is best implemented as a streaming protocol.
|
||||
The socket implementation provides for asynchronous, ordered message passing over unix or tcp.
|
||||
Messages are serialized using Protobuf3 and length-prefixed with a [signed Varint](https://developers.google.com/protocol-buffers/docs/encoding?csw=1#signed-integers)
|
||||
|
||||
For example, if the Protobuf3 encoded ABCI message is `0xDEADBEEF` (4 bytes), the length-prefixed message is `0x08DEADBEEF`, since `0x08` is the signed varint
|
||||
encoding of `4`. If the Protobuf3 encoded ABCI message is 65535 bytes long, the length-prefixed message would be like `0xFEFF07...`.
|
||||
|
||||
Note the benefit of using this `varint` encoding over the old version (where integers were encoded as `<len of len><big endian len>` is that
|
||||
it is the standard way to encode integers in Protobuf. It is also generally shorter.
|
||||
|
||||
### GRPC
|
||||
|
||||
GRPC is an rpc framework native to Protocol Buffers with support in many languages.
|
||||
Implementing the ABCI using GRPC can allow for faster prototyping, but is expected to be much slower than
|
||||
the ordered, asynchronous socket protocol. The implementation has also not received as much testing or review.
|
||||
|
||||
Note the length-prefixing used in the socket implementation does not apply for GRPC.
|
||||
|
||||
## Usage
|
||||
|
||||
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.
|
||||
|
||||
### Examples
|
||||
|
||||
Check out the variety of example applications in the [example directory](example/).
|
||||
It also contains the code refered to by the `counter` and `kvstore` apps; these apps come
|
||||
built into the `abci-cli` binary.
|
||||
|
||||
#### Counter
|
||||
|
||||
The `abci-cli counter` application illustrates nonce checking in transactions. It's code looks like:
|
||||
|
||||
```golang
|
||||
func cmdCounter(cmd *cobra.Command, args []string) error {
|
||||
|
||||
app := counter.NewCounterApplication(flagSerial)
|
||||
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
|
||||
// Start the listener
|
||||
srv, err := server.NewServer(flagAddrC, flagAbci, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.SetLogger(logger.With("module", "abci-server"))
|
||||
if err := srv.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
srv.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
and can be found in [this file](cmd/abci-cli/abci-cli.go).
|
||||
|
||||
#### kvstore
|
||||
|
||||
The `abci-cli kvstore` application, which illustrates a simple key-value Merkle tree
|
||||
|
||||
```golang
|
||||
func cmdKVStore(cmd *cobra.Command, args []string) error {
|
||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
|
||||
|
||||
// Create the application - in memory or persisted to disk
|
||||
var app types.Application
|
||||
if flagPersist == "" {
|
||||
app = kvstore.NewKVStoreApplication()
|
||||
} else {
|
||||
app = kvstore.NewPersistentKVStoreApplication(flagPersist)
|
||||
app.(*kvstore.PersistentKVStoreApplication).SetLogger(logger.With("module", "kvstore"))
|
||||
}
|
||||
|
||||
// Start the listener
|
||||
srv, err := server.NewServer(flagAddrD, flagAbci, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
srv.SetLogger(logger.With("module", "abci-server"))
|
||||
if err := srv.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait forever
|
||||
cmn.TrapSignal(func() {
|
||||
// Cleanup
|
||||
srv.Stop()
|
||||
})
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
@@ -105,8 +105,8 @@ func (reqRes *ReqRes) SetCallback(cb func(res *types.Response)) {
|
||||
return
|
||||
}
|
||||
|
||||
defer reqRes.mtx.Unlock()
|
||||
reqRes.cb = cb
|
||||
reqRes.mtx.Unlock()
|
||||
}
|
||||
|
||||
func (reqRes *ReqRes) GetCallback() func(*types.Response) {
|
||||
|
@@ -22,6 +22,7 @@ type grpcClient struct {
|
||||
mustConnect bool
|
||||
|
||||
client types.ABCIApplicationClient
|
||||
conn *grpc.ClientConn
|
||||
|
||||
mtx sync.Mutex
|
||||
addr string
|
||||
@@ -53,17 +54,18 @@ RETRY_LOOP:
|
||||
if cli.mustConnect {
|
||||
return err
|
||||
}
|
||||
cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr))
|
||||
cli.Logger.Error(fmt.Sprintf("abci.grpcClient failed to connect to %v. Retrying...\n", cli.addr), "err", err)
|
||||
time.Sleep(time.Second * dialRetryIntervalSeconds)
|
||||
continue RETRY_LOOP
|
||||
}
|
||||
|
||||
cli.Logger.Info("Dialed server. Waiting for echo.", "addr", cli.addr)
|
||||
client := types.NewABCIApplicationClient(conn)
|
||||
cli.conn = conn
|
||||
|
||||
ENSURE_CONNECTED:
|
||||
for {
|
||||
_, err := client.Echo(context.Background(), &types.RequestEcho{"hello"}, grpc.FailFast(true))
|
||||
_, err := client.Echo(context.Background(), &types.RequestEcho{Message: "hello"}, grpc.FailFast(true))
|
||||
if err == nil {
|
||||
break ENSURE_CONNECTED
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -111,8 +111,8 @@ func (cli *grpcClient) Error() error {
|
||||
// NOTE: callback may get internally generated flush responses.
|
||||
func (cli *grpcClient) SetResponseCallback(resCb Callback) {
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
cli.resCb = resCb
|
||||
cli.mtx.Unlock()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
@@ -129,7 +129,7 @@ func (cli *grpcClient) EchoAsync(msg string) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Echo{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Echo{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) FlushAsync() *ReqRes {
|
||||
@@ -138,7 +138,7 @@ func (cli *grpcClient) FlushAsync() *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Flush{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Flush{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
|
||||
@@ -147,7 +147,7 @@ func (cli *grpcClient) InfoAsync(params types.RequestInfo) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Info{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Info{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes {
|
||||
@@ -156,7 +156,7 @@ func (cli *grpcClient) SetOptionAsync(params types.RequestSetOption) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_SetOption{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_SetOption{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes {
|
||||
@@ -165,7 +165,7 @@ func (cli *grpcClient) DeliverTxAsync(tx []byte) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_DeliverTx{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_DeliverTx{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes {
|
||||
@@ -174,7 +174,7 @@ func (cli *grpcClient) CheckTxAsync(tx []byte) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_CheckTx{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_CheckTx{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
|
||||
@@ -183,7 +183,7 @@ func (cli *grpcClient) QueryAsync(params types.RequestQuery) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Query{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Query{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) CommitAsync() *ReqRes {
|
||||
@@ -192,7 +192,7 @@ func (cli *grpcClient) CommitAsync() *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_Commit{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_Commit{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
|
||||
@@ -201,7 +201,7 @@ func (cli *grpcClient) InitChainAsync(params types.RequestInitChain) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_InitChain{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_InitChain{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
|
||||
@@ -210,7 +210,7 @@ func (cli *grpcClient) BeginBlockAsync(params types.RequestBeginBlock) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_BeginBlock{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_BeginBlock{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes {
|
||||
@@ -219,7 +219,7 @@ func (cli *grpcClient) EndBlockAsync(params types.RequestEndBlock) *ReqRes {
|
||||
if err != nil {
|
||||
cli.StopForError(err)
|
||||
}
|
||||
return cli.finishAsyncCall(req, &types.Response{&types.Response_EndBlock{res}})
|
||||
return cli.finishAsyncCall(req, &types.Response{Value: &types.Response_EndBlock{res}})
|
||||
}
|
||||
|
||||
func (cli *grpcClient) finishAsyncCall(req *types.Request, res *types.Response) *ReqRes {
|
||||
|
@@ -9,8 +9,13 @@ import (
|
||||
|
||||
var _ Client = (*localClient)(nil)
|
||||
|
||||
// NOTE: use defer to unlock mutex because Application might panic (e.g., in
|
||||
// case of malicious tx or query). It only makes sense for publicly exposed
|
||||
// methods like CheckTx (/broadcast_tx_* RPC endpoint) or Query (/abci_query
|
||||
// RPC endpoint), but defers are used everywhere for the sake of consistency.
|
||||
type localClient struct {
|
||||
cmn.BaseService
|
||||
|
||||
mtx *sync.Mutex
|
||||
types.Application
|
||||
Callback
|
||||
@@ -30,8 +35,8 @@ func NewLocalClient(mtx *sync.Mutex, app types.Application) *localClient {
|
||||
|
||||
func (app *localClient) SetResponseCallback(cb Callback) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
app.Callback = cb
|
||||
app.mtx.Unlock()
|
||||
}
|
||||
|
||||
// TODO: change types.Application to include Error()?
|
||||
@@ -45,6 +50,9 @@ func (app *localClient) FlushAsync() *ReqRes {
|
||||
}
|
||||
|
||||
func (app *localClient) EchoAsync(msg string) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
return app.callback(
|
||||
types.ToRequestEcho(msg),
|
||||
types.ToResponseEcho(msg),
|
||||
@@ -53,8 +61,9 @@ func (app *localClient) EchoAsync(msg string) *ReqRes {
|
||||
|
||||
func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Info(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestInfo(req),
|
||||
types.ToResponseInfo(res),
|
||||
@@ -63,8 +72,9 @@ func (app *localClient) InfoAsync(req types.RequestInfo) *ReqRes {
|
||||
|
||||
func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.SetOption(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestSetOption(req),
|
||||
types.ToResponseSetOption(res),
|
||||
@@ -73,8 +83,9 @@ func (app *localClient) SetOptionAsync(req types.RequestSetOption) *ReqRes {
|
||||
|
||||
func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.DeliverTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestDeliverTx(tx),
|
||||
types.ToResponseDeliverTx(res),
|
||||
@@ -83,8 +94,9 @@ func (app *localClient) DeliverTxAsync(tx []byte) *ReqRes {
|
||||
|
||||
func (app *localClient) CheckTxAsync(tx []byte) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.CheckTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestCheckTx(tx),
|
||||
types.ToResponseCheckTx(res),
|
||||
@@ -93,8 +105,9 @@ func (app *localClient) CheckTxAsync(tx []byte) *ReqRes {
|
||||
|
||||
func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Query(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestQuery(req),
|
||||
types.ToResponseQuery(res),
|
||||
@@ -103,8 +116,9 @@ func (app *localClient) QueryAsync(req types.RequestQuery) *ReqRes {
|
||||
|
||||
func (app *localClient) CommitAsync() *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Commit()
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestCommit(),
|
||||
types.ToResponseCommit(res),
|
||||
@@ -113,19 +127,20 @@ func (app *localClient) CommitAsync() *ReqRes {
|
||||
|
||||
func (app *localClient) InitChainAsync(req types.RequestInitChain) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.InitChain(req)
|
||||
reqRes := app.callback(
|
||||
return app.callback(
|
||||
types.ToRequestInitChain(req),
|
||||
types.ToResponseInitChain(res),
|
||||
)
|
||||
app.mtx.Unlock()
|
||||
return reqRes
|
||||
}
|
||||
|
||||
func (app *localClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.BeginBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestBeginBlock(req),
|
||||
types.ToResponseBeginBlock(res),
|
||||
@@ -134,8 +149,9 @@ func (app *localClient) BeginBlockAsync(req types.RequestBeginBlock) *ReqRes {
|
||||
|
||||
func (app *localClient) EndBlockAsync(req types.RequestEndBlock) *ReqRes {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.EndBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return app.callback(
|
||||
types.ToRequestEndBlock(req),
|
||||
types.ToResponseEndBlock(res),
|
||||
@@ -149,69 +165,78 @@ func (app *localClient) FlushSync() error {
|
||||
}
|
||||
|
||||
func (app *localClient) EchoSync(msg string) (*types.ResponseEcho, error) {
|
||||
return &types.ResponseEcho{msg}, nil
|
||||
return &types.ResponseEcho{Message: msg}, nil
|
||||
}
|
||||
|
||||
func (app *localClient) InfoSync(req types.RequestInfo) (*types.ResponseInfo, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Info(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) SetOptionSync(req types.RequestSetOption) (*types.ResponseSetOption, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.SetOption(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) DeliverTxSync(tx []byte) (*types.ResponseDeliverTx, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.DeliverTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) CheckTxSync(tx []byte) (*types.ResponseCheckTx, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.CheckTx(tx)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) QuerySync(req types.RequestQuery) (*types.ResponseQuery, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Query(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) CommitSync() (*types.ResponseCommit, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.Commit()
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) InitChainSync(req types.RequestInitChain) (*types.ResponseInitChain, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.InitChain(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) BeginBlockSync(req types.RequestBeginBlock) (*types.ResponseBeginBlock, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.BeginBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (app *localClient) EndBlockSync(req types.RequestEndBlock) (*types.ResponseEndBlock, error) {
|
||||
app.mtx.Lock()
|
||||
defer app.mtx.Unlock()
|
||||
|
||||
res := app.Application.EndBlock(req)
|
||||
app.mtx.Unlock()
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
|
@@ -67,7 +67,7 @@ RETRY_LOOP:
|
||||
if cli.mustConnect {
|
||||
return err
|
||||
}
|
||||
cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying...", cli.addr))
|
||||
cli.Logger.Error(fmt.Sprintf("abci.socketClient failed to connect to %v. Retrying...", cli.addr), "err", err)
|
||||
time.Sleep(time.Second * dialRetryIntervalSeconds)
|
||||
continue RETRY_LOOP
|
||||
}
|
||||
@@ -118,8 +118,8 @@ func (cli *socketClient) Error() error {
|
||||
// NOTE: callback may get internally generated flush responses.
|
||||
func (cli *socketClient) SetResponseCallback(resCb Callback) {
|
||||
cli.mtx.Lock()
|
||||
defer cli.mtx.Unlock()
|
||||
cli.resCb = resCb
|
||||
cli.mtx.Unlock()
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
@@ -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
|
||||
@@ -57,7 +58,7 @@ var RootCmd = &cobra.Command{
|
||||
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
|
||||
switch cmd.Use {
|
||||
case "counter", "kvstore", "dummy": // for the examples apps, don't pre-run
|
||||
case "counter", "kvstore": // for the examples apps, don't pre-run
|
||||
return nil
|
||||
case "version": // skip running for version command
|
||||
return nil
|
||||
@@ -100,7 +101,7 @@ type queryResponse struct {
|
||||
Key []byte
|
||||
Value []byte
|
||||
Height int64
|
||||
Proof []byte
|
||||
Proof *merkle.Proof
|
||||
}
|
||||
|
||||
func Execute() error {
|
||||
@@ -126,10 +127,6 @@ func addCounterFlags() {
|
||||
counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions")
|
||||
}
|
||||
|
||||
func addDummyFlags() {
|
||||
dummyCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
||||
}
|
||||
|
||||
func addKVStoreFlags() {
|
||||
kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database")
|
||||
}
|
||||
@@ -151,10 +148,6 @@ func addCommands() {
|
||||
// examples
|
||||
addCounterFlags()
|
||||
RootCmd.AddCommand(counterCmd)
|
||||
// deprecated, left for backwards compatibility
|
||||
addDummyFlags()
|
||||
RootCmd.AddCommand(dummyCmd)
|
||||
// replaces dummy, see issue #196
|
||||
addKVStoreFlags()
|
||||
RootCmd.AddCommand(kvstoreCmd)
|
||||
}
|
||||
@@ -290,18 +283,6 @@ var counterCmd = &cobra.Command{
|
||||
},
|
||||
}
|
||||
|
||||
// deprecated, left for backwards compatibility
|
||||
var dummyCmd = &cobra.Command{
|
||||
Use: "dummy",
|
||||
Deprecated: "use: [abci-cli kvstore] instead",
|
||||
Short: "ABCI demo example",
|
||||
Long: "ABCI demo example",
|
||||
Args: cobra.ExactArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return cmdKVStore(cmd, args)
|
||||
},
|
||||
}
|
||||
|
||||
var kvstoreCmd = &cobra.Command{
|
||||
Use: "kvstore",
|
||||
Short: "ABCI demo example",
|
||||
@@ -477,11 +458,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 +467,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
|
||||
}
|
||||
|
||||
@@ -514,7 +503,7 @@ func cmdInfo(cmd *cobra.Command, args []string) error {
|
||||
if len(args) == 1 {
|
||||
version = args[0]
|
||||
}
|
||||
res, err := client.InfoSync(types.RequestInfo{version})
|
||||
res, err := client.InfoSync(types.RequestInfo{Version: version})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -537,7 +526,7 @@ func cmdSetOption(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
key, val := args[0], args[1]
|
||||
_, err := client.SetOptionSync(types.RequestSetOption{key, val})
|
||||
_, err := client.SetOptionSync(types.RequestSetOption{Key: key, Value: val})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -740,7 +729,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)}
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
@@ -37,13 +39,13 @@ 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)
|
||||
server.SetLogger(log.TestingLogger().With("module", "abci-server"))
|
||||
if err := server.Start(); err != nil {
|
||||
t.Fatalf("Error starting socket server: %v", err.Error())
|
||||
require.NoError(t, err, "Error starting socket server")
|
||||
}
|
||||
defer server.Stop()
|
||||
|
||||
@@ -70,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
|
||||
@@ -132,7 +134,7 @@ func testGRPCSync(t *testing.T, app *types.GRPCApplication) {
|
||||
// Write requests
|
||||
for counter := 0; counter < numDeliverTxs; counter++ {
|
||||
// Send request
|
||||
response, err := client.DeliverTx(context.Background(), &types.RequestDeliverTx{[]byte("test")})
|
||||
response, err := client.DeliverTx(context.Background(), &types.RequestDeliverTx{Tx: []byte("test")})
|
||||
if err != nil {
|
||||
t.Fatalf("Error in GRPC DeliverTx: %v", err.Error())
|
||||
}
|
||||
@@ -146,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"
|
||||
@@ -90,8 +91,8 @@ func TestPersistentKVStoreInfo(t *testing.T) {
|
||||
header := types.Header{
|
||||
Height: int64(height),
|
||||
}
|
||||
kvstore.BeginBlock(types.RequestBeginBlock{hash, header, nil, nil})
|
||||
kvstore.EndBlock(types.RequestEndBlock{header.Height})
|
||||
kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header})
|
||||
kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
|
||||
kvstore.Commit()
|
||||
|
||||
resInfo = kvstore.Info(types.RequestInfo{})
|
||||
@@ -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")
|
||||
@@ -176,13 +177,13 @@ func makeApplyBlock(t *testing.T, kvstore types.Application, heightInt int, diff
|
||||
Height: height,
|
||||
}
|
||||
|
||||
kvstore.BeginBlock(types.RequestBeginBlock{hash, header, nil, nil})
|
||||
kvstore.BeginBlock(types.RequestBeginBlock{Hash: hash, Header: header})
|
||||
for _, tx := range txs {
|
||||
if r := kvstore.DeliverTx(tx); r.IsErr() {
|
||||
t.Fatal(r)
|
||||
}
|
||||
}
|
||||
resEndBlock := kvstore.EndBlock(types.RequestEndBlock{header.Height})
|
||||
resEndBlock := kvstore.EndBlock(types.RequestEndBlock{Height: header.Height})
|
||||
kvstore.Commit()
|
||||
|
||||
valsEqual(t, diff, resEndBlock.ValidatorUpdates)
|
||||
@@ -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,
|
||||
|
@@ -26,7 +26,7 @@ func startClient(abciType string) abcicli.Client {
|
||||
}
|
||||
|
||||
func setOption(client abcicli.Client, key, value string) {
|
||||
_, err := client.SetOptionSync(types.RequestSetOption{key, value})
|
||||
_, err := client.SetOptionSync(types.RequestSetOption{Key: key, Value: value})
|
||||
if err != nil {
|
||||
panicf("setting %v=%v: \nerr: %v", key, value, err)
|
||||
}
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -85,7 +85,7 @@ func NewGRPCApplication(app Application) *GRPCApplication {
|
||||
}
|
||||
|
||||
func (app *GRPCApplication) Echo(ctx context.Context, req *RequestEcho) (*ResponseEcho, error) {
|
||||
return &ResponseEcho{req.Message}, nil
|
||||
return &ResponseEcho{Message: req.Message}, nil
|
||||
}
|
||||
|
||||
func (app *GRPCApplication) Flush(ctx context.Context, req *RequestFlush) (*ResponseFlush, error) {
|
||||
|
@@ -71,7 +71,7 @@ func encodeVarint(w io.Writer, i int64) (err error) {
|
||||
|
||||
func ToRequestEcho(message string) *Request {
|
||||
return &Request{
|
||||
Value: &Request_Echo{&RequestEcho{message}},
|
||||
Value: &Request_Echo{&RequestEcho{Message: message}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,13 +95,13 @@ func ToRequestSetOption(req RequestSetOption) *Request {
|
||||
|
||||
func ToRequestDeliverTx(tx []byte) *Request {
|
||||
return &Request{
|
||||
Value: &Request_DeliverTx{&RequestDeliverTx{tx}},
|
||||
Value: &Request_DeliverTx{&RequestDeliverTx{Tx: tx}},
|
||||
}
|
||||
}
|
||||
|
||||
func ToRequestCheckTx(tx []byte) *Request {
|
||||
return &Request{
|
||||
Value: &Request_CheckTx{&RequestCheckTx{tx}},
|
||||
Value: &Request_CheckTx{&RequestCheckTx{Tx: tx}},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,13 +139,13 @@ func ToRequestEndBlock(req RequestEndBlock) *Request {
|
||||
|
||||
func ToResponseException(errStr string) *Response {
|
||||
return &Response{
|
||||
Value: &Response_Exception{&ResponseException{errStr}},
|
||||
Value: &Response_Exception{&ResponseException{Error: errStr}},
|
||||
}
|
||||
}
|
||||
|
||||
func ToResponseEcho(message string) *Response {
|
||||
return &Response{
|
||||
Value: &Response_Echo{&ResponseEcho{message}},
|
||||
Value: &Response_Echo{&ResponseEcho{Message: message}},
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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,9 +83,8 @@ 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")},
|
||||
},
|
||||
// Fee: cmn.KI64Pair{
|
||||
},
|
||||
// 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
@@ -4,7 +4,9 @@ package types;
|
||||
// For more information on gogo.proto, see:
|
||||
// https://github.com/gogo/protobuf/blob/master/extensions.md
|
||||
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "github.com/tendermint/tendermint/libs/common/types.proto";
|
||||
import "github.com/tendermint/tendermint/crypto/merkle/merkle.proto";
|
||||
|
||||
// This file is copied from http://github.com/tendermint/abci
|
||||
// NOTE: When using custom types, mind the warnings.
|
||||
@@ -47,6 +49,8 @@ message RequestFlush {
|
||||
|
||||
message RequestInfo {
|
||||
string version = 1;
|
||||
uint64 block_version = 2;
|
||||
uint64 p2p_version = 3;
|
||||
}
|
||||
|
||||
// nondeterministic
|
||||
@@ -56,10 +60,10 @@ message RequestSetOption {
|
||||
}
|
||||
|
||||
message RequestInitChain {
|
||||
int64 time = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -73,7 +77,7 @@ message RequestQuery {
|
||||
message RequestBeginBlock {
|
||||
bytes hash = 1;
|
||||
Header header = 2 [(gogoproto.nullable)=false];
|
||||
repeated SigningValidator validators = 3 [(gogoproto.nullable)=false];
|
||||
LastCommitInfo last_commit_info = 3 [(gogoproto.nullable)=false];
|
||||
repeated Evidence byzantine_validators = 4 [(gogoproto.nullable)=false];
|
||||
}
|
||||
|
||||
@@ -126,9 +130,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
|
||||
@@ -141,7 +148,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 {
|
||||
@@ -152,8 +159,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 {
|
||||
@@ -168,7 +176,7 @@ message ResponseCheckTx {
|
||||
int64 gas_wanted = 5;
|
||||
int64 gas_used = 6;
|
||||
repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
|
||||
common.KI64Pair fee = 8 [(gogoproto.nullable)=false];
|
||||
string codespace = 8;
|
||||
}
|
||||
|
||||
message ResponseDeliverTx {
|
||||
@@ -179,11 +187,11 @@ message ResponseDeliverTx {
|
||||
int64 gas_wanted = 5;
|
||||
int64 gas_used = 6;
|
||||
repeated common.KVPair tags = 7 [(gogoproto.nullable)=false, (gogoproto.jsontag)="tags,omitempty"];
|
||||
common.KI64Pair fee = 8 [(gogoproto.nullable)=false];
|
||||
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"];
|
||||
}
|
||||
@@ -199,63 +207,97 @@ message ResponseCommit {
|
||||
// ConsensusParams contains all consensus-relevant parameters
|
||||
// that can be adjusted by the abci app
|
||||
message ConsensusParams {
|
||||
BlockSize block_size = 1;
|
||||
TxSize tx_size = 2;
|
||||
BlockGossip block_gossip = 3;
|
||||
BlockSizeParams block_size = 1;
|
||||
EvidenceParams evidence = 2;
|
||||
ValidatorParams validator = 3;
|
||||
}
|
||||
|
||||
// BlockSize contain limits on the block size.
|
||||
message BlockSize {
|
||||
int32 max_bytes = 1;
|
||||
int32 max_txs = 2;
|
||||
int64 max_gas = 3;
|
||||
}
|
||||
|
||||
// TxSize contain limits on the tx size.
|
||||
message TxSize {
|
||||
int32 max_bytes = 1;
|
||||
// BlockSize contains limits on the block size.
|
||||
message BlockSizeParams {
|
||||
// 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;
|
||||
}
|
||||
|
||||
// ValidatorParams contains limits on validators.
|
||||
message ValidatorParams {
|
||||
repeated string pub_key_types = 1;
|
||||
}
|
||||
|
||||
message LastCommitInfo {
|
||||
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;
|
||||
int64 time = 3;
|
||||
// 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;
|
||||
}
|
||||
@@ -269,7 +311,7 @@ message Evidence {
|
||||
string type = 1;
|
||||
Validator validator = 2 [(gogoproto.nullable)=false];
|
||||
int64 height = 3;
|
||||
int64 time = 4;
|
||||
google.protobuf.Timestamp time = 4 [(gogoproto.nullable)=false, (gogoproto.stdtime)=true];
|
||||
int64 total_voting_power = 5;
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@@ -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() {
|
||||
|
@@ -29,10 +29,10 @@ eg, L = latency = 0.1s
|
||||
*/
|
||||
|
||||
const (
|
||||
requestIntervalMS = 100
|
||||
maxTotalRequesters = 1000
|
||||
requestIntervalMS = 2
|
||||
maxTotalRequesters = 600
|
||||
maxPendingRequests = maxTotalRequesters
|
||||
maxPendingRequestsPerPeer = 50
|
||||
maxPendingRequestsPerPeer = 20
|
||||
|
||||
// Minimum recv rate to ensure we're receiving blocks from a peer fast
|
||||
// enough. If a peer is not sending us data at at least that rate, we
|
||||
@@ -168,9 +168,12 @@ func (pool *BlockPool) IsCaughtUp() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// some conditions to determine if we're caught up
|
||||
receivedBlockOrTimedOut := (pool.height > 0 || time.Since(pool.startTime) > 5*time.Second)
|
||||
ourChainIsLongestAmongPeers := pool.maxPeerHeight == 0 || pool.height >= pool.maxPeerHeight
|
||||
// Some conditions to determine if we're caught up.
|
||||
// Ensures we've either received a block or waited some amount of time,
|
||||
// and that we're synced to the highest known height. Note we use maxPeerHeight - 1
|
||||
// because to sync block H requires block H+1 to verify the LastCommit.
|
||||
receivedBlockOrTimedOut := pool.height > 0 || time.Since(pool.startTime) > 5*time.Second
|
||||
ourChainIsLongestAmongPeers := pool.maxPeerHeight == 0 || pool.height >= (pool.maxPeerHeight-1)
|
||||
isCaughtUp := receivedBlockOrTimedOut && ourChainIsLongestAmongPeers
|
||||
return isCaughtUp
|
||||
}
|
||||
@@ -219,14 +222,12 @@ func (pool *BlockPool) RedoRequest(height int64) p2p.ID {
|
||||
defer pool.mtx.Unlock()
|
||||
|
||||
request := pool.requesters[height]
|
||||
|
||||
if request.block == nil {
|
||||
panic("Expected block to be non-nil")
|
||||
peerID := request.getPeerID()
|
||||
if peerID != p2p.ID("") {
|
||||
// RemovePeer will redo all requesters associated with this peer.
|
||||
pool.removePeer(peerID)
|
||||
}
|
||||
|
||||
// RemovePeer will redo all requesters associated with this peer.
|
||||
pool.removePeer(request.peerID)
|
||||
return request.peerID
|
||||
return peerID
|
||||
}
|
||||
|
||||
// TODO: ensure that blocks come in order for each peer.
|
||||
@@ -254,7 +255,8 @@ func (pool *BlockPool) AddBlock(peerID p2p.ID, block *types.Block, blockSize int
|
||||
peer.decrPending(blockSize)
|
||||
}
|
||||
} else {
|
||||
// Bad peer?
|
||||
pool.Logger.Info("invalid peer", "peer", peerID, "blockHeight", block.Height)
|
||||
pool.sendError(errors.New("invalid peer"), peerID)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +296,7 @@ func (pool *BlockPool) RemovePeer(peerID p2p.ID) {
|
||||
func (pool *BlockPool) removePeer(peerID p2p.ID) {
|
||||
for _, requester := range pool.requesters {
|
||||
if requester.getPeerID() == peerID {
|
||||
requester.redo()
|
||||
requester.redo(peerID)
|
||||
}
|
||||
}
|
||||
delete(pool.peers, peerID)
|
||||
@@ -328,8 +330,11 @@ func (pool *BlockPool) makeNextRequester() {
|
||||
defer pool.mtx.Unlock()
|
||||
|
||||
nextHeight := pool.height + pool.requestersLen()
|
||||
if nextHeight > pool.maxPeerHeight {
|
||||
return
|
||||
}
|
||||
|
||||
request := newBPRequester(pool, nextHeight)
|
||||
// request.SetLogger(pool.Logger.With("height", nextHeight))
|
||||
|
||||
pool.requesters[nextHeight] = request
|
||||
atomic.AddInt32(&pool.numPending, 1)
|
||||
@@ -367,10 +372,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
|
||||
@@ -455,7 +460,7 @@ type bpRequester struct {
|
||||
pool *BlockPool
|
||||
height int64
|
||||
gotBlockCh chan struct{}
|
||||
redoCh chan struct{}
|
||||
redoCh chan p2p.ID //redo may send multitime, add peerId to identify repeat
|
||||
|
||||
mtx sync.Mutex
|
||||
peerID p2p.ID
|
||||
@@ -467,7 +472,7 @@ func newBPRequester(pool *BlockPool, height int64) *bpRequester {
|
||||
pool: pool,
|
||||
height: height,
|
||||
gotBlockCh: make(chan struct{}, 1),
|
||||
redoCh: make(chan struct{}, 1),
|
||||
redoCh: make(chan p2p.ID, 1),
|
||||
|
||||
peerID: "",
|
||||
block: nil,
|
||||
@@ -526,9 +531,9 @@ func (bpr *bpRequester) reset() {
|
||||
// Tells bpRequester to pick another peer and try again.
|
||||
// NOTE: Nonblocking, and does nothing if another redo
|
||||
// was already requested.
|
||||
func (bpr *bpRequester) redo() {
|
||||
func (bpr *bpRequester) redo(peerId p2p.ID) {
|
||||
select {
|
||||
case bpr.redoCh <- struct{}{}:
|
||||
case bpr.redoCh <- peerId:
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -567,9 +572,13 @@ OUTER_LOOP:
|
||||
return
|
||||
case <-bpr.Quit():
|
||||
return
|
||||
case <-bpr.redoCh:
|
||||
bpr.reset()
|
||||
continue OUTER_LOOP
|
||||
case peerID := <-bpr.redoCh:
|
||||
if peerID == bpr.peerID {
|
||||
bpr.reset()
|
||||
continue OUTER_LOOP
|
||||
} else {
|
||||
continue WAIT_LOOP
|
||||
}
|
||||
case <-bpr.gotBlockCh:
|
||||
// We got a block!
|
||||
// Continue the for-loop and wait til Quit.
|
||||
|
@@ -16,16 +16,52 @@ func init() {
|
||||
}
|
||||
|
||||
type testPeer struct {
|
||||
id p2p.ID
|
||||
height int64
|
||||
id p2p.ID
|
||||
height int64
|
||||
inputChan chan inputData //make sure each peer's data is sequential
|
||||
}
|
||||
|
||||
func makePeers(numPeers int, minHeight, maxHeight int64) map[p2p.ID]testPeer {
|
||||
peers := make(map[p2p.ID]testPeer, numPeers)
|
||||
type inputData struct {
|
||||
t *testing.T
|
||||
pool *BlockPool
|
||||
request BlockRequest
|
||||
}
|
||||
|
||||
func (p testPeer) runInputRoutine() {
|
||||
go func() {
|
||||
for input := range p.inputChan {
|
||||
p.simulateInput(input)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Request desired, pretend like we got the block immediately.
|
||||
func (p testPeer) simulateInput(input inputData) {
|
||||
block := &types.Block{Header: types.Header{Height: input.request.Height}}
|
||||
input.pool.AddBlock(input.request.PeerID, block, 123)
|
||||
input.t.Logf("Added block from peer %v (height: %v)", input.request.PeerID, input.request.Height)
|
||||
}
|
||||
|
||||
type testPeers map[p2p.ID]testPeer
|
||||
|
||||
func (ps testPeers) start() {
|
||||
for _, v := range ps {
|
||||
v.runInputRoutine()
|
||||
}
|
||||
}
|
||||
|
||||
func (ps testPeers) stop() {
|
||||
for _, v := range ps {
|
||||
close(v.inputChan)
|
||||
}
|
||||
}
|
||||
|
||||
func makePeers(numPeers int, minHeight, maxHeight int64) testPeers {
|
||||
peers := make(testPeers, numPeers)
|
||||
for i := 0; i < numPeers; i++ {
|
||||
peerID := p2p.ID(cmn.RandStr(12))
|
||||
height := minHeight + cmn.RandInt63n(maxHeight-minHeight)
|
||||
peers[peerID] = testPeer{peerID, height}
|
||||
peers[peerID] = testPeer{peerID, height, make(chan inputData, 10)}
|
||||
}
|
||||
return peers
|
||||
}
|
||||
@@ -45,6 +81,9 @@ func TestBasic(t *testing.T) {
|
||||
|
||||
defer pool.Stop()
|
||||
|
||||
peers.start()
|
||||
defer peers.stop()
|
||||
|
||||
// Introduce each peer.
|
||||
go func() {
|
||||
for _, peer := range peers {
|
||||
@@ -77,12 +116,8 @@ func TestBasic(t *testing.T) {
|
||||
if request.Height == 300 {
|
||||
return // Done!
|
||||
}
|
||||
// Request desired, pretend like we got the block immediately.
|
||||
go func() {
|
||||
block := &types.Block{Header: types.Header{Height: request.Height}}
|
||||
pool.AddBlock(request.PeerID, block, 123)
|
||||
t.Logf("Added block from peer %v (height: %v)", request.PeerID, request.Height)
|
||||
}()
|
||||
|
||||
peers[request.PeerID].inputChan <- inputData{t, pool, request}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
@@ -18,7 +19,8 @@ const (
|
||||
// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
|
||||
BlockchainChannel = byte(0x40)
|
||||
|
||||
trySyncIntervalMS = 50
|
||||
trySyncIntervalMS = 10
|
||||
|
||||
// stop syncing when last block's time is
|
||||
// within this much of the system time.
|
||||
// stopSyncingDurationMinutes = 10
|
||||
@@ -76,8 +78,9 @@ func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *Bl
|
||||
store.Height()))
|
||||
}
|
||||
|
||||
const capacity = 1000 // must be bigger than peers count
|
||||
requestsCh := make(chan BlockRequest, capacity)
|
||||
requestsCh := make(chan BlockRequest, maxTotalRequesters)
|
||||
|
||||
const capacity = 1000 // must be bigger than peers count
|
||||
errorsCh := make(chan peerError, capacity) // so we don't block in #Receive#pool.AddBlock
|
||||
|
||||
pool := NewBlockPool(
|
||||
@@ -107,9 +110,6 @@ func (bcR *BlockchainReactor) SetLogger(l log.Logger) {
|
||||
|
||||
// OnStart implements cmn.Service.
|
||||
func (bcR *BlockchainReactor) OnStart() error {
|
||||
if err := bcR.BaseReactor.OnStart(); err != nil {
|
||||
return err
|
||||
}
|
||||
if bcR.fastSync {
|
||||
err := bcR.pool.Start()
|
||||
if err != nil {
|
||||
@@ -122,7 +122,6 @@ func (bcR *BlockchainReactor) OnStart() error {
|
||||
|
||||
// OnStop implements cmn.Service.
|
||||
func (bcR *BlockchainReactor) OnStop() {
|
||||
bcR.BaseReactor.OnStop()
|
||||
bcR.pool.Stop()
|
||||
}
|
||||
|
||||
@@ -182,6 +181,12 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
return
|
||||
}
|
||||
|
||||
if err = msg.ValidateBasic(); err != nil {
|
||||
bcR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
|
||||
bcR.Switch.StopPeerForError(src, err)
|
||||
return
|
||||
}
|
||||
|
||||
bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)
|
||||
|
||||
switch msg := msg.(type) {
|
||||
@@ -190,7 +195,6 @@ func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte)
|
||||
// Unfortunately not queued since the queue is full.
|
||||
}
|
||||
case *bcBlockResponseMessage:
|
||||
// Got a block.
|
||||
bcR.pool.AddBlock(src.ID(), msg.Block, len(msgBytes))
|
||||
case *bcStatusRequestMessage:
|
||||
// Send peer our state.
|
||||
@@ -203,13 +207,12 @@ 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)))
|
||||
}
|
||||
}
|
||||
|
||||
// Handle messages from the poolReactor telling the reactor what to do.
|
||||
// NOTE: Don't sleep in the FOR_LOOP or otherwise slow it down!
|
||||
// (Except for the SYNC_LOOP, which is the primary purpose and must be synchronous.)
|
||||
func (bcR *BlockchainReactor) poolRoutine() {
|
||||
|
||||
trySyncTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
|
||||
@@ -224,6 +227,8 @@ func (bcR *BlockchainReactor) poolRoutine() {
|
||||
lastHundred := time.Now()
|
||||
lastRate := 0.0
|
||||
|
||||
didProcessCh := make(chan struct{}, 1)
|
||||
|
||||
FOR_LOOP:
|
||||
for {
|
||||
select {
|
||||
@@ -239,14 +244,17 @@ FOR_LOOP:
|
||||
// The pool handles timeouts, just let it go.
|
||||
continue FOR_LOOP
|
||||
}
|
||||
|
||||
case err := <-bcR.errorsCh:
|
||||
peer := bcR.Switch.Peers().Get(err.peerID)
|
||||
if peer != nil {
|
||||
bcR.Switch.StopPeerForError(peer, err)
|
||||
}
|
||||
|
||||
case <-statusUpdateTicker.C:
|
||||
// ask for status updates
|
||||
go bcR.BroadcastStatusRequest() // nolint: errcheck
|
||||
|
||||
case <-switchToConsensusTicker.C:
|
||||
height, numPending, lenRequesters := bcR.pool.GetStatus()
|
||||
outbound, inbound, _ := bcR.Switch.NumPeers()
|
||||
@@ -256,65 +264,94 @@ FOR_LOOP:
|
||||
bcR.Logger.Info("Time to switch to consensus reactor!", "height", height)
|
||||
bcR.pool.Stop()
|
||||
|
||||
conR := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
|
||||
conR.SwitchToConsensus(state, blocksSynced)
|
||||
conR, ok := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
|
||||
if ok {
|
||||
conR.SwitchToConsensus(state, blocksSynced)
|
||||
} else {
|
||||
// should only happen during testing
|
||||
}
|
||||
|
||||
break FOR_LOOP
|
||||
}
|
||||
|
||||
case <-trySyncTicker.C: // chan time
|
||||
// This loop can be slow as long as it's doing syncing work.
|
||||
SYNC_LOOP:
|
||||
for i := 0; i < 10; i++ {
|
||||
// See if there are any blocks to sync.
|
||||
first, second := bcR.pool.PeekTwoBlocks()
|
||||
//bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
|
||||
if first == nil || second == nil {
|
||||
// We need both to sync the first block.
|
||||
break SYNC_LOOP
|
||||
select {
|
||||
case didProcessCh <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
|
||||
case <-didProcessCh:
|
||||
// NOTE: It is a subtle mistake to process more than a single block
|
||||
// at a time (e.g. 10) here, because we only TrySend 1 request per
|
||||
// loop. The ratio mismatch can result in starving of blocks, a
|
||||
// sudden burst of requests and responses, and repeat.
|
||||
// Consequently, it is better to split these routines rather than
|
||||
// coupling them as it's written here. TODO uncouple from request
|
||||
// routine.
|
||||
|
||||
// See if there are any blocks to sync.
|
||||
first, second := bcR.pool.PeekTwoBlocks()
|
||||
//bcR.Logger.Info("TrySync peeked", "first", first, "second", second)
|
||||
if first == nil || second == nil {
|
||||
// We need both to sync the first block.
|
||||
continue FOR_LOOP
|
||||
} else {
|
||||
// Try again quickly next loop.
|
||||
didProcessCh <- struct{}{}
|
||||
}
|
||||
|
||||
firstParts := first.MakePartSet(types.BlockPartSizeBytes)
|
||||
firstPartsHeader := firstParts.Header()
|
||||
firstID := types.BlockID{first.Hash(), firstPartsHeader}
|
||||
// Finally, verify the first block using the second's commit
|
||||
// NOTE: we can probably make this more efficient, but note that calling
|
||||
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
|
||||
// currently necessary.
|
||||
err := state.Validators.VerifyCommit(
|
||||
chainID, firstID, first.Height, second.LastCommit)
|
||||
if err != nil {
|
||||
bcR.Logger.Error("Error in validation", "err", err)
|
||||
peerID := bcR.pool.RedoRequest(first.Height)
|
||||
peer := bcR.Switch.Peers().Get(peerID)
|
||||
if peer != nil {
|
||||
// NOTE: we've already removed the peer's request, but we
|
||||
// still need to clean up the rest.
|
||||
bcR.Switch.StopPeerForError(peer, fmt.Errorf("BlockchainReactor validation error: %v", err))
|
||||
}
|
||||
firstParts := first.MakePartSet(state.ConsensusParams.BlockPartSizeBytes)
|
||||
firstPartsHeader := firstParts.Header()
|
||||
firstID := types.BlockID{first.Hash(), firstPartsHeader}
|
||||
// Finally, verify the first block using the second's commit
|
||||
// NOTE: we can probably make this more efficient, but note that calling
|
||||
// first.Hash() doesn't verify the tx contents, so MakePartSet() is
|
||||
// currently necessary.
|
||||
err := state.Validators.VerifyCommit(
|
||||
chainID, firstID, first.Height, second.LastCommit)
|
||||
peerID2 := bcR.pool.RedoRequest(second.Height)
|
||||
peer2 := bcR.Switch.Peers().Get(peerID2)
|
||||
if peer2 != nil && peer2 != peer {
|
||||
// NOTE: we've already removed the peer's request, but we
|
||||
// still need to clean up the rest.
|
||||
bcR.Switch.StopPeerForError(peer2, fmt.Errorf("BlockchainReactor validation error: %v", err))
|
||||
}
|
||||
continue FOR_LOOP
|
||||
} else {
|
||||
bcR.pool.PopRequest()
|
||||
|
||||
// TODO: batch saves so we dont persist to disk every block
|
||||
bcR.store.SaveBlock(first, firstParts, second.LastCommit)
|
||||
|
||||
// TODO: same thing for app - but we would need a way to
|
||||
// get the hash without persisting the state
|
||||
var err error
|
||||
state, err = bcR.blockExec.ApplyBlock(state, firstID, first)
|
||||
if err != nil {
|
||||
bcR.Logger.Error("Error in validation", "err", err)
|
||||
peerID := bcR.pool.RedoRequest(first.Height)
|
||||
peer := bcR.Switch.Peers().Get(peerID)
|
||||
if peer != nil {
|
||||
bcR.Switch.StopPeerForError(peer, fmt.Errorf("BlockchainReactor validation error: %v", err))
|
||||
}
|
||||
break SYNC_LOOP
|
||||
} else {
|
||||
bcR.pool.PopRequest()
|
||||
// TODO This is bad, are we zombie?
|
||||
cmn.PanicQ(fmt.Sprintf("Failed to process committed block (%d:%X): %v",
|
||||
first.Height, first.Hash(), err))
|
||||
}
|
||||
blocksSynced++
|
||||
|
||||
// TODO: batch saves so we dont persist to disk every block
|
||||
bcR.store.SaveBlock(first, firstParts, second.LastCommit)
|
||||
|
||||
// TODO: same thing for app - but we would need a way to
|
||||
// get the hash without persisting the state
|
||||
var err error
|
||||
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",
|
||||
first.Height, first.Hash(), err))
|
||||
}
|
||||
blocksSynced++
|
||||
|
||||
if blocksSynced%100 == 0 {
|
||||
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
|
||||
bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height,
|
||||
"max_peer_height", bcR.pool.MaxPeerHeight(), "blocks/s", lastRate)
|
||||
lastHundred = time.Now()
|
||||
}
|
||||
if blocksSynced%100 == 0 {
|
||||
lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
|
||||
bcR.Logger.Info("Fast Sync Rate", "height", bcR.pool.height,
|
||||
"max_peer_height", bcR.pool.MaxPeerHeight(), "blocks/s", lastRate)
|
||||
lastHundred = time.Now()
|
||||
}
|
||||
}
|
||||
continue FOR_LOOP
|
||||
|
||||
case <-bcR.Quit():
|
||||
break FOR_LOOP
|
||||
}
|
||||
@@ -332,15 +369,17 @@ func (bcR *BlockchainReactor) BroadcastStatusRequest() error {
|
||||
// Messages
|
||||
|
||||
// BlockchainMessage is a generic message for this reactor.
|
||||
type BlockchainMessage interface{}
|
||||
type BlockchainMessage interface {
|
||||
ValidateBasic() error
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -357,16 +396,32 @@ type bcBlockRequestMessage struct {
|
||||
Height int64
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcBlockRequestMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("Negative Height")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *bcBlockRequestMessage) String() string {
|
||||
return cmn.Fmt("[bcBlockRequestMessage %v]", m.Height)
|
||||
return fmt.Sprintf("[bcBlockRequestMessage %v]", m.Height)
|
||||
}
|
||||
|
||||
type bcNoBlockResponseMessage struct {
|
||||
Height int64
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcNoBlockResponseMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("Negative Height")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (brm *bcNoBlockResponseMessage) String() string {
|
||||
return cmn.Fmt("[bcNoBlockResponseMessage %d]", brm.Height)
|
||||
return fmt.Sprintf("[bcNoBlockResponseMessage %d]", brm.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -375,8 +430,13 @@ type bcBlockResponseMessage struct {
|
||||
Block *types.Block
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcBlockResponseMessage) ValidateBasic() error {
|
||||
return m.Block.ValidateBasic()
|
||||
}
|
||||
|
||||
func (m *bcBlockResponseMessage) String() string {
|
||||
return cmn.Fmt("[bcBlockResponseMessage %v]", m.Block.Height)
|
||||
return fmt.Sprintf("[bcBlockResponseMessage %v]", m.Block.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -385,8 +445,16 @@ type bcStatusRequestMessage struct {
|
||||
Height int64
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcStatusRequestMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("Negative Height")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *bcStatusRequestMessage) String() string {
|
||||
return cmn.Fmt("[bcStatusRequestMessage %v]", m.Height)
|
||||
return fmt.Sprintf("[bcStatusRequestMessage %v]", m.Height)
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -395,6 +463,14 @@ type bcStatusResponseMessage struct {
|
||||
Height int64
|
||||
}
|
||||
|
||||
func (m *bcStatusResponseMessage) String() string {
|
||||
return cmn.Fmt("[bcStatusResponseMessage %v]", m.Height)
|
||||
// ValidateBasic performs basic validation.
|
||||
func (m *bcStatusResponseMessage) ValidateBasic() error {
|
||||
if m.Height < 0 {
|
||||
return errors.New("Negative Height")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *bcStatusResponseMessage) String() string {
|
||||
return fmt.Sprintf("[bcStatusResponseMessage %v]", m.Height)
|
||||
}
|
||||
|
@@ -1,72 +1,151 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sort"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) {
|
||||
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
||||
// blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB())
|
||||
// stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB())
|
||||
var config *cfg.Config
|
||||
|
||||
func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.GenesisDoc, []types.PrivValidator) {
|
||||
validators := make([]types.GenesisValidator, numValidators)
|
||||
privValidators := make([]types.PrivValidator, numValidators)
|
||||
for i := 0; i < numValidators; i++ {
|
||||
val, privVal := types.RandValidator(randPower, minPower)
|
||||
validators[i] = types.GenesisValidator{
|
||||
PubKey: val.PubKey,
|
||||
Power: val.VotingPower,
|
||||
}
|
||||
privValidators[i] = privVal
|
||||
}
|
||||
sort.Sort(types.PrivValidatorsByAddress(privValidators))
|
||||
|
||||
return &types.GenesisDoc{
|
||||
GenesisTime: tmtime.Now(),
|
||||
ChainID: config.ChainID(),
|
||||
Validators: validators,
|
||||
}, privValidators
|
||||
}
|
||||
|
||||
func makeVote(header *types.Header, blockID types.BlockID, valset *types.ValidatorSet, privVal types.PrivValidator) *types.Vote {
|
||||
addr := privVal.GetPubKey().Address()
|
||||
idx, _ := valset.GetByAddress(addr)
|
||||
vote := &types.Vote{
|
||||
ValidatorAddress: addr,
|
||||
ValidatorIndex: idx,
|
||||
Height: header.Height,
|
||||
Round: 1,
|
||||
Timestamp: tmtime.Now(),
|
||||
Type: types.PrecommitType,
|
||||
BlockID: blockID,
|
||||
}
|
||||
|
||||
privVal.SignVote(header.ChainID, vote)
|
||||
|
||||
return vote
|
||||
}
|
||||
|
||||
type BlockchainReactorPair struct {
|
||||
reactor *BlockchainReactor
|
||||
app proxy.AppConns
|
||||
}
|
||||
|
||||
func newBlockchainReactor(logger log.Logger, genDoc *types.GenesisDoc, privVals []types.PrivValidator, maxBlockHeight int64) BlockchainReactorPair {
|
||||
if len(privVals) != 1 {
|
||||
panic("only support one validator")
|
||||
}
|
||||
|
||||
app := &testApp{}
|
||||
cc := proxy.NewLocalClientCreator(app)
|
||||
proxyApp := proxy.NewAppConns(cc)
|
||||
err := proxyApp.Start()
|
||||
if err != nil {
|
||||
panic(cmn.ErrorWrap(err, "error start app"))
|
||||
}
|
||||
|
||||
blockDB := dbm.NewMemDB()
|
||||
stateDB := dbm.NewMemDB()
|
||||
blockStore := NewBlockStore(blockDB)
|
||||
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
|
||||
|
||||
state, err := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
if err != nil {
|
||||
panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
|
||||
}
|
||||
return state, blockStore
|
||||
}
|
||||
|
||||
func newBlockchainReactor(logger log.Logger, maxBlockHeight int64) *BlockchainReactor {
|
||||
state, blockStore := makeStateAndBlockStore(logger)
|
||||
|
||||
// Make the blockchainReactor itself
|
||||
// Make the BlockchainReactor itself.
|
||||
// NOTE we have to create and commit the blocks first because
|
||||
// pool.height is determined from the store.
|
||||
fastSync := true
|
||||
var nilApp proxy.AppConnConsensus
|
||||
blockExec := sm.NewBlockExecutor(dbm.NewMemDB(), log.TestingLogger(), nilApp,
|
||||
blockExec := sm.NewBlockExecutor(dbm.NewMemDB(), log.TestingLogger(), proxyApp.Consensus(),
|
||||
sm.MockMempool{}, sm.MockEvidencePool{})
|
||||
|
||||
// let's add some blocks in
|
||||
for blockHeight := int64(1); blockHeight <= maxBlockHeight; blockHeight++ {
|
||||
lastCommit := &types.Commit{}
|
||||
if blockHeight > 1 {
|
||||
lastBlockMeta := blockStore.LoadBlockMeta(blockHeight - 1)
|
||||
lastBlock := blockStore.LoadBlock(blockHeight - 1)
|
||||
|
||||
vote := makeVote(&lastBlock.Header, lastBlockMeta.BlockID, state.Validators, privVals[0])
|
||||
lastCommit = &types.Commit{Precommits: []*types.Vote{vote}, BlockID: lastBlockMeta.BlockID}
|
||||
}
|
||||
|
||||
thisBlock := makeBlock(blockHeight, state, lastCommit)
|
||||
|
||||
thisParts := thisBlock.MakePartSet(types.BlockPartSizeBytes)
|
||||
blockID := types.BlockID{thisBlock.Hash(), thisParts.Header()}
|
||||
|
||||
state, err = blockExec.ApplyBlock(state, blockID, thisBlock)
|
||||
if err != nil {
|
||||
panic(cmn.ErrorWrap(err, "error apply block"))
|
||||
}
|
||||
|
||||
blockStore.SaveBlock(thisBlock, thisParts, lastCommit)
|
||||
}
|
||||
|
||||
bcReactor := NewBlockchainReactor(state.Copy(), blockExec, blockStore, fastSync)
|
||||
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())
|
||||
|
||||
// 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)
|
||||
blockStore.SaveBlock(firstBlock, firstParts, secondBlock.LastCommit)
|
||||
}
|
||||
|
||||
return bcReactor
|
||||
return BlockchainReactorPair{bcReactor, proxyApp}
|
||||
}
|
||||
|
||||
func TestNoBlockResponse(t *testing.T) {
|
||||
maxBlockHeight := int64(20)
|
||||
config = cfg.ResetTestRoot("blockchain_reactor_test")
|
||||
genDoc, privVals := randGenesisDoc(1, false, 30)
|
||||
|
||||
bcr := newBlockchainReactor(log.TestingLogger(), maxBlockHeight)
|
||||
bcr.Start()
|
||||
defer bcr.Stop()
|
||||
maxBlockHeight := int64(65)
|
||||
|
||||
// Add some peers in
|
||||
peer := newbcrTestPeer(p2p.ID(cmn.RandStr(12)))
|
||||
bcr.AddPeer(peer)
|
||||
reactorPairs := make([]BlockchainReactorPair, 2)
|
||||
|
||||
chID := byte(0x01)
|
||||
reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
|
||||
p2p.MakeConnectedSwitches(config.P2P, 2, func(i int, s *p2p.Switch) *p2p.Switch {
|
||||
s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
|
||||
return s
|
||||
|
||||
}, p2p.Connect2Switches)
|
||||
|
||||
defer func() {
|
||||
for _, r := range reactorPairs {
|
||||
r.reactor.Stop()
|
||||
r.app.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
tests := []struct {
|
||||
height int64
|
||||
@@ -78,72 +157,100 @@ func TestNoBlockResponse(t *testing.T) {
|
||||
{100, false},
|
||||
}
|
||||
|
||||
// receive a request message from peer,
|
||||
// wait for our response to be received on the peer
|
||||
for _, tt := range tests {
|
||||
reqBlockMsg := &bcBlockRequestMessage{tt.height}
|
||||
reqBlockBytes := cdc.MustMarshalBinaryBare(reqBlockMsg)
|
||||
bcr.Receive(chID, peer, reqBlockBytes)
|
||||
msg := peer.lastBlockchainMessage()
|
||||
for {
|
||||
if reactorPairs[1].reactor.pool.IsCaughtUp() {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
assert.Equal(t, maxBlockHeight, reactorPairs[0].reactor.store.Height())
|
||||
|
||||
for _, tt := range tests {
|
||||
block := reactorPairs[1].reactor.store.LoadBlock(tt.height)
|
||||
if tt.existent {
|
||||
if blockMsg, ok := msg.(*bcBlockResponseMessage); !ok {
|
||||
t.Fatalf("Expected to receive a block response for height %d", tt.height)
|
||||
} else if blockMsg.Block.Height != tt.height {
|
||||
t.Fatalf("Expected response to be for height %d, got %d", tt.height, blockMsg.Block.Height)
|
||||
}
|
||||
assert.True(t, block != nil)
|
||||
} else {
|
||||
if noBlockMsg, ok := msg.(*bcNoBlockResponseMessage); !ok {
|
||||
t.Fatalf("Expected to receive a no block response for height %d", tt.height)
|
||||
} else if noBlockMsg.Height != tt.height {
|
||||
t.Fatalf("Expected response to be for height %d, got %d", tt.height, noBlockMsg.Height)
|
||||
}
|
||||
assert.True(t, block == nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// NOTE: This is too hard to test without
|
||||
// an easy way to add test peer to switch
|
||||
// or without significant refactoring of the module.
|
||||
// Alternatively we could actually dial a TCP conn but
|
||||
// that seems extreme.
|
||||
func TestBadBlockStopsPeer(t *testing.T) {
|
||||
maxBlockHeight := int64(20)
|
||||
config = cfg.ResetTestRoot("blockchain_reactor_test")
|
||||
genDoc, privVals := randGenesisDoc(1, false, 30)
|
||||
|
||||
bcr := newBlockchainReactor(log.TestingLogger(), maxBlockHeight)
|
||||
bcr.Start()
|
||||
defer bcr.Stop()
|
||||
maxBlockHeight := int64(148)
|
||||
|
||||
// Add some peers in
|
||||
peer := newbcrTestPeer(p2p.ID(cmn.RandStr(12)))
|
||||
otherChain := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
defer func() {
|
||||
otherChain.reactor.Stop()
|
||||
otherChain.app.Stop()
|
||||
}()
|
||||
|
||||
// XXX: This doesn't add the peer to anything,
|
||||
// so it's hard to check that it's later removed
|
||||
bcr.AddPeer(peer)
|
||||
assert.True(t, bcr.Switch.Peers().Size() > 0)
|
||||
reactorPairs := make([]BlockchainReactorPair, 4)
|
||||
|
||||
// send a bad block from the peer
|
||||
// default blocks already dont have commits, so should fail
|
||||
block := bcr.store.LoadBlock(3)
|
||||
msg := &bcBlockResponseMessage{Block: block}
|
||||
peer.Send(BlockchainChannel, struct{ BlockchainMessage }{msg})
|
||||
reactorPairs[0] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, maxBlockHeight)
|
||||
reactorPairs[1] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[2] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs[3] = newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
|
||||
ticker := time.NewTicker(time.Millisecond * 10)
|
||||
timer := time.NewTimer(time.Second * 2)
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if bcr.Switch.Peers().Size() == 0 {
|
||||
break LOOP
|
||||
}
|
||||
case <-timer.C:
|
||||
t.Fatal("Timed out waiting to disconnect peer")
|
||||
switches := p2p.MakeConnectedSwitches(config.P2P, 4, func(i int, s *p2p.Switch) *p2p.Switch {
|
||||
s.AddReactor("BLOCKCHAIN", reactorPairs[i].reactor)
|
||||
return s
|
||||
|
||||
}, p2p.Connect2Switches)
|
||||
|
||||
defer func() {
|
||||
for _, r := range reactorPairs {
|
||||
r.reactor.Stop()
|
||||
r.app.Stop()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
if reactorPairs[3].reactor.pool.IsCaughtUp() {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
//at this time, reactors[0-3] is the newest
|
||||
assert.Equal(t, 3, reactorPairs[1].reactor.Switch.Peers().Size())
|
||||
|
||||
//mark reactorPairs[3] is an invalid peer
|
||||
reactorPairs[3].reactor.store = otherChain.reactor.store
|
||||
|
||||
lastReactorPair := newBlockchainReactor(log.TestingLogger(), genDoc, privVals, 0)
|
||||
reactorPairs = append(reactorPairs, lastReactorPair)
|
||||
|
||||
switches = append(switches, p2p.MakeConnectedSwitches(config.P2P, 1, func(i int, s *p2p.Switch) *p2p.Switch {
|
||||
s.AddReactor("BLOCKCHAIN", reactorPairs[len(reactorPairs)-1].reactor)
|
||||
return s
|
||||
|
||||
}, p2p.Connect2Switches)...)
|
||||
|
||||
for i := 0; i < len(reactorPairs)-1; i++ {
|
||||
p2p.Connect2Switches(switches, i, len(reactorPairs)-1)
|
||||
}
|
||||
|
||||
for {
|
||||
if lastReactorPair.reactor.pool.IsCaughtUp() || lastReactorPair.reactor.Switch.Peers().Size() == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
assert.True(t, lastReactorPair.reactor.Switch.Peers().Size() < len(reactorPairs)-1)
|
||||
}
|
||||
*/
|
||||
|
||||
//----------------------------------------------
|
||||
// utility funcs
|
||||
@@ -155,55 +262,41 @@ func makeTxs(height int64) (txs []types.Tx) {
|
||||
return txs
|
||||
}
|
||||
|
||||
func makeBlock(height int64, state sm.State) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), new(types.Commit), nil)
|
||||
func makeBlock(height int64, state sm.State, lastCommit *types.Commit) *types.Block {
|
||||
block, _ := state.MakeBlock(height, makeTxs(height), lastCommit, nil, state.Validators.GetProposer().Address)
|
||||
return block
|
||||
}
|
||||
|
||||
// The Test peer
|
||||
type bcrTestPeer struct {
|
||||
cmn.BaseService
|
||||
id p2p.ID
|
||||
ch chan interface{}
|
||||
type testApp struct {
|
||||
abci.BaseApplication
|
||||
}
|
||||
|
||||
var _ p2p.Peer = (*bcrTestPeer)(nil)
|
||||
var _ abci.Application = (*testApp)(nil)
|
||||
|
||||
func newbcrTestPeer(id p2p.ID) *bcrTestPeer {
|
||||
bcr := &bcrTestPeer{
|
||||
id: id,
|
||||
ch: make(chan interface{}, 2),
|
||||
}
|
||||
bcr.BaseService = *cmn.NewBaseService(nil, "bcrTestPeer", bcr)
|
||||
return bcr
|
||||
func (app *testApp) Info(req abci.RequestInfo) (resInfo abci.ResponseInfo) {
|
||||
return abci.ResponseInfo{}
|
||||
}
|
||||
|
||||
func (tp *bcrTestPeer) lastBlockchainMessage() interface{} { return <-tp.ch }
|
||||
|
||||
func (tp *bcrTestPeer) TrySend(chID byte, msgBytes []byte) bool {
|
||||
var msg BlockchainMessage
|
||||
err := cdc.UnmarshalBinaryBare(msgBytes, &msg)
|
||||
if err != nil {
|
||||
panic(cmn.ErrorWrap(err, "Error while trying to parse a BlockchainMessage"))
|
||||
}
|
||||
if _, ok := msg.(*bcStatusResponseMessage); ok {
|
||||
// Discard status response messages since they skew our results
|
||||
// We only want to deal with:
|
||||
// + bcBlockResponseMessage
|
||||
// + bcNoBlockResponseMessage
|
||||
} else {
|
||||
tp.ch <- msg
|
||||
}
|
||||
return true
|
||||
func (app *testApp) BeginBlock(req abci.RequestBeginBlock) abci.ResponseBeginBlock {
|
||||
return abci.ResponseBeginBlock{}
|
||||
}
|
||||
|
||||
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) Status() p2p.ConnectionStatus { return p2p.ConnectionStatus{} }
|
||||
func (tp *bcrTestPeer) ID() p2p.ID { return tp.id }
|
||||
func (tp *bcrTestPeer) IsOutbound() bool { return false }
|
||||
func (tp *bcrTestPeer) IsPersistent() bool { return true }
|
||||
func (tp *bcrTestPeer) Get(s string) interface{} { return s }
|
||||
func (tp *bcrTestPeer) Set(string, interface{}) {}
|
||||
func (tp *bcrTestPeer) RemoteIP() net.IP { return []byte{127, 0, 0, 1} }
|
||||
func (tp *bcrTestPeer) OriginalAddr() *p2p.NetAddress { return nil }
|
||||
func (app *testApp) EndBlock(req abci.RequestEndBlock) abci.ResponseEndBlock {
|
||||
return abci.ResponseEndBlock{}
|
||||
}
|
||||
|
||||
func (app *testApp) DeliverTx(tx []byte) abci.ResponseDeliverTx {
|
||||
return abci.ResponseDeliverTx{Tags: []cmn.KVPair{}}
|
||||
}
|
||||
|
||||
func (app *testApp) CheckTx(tx []byte) abci.ResponseCheckTx {
|
||||
return abci.ResponseCheckTx{}
|
||||
}
|
||||
|
||||
func (app *testApp) Commit() abci.ResponseCommit {
|
||||
return abci.ResponseCommit{}
|
||||
}
|
||||
|
||||
func (app *testApp) Query(reqQuery abci.RequestQuery) (resQuery abci.ResponseQuery) {
|
||||
return
|
||||
}
|
||||
|
@@ -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,16 +6,33 @@ import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/db"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
func makeStateAndBlockStore(logger log.Logger) (sm.State, *BlockStore) {
|
||||
config := cfg.ResetTestRoot("blockchain_reactor_test")
|
||||
// blockDB := dbm.NewDebugDB("blockDB", dbm.NewMemDB())
|
||||
// stateDB := dbm.NewDebugDB("stateDB", dbm.NewMemDB())
|
||||
blockDB := dbm.NewMemDB()
|
||||
stateDB := dbm.NewMemDB()
|
||||
state, err := sm.LoadStateFromDBOrGenesisFile(stateDB, config.GenesisFile())
|
||||
if err != nil {
|
||||
panic(cmn.ErrorWrap(err, "error constructing state from genesis file"))
|
||||
}
|
||||
return state, NewBlockStore(blockDB)
|
||||
}
|
||||
|
||||
func TestLoadBlockStoreStateJSON(t *testing.T) {
|
||||
db := db.NewMemDB()
|
||||
|
||||
@@ -49,7 +66,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)
|
||||
@@ -65,12 +82,12 @@ func freshBlockStore() (*BlockStore, db.DB) {
|
||||
var (
|
||||
state, _ = makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
||||
|
||||
block = makeBlock(1, state)
|
||||
block = makeBlock(1, state, new(types.Commit))
|
||||
partSet = block.MakePartSet(2)
|
||||
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 ...
|
||||
@@ -88,10 +105,10 @@ func TestBlockStoreSaveLoadBlock(t *testing.T) {
|
||||
}
|
||||
|
||||
// save a block
|
||||
block := makeBlock(bs.Height()+1, state)
|
||||
block := makeBlock(bs.Height()+1, state, new(types.Commit))
|
||||
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 +120,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 +128,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 +255,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
|
||||
@@ -331,11 +348,11 @@ func TestLoadBlockMeta(t *testing.T) {
|
||||
func TestBlockFetchAtHeight(t *testing.T) {
|
||||
state, bs := makeStateAndBlockStore(log.NewTMLogger(new(bytes.Buffer)))
|
||||
require.Equal(t, bs.Height(), int64(0), "initially the height should be zero")
|
||||
block := makeBlock(bs.Height()+1, state)
|
||||
block := makeBlock(bs.Height()+1, state, new(types.Commit))
|
||||
|
||||
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")
|
||||
|
@@ -2,7 +2,6 @@ package blockchain
|
||||
|
||||
import (
|
||||
"github.com/tendermint/go-amino"
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
@@ -10,6 +9,5 @@ var cdc = amino.NewCodec()
|
||||
|
||||
func init() {
|
||||
RegisterBlockchainMessages(cdc)
|
||||
cryptoAmino.RegisterAmino(cdc)
|
||||
types.RegisterEvidences(cdc)
|
||||
types.RegisterBlockAmino(cdc)
|
||||
}
|
||||
|
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto/ed25519"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
@@ -13,9 +14,10 @@ import (
|
||||
|
||||
func main() {
|
||||
var (
|
||||
addr = flag.String("addr", ":26659", "Address of client to connect to")
|
||||
chainID = flag.String("chain-id", "mychain", "chain id")
|
||||
privValPath = flag.String("priv", "", "priv val file path")
|
||||
addr = flag.String("addr", ":26659", "Address of client to connect to")
|
||||
chainID = flag.String("chain-id", "mychain", "chain id")
|
||||
privValKeyPath = flag.String("priv-key", "", "priv val key file path")
|
||||
privValStatePath = flag.String("priv-state", "", "priv val state file path")
|
||||
|
||||
logger = log.NewTMLogger(
|
||||
log.NewSyncWriter(os.Stdout),
|
||||
@@ -27,18 +29,26 @@ func main() {
|
||||
"Starting private validator",
|
||||
"addr", *addr,
|
||||
"chainID", *chainID,
|
||||
"privPath", *privValPath,
|
||||
"privKeyPath", *privValKeyPath,
|
||||
"privStatePath", *privValStatePath,
|
||||
)
|
||||
|
||||
pv := privval.LoadFilePV(*privValPath)
|
||||
pv := privval.LoadFilePV(*privValKeyPath, *privValStatePath)
|
||||
|
||||
rs := privval.NewRemoteSigner(
|
||||
logger,
|
||||
*chainID,
|
||||
*addr,
|
||||
pv,
|
||||
ed25519.GenPrivKey(),
|
||||
)
|
||||
var dialer privval.Dialer
|
||||
protocol, address := cmn.ProtocolAndAddress(*addr)
|
||||
switch protocol {
|
||||
case "unix":
|
||||
dialer = privval.DialUnixFn(address)
|
||||
case "tcp":
|
||||
connTimeout := 3 * time.Second // TODO
|
||||
dialer = privval.DialTCPFn(address, connTimeout, ed25519.GenPrivKey())
|
||||
default:
|
||||
logger.Error("Unknown protocol", "protocol", protocol)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
rs := privval.NewRemoteSigner(logger, *chainID, pv, dialer)
|
||||
err := rs.Start()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@@ -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
|
||||
|
@@ -17,7 +17,7 @@ var GenValidatorCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func genValidator(cmd *cobra.Command, args []string) {
|
||||
pv := privval.GenFilePV("")
|
||||
pv := privval.GenFilePV("", "")
|
||||
jsbz, err := cdc.MarshalJSON(pv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@@ -1,15 +1,15 @@
|
||||
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.
|
||||
@@ -25,15 +25,18 @@ func initFiles(cmd *cobra.Command, args []string) error {
|
||||
|
||||
func initFilesWithConfig(config *cfg.Config) error {
|
||||
// private validator
|
||||
privValFile := config.PrivValidatorFile()
|
||||
privValKeyFile := config.PrivValidatorKeyFile()
|
||||
privValStateFile := config.PrivValidatorStateFile()
|
||||
var pv *privval.FilePV
|
||||
if cmn.FileExists(privValFile) {
|
||||
pv = privval.LoadFilePV(privValFile)
|
||||
logger.Info("Found private validator", "path", privValFile)
|
||||
if cmn.FileExists(privValKeyFile) {
|
||||
pv = privval.LoadFilePV(privValKeyFile, privValStateFile)
|
||||
logger.Info("Found private validator", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
} else {
|
||||
pv = privval.GenFilePV(privValFile)
|
||||
pv = privval.GenFilePV(privValKeyFile, privValStateFile)
|
||||
pv.Save()
|
||||
logger.Info("Generated private validator", "path", privValFile)
|
||||
logger.Info("Generated private validator", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
}
|
||||
|
||||
nodeKeyFile := config.NodeKeyFile()
|
||||
@@ -52,13 +55,15 @@ 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(),
|
||||
}
|
||||
key := pv.GetPubKey()
|
||||
genDoc.Validators = []types.GenesisValidator{{
|
||||
PubKey: pv.GetPubKey(),
|
||||
Power: 10,
|
||||
Address: key.Address(),
|
||||
PubKey: key,
|
||||
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,9 @@ import (
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
)
|
||||
|
||||
// ResetAllCmd removes the database of this Tendermint core
|
||||
@@ -27,36 +28,41 @@ var ResetPrivValidatorCmd = &cobra.Command{
|
||||
// XXX: this is totally unsafe.
|
||||
// it's only suitable for testnets.
|
||||
func resetAll(cmd *cobra.Command, args []string) {
|
||||
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorFile(), logger)
|
||||
ResetAll(config.DBDir(), config.P2P.AddrBookFile(), config.PrivValidatorKeyFile(),
|
||||
config.PrivValidatorStateFile(), logger)
|
||||
}
|
||||
|
||||
// XXX: this is totally unsafe.
|
||||
// it's only suitable for testnets.
|
||||
func resetPrivValidator(cmd *cobra.Command, args []string) {
|
||||
resetFilePV(config.PrivValidatorFile(), logger)
|
||||
resetFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile(), logger)
|
||||
}
|
||||
|
||||
// ResetAll removes the privValidator and address book files plus all data.
|
||||
// ResetAll removes address book files plus all data, and resets the privValdiator data.
|
||||
// Exported so other CLI tools can use it.
|
||||
func ResetAll(dbDir, addrBookFile, privValFile string, logger log.Logger) {
|
||||
resetFilePV(privValFile, logger)
|
||||
func ResetAll(dbDir, addrBookFile, privValKeyFile, privValStateFile string, logger log.Logger) {
|
||||
removeAddrBook(addrBookFile, logger)
|
||||
if err := os.RemoveAll(dbDir); err == nil {
|
||||
logger.Info("Removed all blockchain history", "dir", dbDir)
|
||||
} else {
|
||||
logger.Error("Error removing all blockchain history", "dir", dbDir, "err", err)
|
||||
}
|
||||
// recreate the dbDir since the privVal state needs to live there
|
||||
cmn.EnsureDir(dbDir, 0700)
|
||||
resetFilePV(privValKeyFile, privValStateFile, logger)
|
||||
}
|
||||
|
||||
func resetFilePV(privValFile string, logger log.Logger) {
|
||||
if _, err := os.Stat(privValFile); err == nil {
|
||||
pv := privval.LoadFilePV(privValFile)
|
||||
func resetFilePV(privValKeyFile, privValStateFile string, logger log.Logger) {
|
||||
if _, err := os.Stat(privValKeyFile); err == nil {
|
||||
pv := privval.LoadFilePVEmptyState(privValKeyFile, privValStateFile)
|
||||
pv.Reset()
|
||||
logger.Info("Reset private validator file to genesis state", "file", privValFile)
|
||||
logger.Info("Reset private validator file to genesis state", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
} else {
|
||||
pv := privval.GenFilePV(privValFile)
|
||||
pv := privval.GenFilePV(privValKeyFile, privValStateFile)
|
||||
pv.Save()
|
||||
logger.Info("Generated private validator file", "file", privValFile)
|
||||
logger.Info("Generated private validator file", "file", "keyFile", privValKeyFile,
|
||||
"stateFile", privValStateFile)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
@@ -50,6 +54,9 @@ var RootCmd = &cobra.Command{
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if config.LogFormat == cfg.LogFormatJSON {
|
||||
logger = log.NewTMJSONLogger(log.NewSyncWriter(os.Stdout))
|
||||
}
|
||||
logger, err = tmflags.ParseLogLevel(config.LogLevel, logger, cfg.DefaultLogLevel())
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -2,6 +2,9 @@ package commands
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
@@ -21,7 +24,7 @@ func AddNodeFlags(cmd *cobra.Command) {
|
||||
cmd.Flags().Bool("fast_sync", config.FastSync, "Fast blockchain syncing")
|
||||
|
||||
// abci flags
|
||||
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or 'nilapp' or 'kvstore' for local testing.")
|
||||
cmd.Flags().String("proxy_app", config.ProxyApp, "Proxy app address, or one of: 'kvstore', 'persistent_kvstore', 'counter', 'counter_serial' or 'noop' for local testing.")
|
||||
cmd.Flags().String("abci", config.ABCI, "Specify abci transport (socket | grpc)")
|
||||
|
||||
// rpc flags
|
||||
@@ -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
|
||||
},
|
||||
|
@@ -16,7 +16,7 @@ var ShowValidatorCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func showValidator(cmd *cobra.Command, args []string) {
|
||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorFile())
|
||||
privValidator := privval.LoadOrGenFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
pubKeyJSONBytes, _ := cdc.MarshalJSON(privValidator.GetPubKey())
|
||||
fmt.Println(string(pubKeyJSONBytes))
|
||||
}
|
||||
|
@@ -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)
|
||||
|
||||
@@ -85,20 +85,28 @@ func testnetFiles(cmd *cobra.Command, args []string) error {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
}
|
||||
err = os.MkdirAll(filepath.Join(nodeDir, "data"), nodeDirPerm)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
}
|
||||
|
||||
initFilesWithConfig(config)
|
||||
|
||||
pvFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidator)
|
||||
pv := privval.LoadFilePV(pvFile)
|
||||
pvKeyFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorKey)
|
||||
pvStateFile := filepath.Join(nodeDir, config.BaseConfig.PrivValidatorState)
|
||||
|
||||
pv := privval.LoadFilePV(pvKeyFile, pvStateFile)
|
||||
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,28 +120,46 @@ 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
|
||||
}
|
||||
}
|
||||
|
||||
// Gather persistent peer addresses.
|
||||
var (
|
||||
persistentPeers string
|
||||
err error
|
||||
)
|
||||
if populatePersistentPeers {
|
||||
err := populatePersistentPeersInConfigAndWriteIt(config)
|
||||
persistentPeers, err = persistentPeersString(config)
|
||||
if err != nil {
|
||||
_ = os.RemoveAll(outputDir)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite default config.
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, fmt.Sprintf("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
config.P2P.AddrBookStrict = false
|
||||
config.P2P.AllowDuplicateIP = true
|
||||
if populatePersistentPeers {
|
||||
config.P2P.PersistentPeers = persistentPeers
|
||||
}
|
||||
|
||||
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully initialized %v node directories\n", nValidators+nNonValidators)
|
||||
return nil
|
||||
}
|
||||
@@ -156,28 +182,16 @@ func hostnameOrIP(i int) string {
|
||||
return fmt.Sprintf("%s%d", hostnamePrefix, i)
|
||||
}
|
||||
|
||||
func populatePersistentPeersInConfigAndWriteIt(config *cfg.Config) error {
|
||||
func persistentPeersString(config *cfg.Config) (string, 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 {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
persistentPeers[i] = p2p.IDAddressString(nodeKey.ID(), fmt.Sprintf("%s:%d", hostnameOrIP(i), p2pPort))
|
||||
}
|
||||
persistentPeersList := strings.Join(persistentPeers, ",")
|
||||
|
||||
for i := 0; i < nValidators+nNonValidators; i++ {
|
||||
nodeDir := filepath.Join(outputDir, cmn.Fmt("%s%d", nodeDirPrefix, i))
|
||||
config.SetRoot(nodeDir)
|
||||
config.P2P.PersistentPeers = persistentPeersList
|
||||
config.P2P.AddrBookStrict = false
|
||||
|
||||
// overwrite default config
|
||||
cfg.WriteConfigFile(filepath.Join(nodeDir, "config", "config.toml"), config)
|
||||
}
|
||||
|
||||
return nil
|
||||
return strings.Join(persistentPeers, ","), nil
|
||||
}
|
||||
|
484
config/config.go
484
config/config.go
@@ -5,6 +5,8 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,6 +14,11 @@ const (
|
||||
FuzzModeDrop = iota
|
||||
// FuzzModeDelay is a mode in which we randomly sleep
|
||||
FuzzModeDelay
|
||||
|
||||
// LogFormatPlain is a format for colored text
|
||||
LogFormatPlain = "plain"
|
||||
// LogFormatJSON is a format for json output
|
||||
LogFormatJSON = "json"
|
||||
)
|
||||
|
||||
// NOTE: Most of the structs & relevant comments + the
|
||||
@@ -19,7 +26,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"
|
||||
@@ -28,15 +35,24 @@ var (
|
||||
defaultConfigFileName = "config.toml"
|
||||
defaultGenesisJSONName = "genesis.json"
|
||||
|
||||
defaultPrivValName = "priv_validator.json"
|
||||
defaultPrivValKeyName = "priv_validator_key.json"
|
||||
defaultPrivValStateName = "priv_validator_state.json"
|
||||
|
||||
defaultNodeKeyName = "node_key.json"
|
||||
defaultAddrBookName = "addrbook.json"
|
||||
|
||||
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
|
||||
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
|
||||
defaultPrivValPath = filepath.Join(defaultConfigDir, defaultPrivValName)
|
||||
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
|
||||
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
||||
defaultConfigFilePath = filepath.Join(defaultConfigDir, defaultConfigFileName)
|
||||
defaultGenesisJSONPath = filepath.Join(defaultConfigDir, defaultGenesisJSONName)
|
||||
defaultPrivValKeyPath = filepath.Join(defaultConfigDir, defaultPrivValKeyName)
|
||||
defaultPrivValStatePath = filepath.Join(defaultDataDir, defaultPrivValStateName)
|
||||
|
||||
defaultNodeKeyPath = filepath.Join(defaultConfigDir, defaultNodeKeyName)
|
||||
defaultAddrBookPath = filepath.Join(defaultConfigDir, defaultAddrBookName)
|
||||
)
|
||||
|
||||
var (
|
||||
oldPrivVal = "priv_validator.json"
|
||||
oldPrivValPath = filepath.Join(defaultConfigDir, oldPrivVal)
|
||||
)
|
||||
|
||||
// Config defines the top level configuration for a Tendermint node
|
||||
@@ -89,12 +105,35 @@ 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.BaseConfig.ValidateBasic(); err != nil {
|
||||
return err
|
||||
}
|
||||
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,66 +141,74 @@ 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"`
|
||||
|
||||
// Output format: 'plain' (colored text) or 'json'
|
||||
LogFormat string `mapstructure:"log_format"`
|
||||
|
||||
// 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
|
||||
PrivValidatorKey string `mapstructure:"priv_validator_key_file"`
|
||||
|
||||
// Path to the JSON file containing the last sign state of a validator
|
||||
PrivValidatorState string `mapstructure:"priv_validator_state_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
|
||||
func DefaultBaseConfig() BaseConfig {
|
||||
return BaseConfig{
|
||||
Genesis: defaultGenesisJSONPath,
|
||||
PrivValidator: defaultPrivValPath,
|
||||
NodeKey: defaultNodeKeyPath,
|
||||
Moniker: defaultMoniker,
|
||||
ProxyApp: "tcp://127.0.0.1:26658",
|
||||
ABCI: "socket",
|
||||
LogLevel: DefaultPackageLogLevels(),
|
||||
ProfListenAddress: "",
|
||||
FastSync: true,
|
||||
FilterPeers: false,
|
||||
DBBackend: "leveldb",
|
||||
DBPath: "data",
|
||||
Genesis: defaultGenesisJSONPath,
|
||||
PrivValidatorKey: defaultPrivValKeyPath,
|
||||
PrivValidatorState: defaultPrivValStatePath,
|
||||
NodeKey: defaultNodeKeyPath,
|
||||
Moniker: defaultMoniker,
|
||||
ProxyApp: "tcp://127.0.0.1:26658",
|
||||
ABCI: "socket",
|
||||
LogLevel: DefaultPackageLogLevels(),
|
||||
LogFormat: LogFormatPlain,
|
||||
ProfListenAddress: "",
|
||||
FastSync: true,
|
||||
FilterPeers: false,
|
||||
DBBackend: "leveldb",
|
||||
DBPath: "data",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,9 +231,20 @@ func (cfg BaseConfig) GenesisFile() string {
|
||||
return rootify(cfg.Genesis, cfg.RootDir)
|
||||
}
|
||||
|
||||
// PrivValidatorFile returns the full path to the priv_validator.json file
|
||||
func (cfg BaseConfig) PrivValidatorFile() string {
|
||||
return rootify(cfg.PrivValidator, cfg.RootDir)
|
||||
// PrivValidatorKeyFile returns the full path to the priv_validator_key.json file
|
||||
func (cfg BaseConfig) PrivValidatorKeyFile() string {
|
||||
return rootify(cfg.PrivValidatorKey, cfg.RootDir)
|
||||
}
|
||||
|
||||
// PrivValidatorFile returns the full path to the priv_validator_state.json file
|
||||
func (cfg BaseConfig) PrivValidatorStateFile() string {
|
||||
return rootify(cfg.PrivValidatorState, cfg.RootDir)
|
||||
}
|
||||
|
||||
// OldPrivValidatorFile returns the full path of the priv_validator.json from pre v0.28.0.
|
||||
// TODO: eventually remove.
|
||||
func (cfg BaseConfig) OldPrivValidatorFile() string {
|
||||
return rootify(oldPrivValPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// NodeKeyFile returns the full path to the node_key.json file
|
||||
@@ -199,6 +257,17 @@ func (cfg BaseConfig) DBDir() string {
|
||||
return rootify(cfg.DBPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// ValidateBasic performs basic validation (checking param bounds, etc.) and
|
||||
// returns an error if any check fails.
|
||||
func (cfg BaseConfig) ValidateBasic() error {
|
||||
switch cfg.LogFormat {
|
||||
case LogFormatPlain, LogFormatJSON:
|
||||
default:
|
||||
return errors.New("unknown log_format (must be 'plain' or 'json')")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultLogLevel returns a default log level of "error"
|
||||
func DefaultLogLevel() string {
|
||||
return "error"
|
||||
@@ -220,13 +289,25 @@ type RPCConfig struct {
|
||||
// TCP or UNIX socket address for the RPC server to listen on
|
||||
ListenAddress string `mapstructure:"laddr"`
|
||||
|
||||
// A list of origins a cross-domain request can be executed from.
|
||||
// If the special '*' value is present in the list, all origins will be allowed.
|
||||
// An origin may contain a wildcard (*) to replace 0 or more characters (i.e.: http://*.domain.com).
|
||||
// Only one wildcard can be used per origin.
|
||||
CORSAllowedOrigins []string `mapstructure:"cors_allowed_origins"`
|
||||
|
||||
// A list of methods the client is allowed to use with cross-domain requests.
|
||||
CORSAllowedMethods []string `mapstructure:"cors_allowed_methods"`
|
||||
|
||||
// A list of non simple headers the client is allowed to use with cross-domain requests.
|
||||
CORSAllowedHeaders []string `mapstructure:"cors_allowed_headers"`
|
||||
|
||||
// TCP or UNIX socket address for the gRPC server to listen on
|
||||
// NOTE: This server only supports /broadcast_tx_commit
|
||||
GRPCListenAddress string `mapstructure:"grpc_laddr"`
|
||||
|
||||
// Maximum number of simultaneous connections.
|
||||
// Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
||||
// If you want to accept more significant number than the default, make sure
|
||||
// If you want to accept a larger number than the default, make sure
|
||||
// you increase your OS limits.
|
||||
// 0 - unlimited.
|
||||
GRPCMaxOpenConnections int `mapstructure:"grpc_max_open_connections"`
|
||||
@@ -236,23 +317,25 @@ type RPCConfig struct {
|
||||
|
||||
// Maximum number of simultaneous connections (including WebSocket).
|
||||
// Does not include gRPC connections. See grpc_max_open_connections
|
||||
// If you want to accept more significant number than the default, make sure
|
||||
// If you want to accept a larger 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"`
|
||||
}
|
||||
|
||||
// DefaultRPCConfig returns a default configuration for the RPC server
|
||||
func DefaultRPCConfig() *RPCConfig {
|
||||
return &RPCConfig{
|
||||
ListenAddress: "tcp://0.0.0.0:26657",
|
||||
|
||||
ListenAddress: "tcp://0.0.0.0:26657",
|
||||
CORSAllowedOrigins: []string{},
|
||||
CORSAllowedMethods: []string{"HEAD", "GET", "POST"},
|
||||
CORSAllowedHeaders: []string{"Origin", "Accept", "Content-Type", "X-Requested-With", "X-Server-Time"},
|
||||
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 +349,23 @@ 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
|
||||
}
|
||||
|
||||
// IsCorsEnabled returns true if cross-origin resource sharing is enabled.
|
||||
func (cfg *RPCConfig) IsCorsEnabled() bool {
|
||||
return len(cfg.CORSAllowedOrigins) != 0
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// P2PConfig
|
||||
|
||||
@@ -293,13 +393,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,14 +450,15 @@ 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
|
||||
PexReactor: true,
|
||||
SeedMode: false,
|
||||
AllowDuplicateIP: true, // so non-breaking yet
|
||||
AllowDuplicateIP: false,
|
||||
HandshakeTimeout: 20 * time.Second,
|
||||
DialTimeout: 3 * time.Second,
|
||||
TestDialFail: false,
|
||||
@@ -366,7 +471,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 +481,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 +530,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 +563,23 @@ func (cfg *MempoolConfig) WalDir() string {
|
||||
return rootify(cfg.WalPath, cfg.RootDir)
|
||||
}
|
||||
|
||||
// WalEnabled returns true if the WAL is enabled.
|
||||
func (cfg *MempoolConfig) WalEnabled() bool {
|
||||
return cfg.WalPath != ""
|
||||
}
|
||||
|
||||
// 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 +590,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 +700,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 +752,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"`
|
||||
}
|
||||
|
||||
@@ -607,10 +798,13 @@ type InstrumentationConfig struct {
|
||||
PrometheusListenAddr string `mapstructure:"prometheus_listen_addr"`
|
||||
|
||||
// Maximum number of simultaneous connections.
|
||||
// If you want to accept more significant number than the default, make sure
|
||||
// If you want to accept a larger number than the default, make sure
|
||||
// you increase your OS limits.
|
||||
// 0 - unlimited.
|
||||
MaxOpenConnections int `mapstructure:"max_open_connections"`
|
||||
|
||||
// Instrumentation namespace.
|
||||
Namespace string `mapstructure:"namespace"`
|
||||
}
|
||||
|
||||
// DefaultInstrumentationConfig returns a default configuration for metrics
|
||||
@@ -620,6 +814,7 @@ func DefaultInstrumentationConfig() *InstrumentationConfig {
|
||||
Prometheus: false,
|
||||
PrometheusListenAddr: ":26660",
|
||||
MaxOpenConnections: 3,
|
||||
Namespace: "tendermint",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,6 +824,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())
|
||||
}
|
||||
|
128
config/toml.go
128
config/toml.go
@@ -77,25 +77,35 @@ 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 }}"
|
||||
|
||||
# Output format: 'plain' (colored text) or 'json'
|
||||
log_format = "{{ .BaseConfig.LogFormat }}"
|
||||
|
||||
##### additional base config options #####
|
||||
|
||||
# Path to the JSON file containing the initial validator set and other meta data
|
||||
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 }}"
|
||||
priv_validator_key_file = "{{ js .BaseConfig.PrivValidatorKey }}"
|
||||
|
||||
# Path to the JSON file containing the last sign state of a validator
|
||||
priv_validator_state_file = "{{ js .BaseConfig.PrivValidatorState }}"
|
||||
|
||||
# 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 }}"
|
||||
@@ -115,15 +125,28 @@ filter_peers = {{ .BaseConfig.FilterPeers }}
|
||||
# TCP or UNIX socket address for the RPC server to listen on
|
||||
laddr = "{{ .RPC.ListenAddress }}"
|
||||
|
||||
# A list of origins a cross-domain request can be executed from
|
||||
# Default value '[]' disables cors support
|
||||
# Use '["*"]' to allow any origin
|
||||
cors_allowed_origins = [{{ range .RPC.CORSAllowedOrigins }}{{ printf "%q, " . }}{{end}}]
|
||||
|
||||
# A list of methods the client is allowed to use with cross-domain requests
|
||||
cors_allowed_methods = [{{ range .RPC.CORSAllowedMethods }}{{ printf "%q, " . }}{{end}}]
|
||||
|
||||
# A list of non simple headers the client is allowed to use with cross-domain requests
|
||||
cors_allowed_headers = [{{ range .RPC.CORSAllowedHeaders }}{{ printf "%q, " . }}{{end}}]
|
||||
|
||||
# TCP or UNIX socket address for the gRPC server to listen on
|
||||
# NOTE: This server only supports /broadcast_tx_commit
|
||||
grpc_laddr = "{{ .RPC.GRPCListenAddress }}"
|
||||
|
||||
# Maximum number of simultaneous connections.
|
||||
# Does not include RPC (HTTP&WebSocket) connections. See max_open_connections
|
||||
# If you want to accept more significant number than the default, make sure
|
||||
# If you want to accept a larger 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
|
||||
@@ -131,9 +154,11 @@ unsafe = {{ .RPC.Unsafe }}
|
||||
|
||||
# Maximum number of simultaneous connections (including WebSocket).
|
||||
# Does not include gRPC connections. See grpc_max_open_connections
|
||||
# If you want to accept more significant number than the default, make sure
|
||||
# If you want to accept a larger 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 +186,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 +219,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 +244,27 @@ 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 }}"
|
||||
|
||||
# Block time parameters. Corresponds to the minimum time increment between consecutive blocks.
|
||||
blocktime_iota = "{{ .Consensus.BlockTimeIota }}"
|
||||
|
||||
##### transactions indexer configuration options #####
|
||||
[tx_index]
|
||||
@@ -235,20 +272,25 @@ peer_query_maj23_sleep_duration = {{ .Consensus.PeerQueryMaj23SleepDuration }}
|
||||
# What indexer to use for transactions
|
||||
#
|
||||
# Options:
|
||||
# 1) "null" (default)
|
||||
# 2) "kv" - the simplest possible indexer, backed by key-value storage (defaults to levelDB; see DBBackend).
|
||||
# 1) "null"
|
||||
# 2) "kv" (default) - 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 #####
|
||||
@@ -263,10 +305,13 @@ prometheus = {{ .Instrumentation.Prometheus }}
|
||||
prometheus_listen_addr = "{{ .Instrumentation.PrometheusListenAddr }}"
|
||||
|
||||
# Maximum number of simultaneous connections.
|
||||
# If you want to accept more significant number than the default, make sure
|
||||
# If you want to accept a larger number than the default, make sure
|
||||
# you increase your OS limits.
|
||||
# 0 - unlimited.
|
||||
max_open_connections = {{ .Instrumentation.MaxOpenConnections }}
|
||||
|
||||
# Instrumentation namespace
|
||||
namespace = "{{ .Instrumentation.Namespace }}"
|
||||
`
|
||||
|
||||
/****** these are for test settings ***********/
|
||||
@@ -300,7 +345,8 @@ func ResetTestRoot(testName string) *Config {
|
||||
baseConfig := DefaultBaseConfig()
|
||||
configFilePath := filepath.Join(rootDir, defaultConfigFilePath)
|
||||
genesisFilePath := filepath.Join(rootDir, baseConfig.Genesis)
|
||||
privFilePath := filepath.Join(rootDir, baseConfig.PrivValidator)
|
||||
privKeyFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorKey)
|
||||
privStateFilePath := filepath.Join(rootDir, baseConfig.PrivValidatorState)
|
||||
|
||||
// Write default config file if missing.
|
||||
if !cmn.FileExists(configFilePath) {
|
||||
@@ -310,14 +356,15 @@ func ResetTestRoot(testName string) *Config {
|
||||
cmn.MustWriteFile(genesisFilePath, []byte(testGenesis), 0644)
|
||||
}
|
||||
// we always overwrite the priv val
|
||||
cmn.MustWriteFile(privFilePath, []byte(testPrivValidator), 0644)
|
||||
cmn.MustWriteFile(privKeyFilePath, []byte(testPrivValidatorKey), 0644)
|
||||
cmn.MustWriteFile(privStateFilePath, []byte(testPrivValidatorState), 0644)
|
||||
|
||||
config := TestConfig().SetRoot(rootDir)
|
||||
return config
|
||||
}
|
||||
|
||||
var testGenesis = `{
|
||||
"genesis_time": "0001-01-01T00:00:00.000Z",
|
||||
"genesis_time": "2018-10-10T08:20:13.695936996Z",
|
||||
"chain_id": "tendermint_test",
|
||||
"validators": [
|
||||
{
|
||||
@@ -332,7 +379,7 @@ var testGenesis = `{
|
||||
"app_hash": ""
|
||||
}`
|
||||
|
||||
var testPrivValidator = `{
|
||||
var testPrivValidatorKey = `{
|
||||
"address": "A3258DCBF45DCA0DF052981870F2D1441A36D145",
|
||||
"pub_key": {
|
||||
"type": "tendermint/PubKeyEd25519",
|
||||
@@ -341,8 +388,11 @@ var testPrivValidator = `{
|
||||
"priv_key": {
|
||||
"type": "tendermint/PrivKeyEd25519",
|
||||
"value": "EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="
|
||||
},
|
||||
"last_height": "0",
|
||||
"last_round": "0",
|
||||
"last_step": 0
|
||||
}
|
||||
}`
|
||||
|
||||
var testPrivValidatorState = `{
|
||||
"height": "0",
|
||||
"round": "0",
|
||||
"step": 0
|
||||
}`
|
||||
|
@@ -60,7 +60,7 @@ func TestEnsureTestRoot(t *testing.T) {
|
||||
|
||||
// TODO: make sure the cfg returned and testconfig are the same!
|
||||
baseConfig := DefaultBaseConfig()
|
||||
ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidator)
|
||||
ensureFiles(t, rootDir, defaultDataDir, baseConfig.Genesis, baseConfig.PrivValidatorKey, baseConfig.PrivValidatorState)
|
||||
}
|
||||
|
||||
func checkConfig(configFile string) bool {
|
||||
|
@@ -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")
|
||||
}
|
||||
@@ -172,16 +179,16 @@ func byzantineDecideProposalFunc(t *testing.T, height int64, round int, cs *Cons
|
||||
|
||||
// Create a new proposal block from state/txs from the mempool.
|
||||
block1, blockParts1 := cs.createProposalBlock()
|
||||
polRound, polBlockID := cs.Votes.POLInfo()
|
||||
proposal1 := types.NewProposal(height, round, blockParts1.Header(), polRound, polBlockID)
|
||||
polRound, propBlockID := cs.ValidRound, types.BlockID{block1.Hash(), blockParts1.Header()}
|
||||
proposal1 := types.NewProposal(height, round, polRound, propBlockID)
|
||||
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Create a new proposal block from state/txs from the mempool.
|
||||
block2, blockParts2 := cs.createProposalBlock()
|
||||
polRound, polBlockID = cs.Votes.POLInfo()
|
||||
proposal2 := types.NewProposal(height, round, blockParts2.Header(), polRound, polBlockID)
|
||||
polRound, propBlockID = cs.ValidRound, types.BlockID{block2.Hash(), blockParts2.Header()}
|
||||
proposal2 := types.NewProposal(height, round, polRound, propBlockID)
|
||||
if err := cs.privValidator.SignProposal(cs.state.ChainID, proposal2); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
@@ -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}))
|
||||
@@ -256,7 +263,7 @@ func (br *ByzantineReactor) AddPeer(peer p2p.Peer) {
|
||||
// Send our state to peer.
|
||||
// If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus().
|
||||
if !br.reactor.fastSync {
|
||||
br.reactor.sendNewRoundStepMessages(peer)
|
||||
br.reactor.sendNewRoundStepMessage(peer)
|
||||
}
|
||||
}
|
||||
func (br *ByzantineReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
|
||||
|
@@ -6,30 +6,31 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log/term"
|
||||
|
||||
abcicli "github.com/tendermint/tendermint/abci/client"
|
||||
"github.com/tendermint/tendermint/abci/example/counter"
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
bc "github.com/tendermint/tendermint/blockchain"
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
cstypes "github.com/tendermint/tendermint/consensus/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
dbm "github.com/tendermint/tendermint/libs/db"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
mempl "github.com/tendermint/tendermint/mempool"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
"github.com/tendermint/tendermint/privval"
|
||||
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/abci/example/counter"
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
|
||||
"github.com/go-kit/kit/log/term"
|
||||
tmtime "github.com/tendermint/tendermint/types/time"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -37,8 +38,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 +70,14 @@ 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) {
|
||||
addr := vs.PrivValidator.GetPubKey().Address()
|
||||
vote := &types.Vote{
|
||||
ValidatorIndex: vs.Index,
|
||||
ValidatorAddress: vs.PrivValidator.GetAddress(),
|
||||
ValidatorAddress: addr,
|
||||
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)
|
||||
@@ -128,8 +130,8 @@ func decideProposal(cs1 *ConsensusState, vs *validatorStub, height int64, round
|
||||
}
|
||||
|
||||
// Make proposal
|
||||
polRound, polBlockID := cs1.Votes.POLInfo()
|
||||
proposal = types.NewProposal(height, round, blockParts.Header(), polRound, polBlockID)
|
||||
polRound, propBlockID := cs1.ValidRound, types.BlockID{block.Hash(), blockParts.Header()}
|
||||
proposal = types.NewProposal(height, round, polRound, propBlockID)
|
||||
if err := vs.SignProposal(cs1.state.ChainID, proposal); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -142,15 +144,16 @@ 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...)
|
||||
}
|
||||
|
||||
func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *validatorStub, blockHash []byte) {
|
||||
prevotes := cs.Votes.Prevotes(round)
|
||||
address := privVal.GetPubKey().Address()
|
||||
var vote *types.Vote
|
||||
if vote = prevotes.GetByAddress(privVal.GetAddress()); vote == nil {
|
||||
if vote = prevotes.GetByAddress(address); vote == nil {
|
||||
panic("Failed to find prevote from validator")
|
||||
}
|
||||
if blockHash == nil {
|
||||
@@ -166,8 +169,9 @@ func validatePrevote(t *testing.T, cs *ConsensusState, round int, privVal *valid
|
||||
|
||||
func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorStub, blockHash []byte) {
|
||||
votes := cs.LastCommit
|
||||
address := privVal.GetPubKey().Address()
|
||||
var vote *types.Vote
|
||||
if vote = votes.GetByAddress(privVal.GetAddress()); vote == nil {
|
||||
if vote = votes.GetByAddress(address); vote == nil {
|
||||
panic("Failed to find precommit from validator")
|
||||
}
|
||||
if !bytes.Equal(vote.BlockID.Hash, blockHash) {
|
||||
@@ -177,8 +181,9 @@ func validateLastPrecommit(t *testing.T, cs *ConsensusState, privVal *validatorS
|
||||
|
||||
func validatePrecommit(t *testing.T, cs *ConsensusState, thisRound, lockRound int, privVal *validatorStub, votedBlockHash, lockedBlockHash []byte) {
|
||||
precommits := cs.Votes.Precommits(thisRound)
|
||||
address := privVal.GetPubKey().Address()
|
||||
var vote *types.Vote
|
||||
if vote = precommits.GetByAddress(privVal.GetAddress()); vote == nil {
|
||||
if vote = precommits.GetByAddress(address); vote == nil {
|
||||
panic("Failed to find precommit from validator")
|
||||
}
|
||||
|
||||
@@ -279,9 +284,10 @@ func newConsensusStateWithConfigAndBlockStore(thisConfig *cfg.Config, state sm.S
|
||||
}
|
||||
|
||||
func loadPrivValidator(config *cfg.Config) *privval.FilePV {
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
ensureDir(path.Dir(privValidatorFile), 0700)
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
||||
privValidatorKeyFile := config.PrivValidatorKeyFile()
|
||||
ensureDir(filepath.Dir(privValidatorKeyFile), 0700)
|
||||
privValidatorStateFile := config.PrivValidatorStateFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
|
||||
privValidator.Reset()
|
||||
return privValidator
|
||||
}
|
||||
@@ -305,23 +311,260 @@ 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) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewRound event")
|
||||
case ev := <-roundCh:
|
||||
rs, ok := ev.(types.EventDataNewRound)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataNewRound, 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewProposal event")
|
||||
case ev := <-proposalCh:
|
||||
rs, ok := ev.(types.EventDataCompleteProposal)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataCompleteProposal, got %v.Wrong subscription channel?",
|
||||
reflect.TypeOf(rs)))
|
||||
}
|
||||
if rs.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height))
|
||||
}
|
||||
if rs.Round != round {
|
||||
panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensureNewValidBlock(validBlockCh <-chan interface{}, height int64, round int) {
|
||||
ensureNewEvent(validBlockCh, height, round, ensureTimeout,
|
||||
"Timeout expired while waiting for NewValidBlock 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 ensureProposal(proposalCh <-chan interface{}, height int64, round int, propId types.BlockID) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for NewProposal event")
|
||||
case ev := <-proposalCh:
|
||||
rs, ok := ev.(types.EventDataCompleteProposal)
|
||||
if !ok {
|
||||
panic(
|
||||
fmt.Sprintf(
|
||||
"expected a EventDataCompleteProposal, got %v.Wrong subscription channel?",
|
||||
reflect.TypeOf(rs)))
|
||||
}
|
||||
if rs.Height != height {
|
||||
panic(fmt.Sprintf("expected height %v, got %v", height, rs.Height))
|
||||
}
|
||||
if rs.Round != round {
|
||||
panic(fmt.Sprintf("expected round %v, got %v", round, rs.Round))
|
||||
}
|
||||
if !rs.BlockID.Equals(propId) {
|
||||
panic("Proposed block does not match expected block")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ensurePrecommit(voteCh <-chan interface{}, height int64, round int) {
|
||||
ensureVote(voteCh, height, round, types.PrecommitType)
|
||||
}
|
||||
|
||||
func ensurePrevote(voteCh <-chan interface{}, height int64, round int) {
|
||||
ensureVote(voteCh, height, round, types.PrevoteType)
|
||||
}
|
||||
|
||||
func ensureNewEventOnChannel(ch <-chan interface{}) {
|
||||
select {
|
||||
case <-time.After(ensureTimeout):
|
||||
panic("Timeout expired while waiting for new activity on the channel")
|
||||
case <-ch:
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,13 +591,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
|
||||
ensureDir(filepath.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,18 +615,26 @@ 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))
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||
ensureDir(filepath.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
var privVal types.PrivValidator
|
||||
if i < nValidators {
|
||||
privVal = privVals[i]
|
||||
} else {
|
||||
_, tempFilePath := cmn.Tempfile("priv_validator_")
|
||||
privVal = privval.GenFilePV(tempFilePath)
|
||||
tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
tempStateFile, err := ioutil.TempFile("", "priv_validator_state_")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privVal = privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name())
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -395,7 +646,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
|
||||
}
|
||||
}
|
||||
@@ -420,7 +671,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
|
||||
@@ -429,8 +680,6 @@ func randGenesisDoc(numValidators int, randPower bool, minPower int64) (*types.G
|
||||
func randGenesisState(numValidators int, randPower bool, minPower int64) (sm.State, []types.PrivValidator) {
|
||||
genDoc, privValidators := randGenesisDoc(numValidators, randPower, minPower)
|
||||
s0, _ := sm.MakeGenesisState(genDoc)
|
||||
db := dbm.NewMemDB()
|
||||
sm.SaveState(db, s0)
|
||||
return s0, privValidators
|
||||
}
|
||||
|
||||
|
@@ -10,8 +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 +27,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 +45,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 +71,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
|
||||
ensureNewRound(newRoundCh, height, round) // first round at first height
|
||||
ensureNewEventOnChannel(newBlockCh) // first block gets committed
|
||||
|
||||
height = height + 1 // moving to the next height
|
||||
round = 0
|
||||
|
||||
ensureNewRound(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
|
||||
ensureNewRound(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 +93,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 +104,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 +130,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 +153,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 +194,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(),
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -4,24 +4,27 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
|
||||
cfg "github.com/tendermint/tendermint/config"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
p2pdummy "github.com/tendermint/tendermint/p2p/dummy"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
|
||||
"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"
|
||||
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"
|
||||
"github.com/tendermint/tendermint/p2p"
|
||||
sm "github.com/tendermint/tendermint/state"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -94,51 +97,125 @@ func TestReactorBasic(t *testing.T) {
|
||||
|
||||
// Ensure we can process blocks with evidence
|
||||
func TestReactorWithEvidence(t *testing.T) {
|
||||
N := 4
|
||||
css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter)
|
||||
evpool := mockEvidencePool{
|
||||
t: t,
|
||||
ev: []types.Evidence{types.NewMockGoodEvidence(1, 1, []byte("somone"))},
|
||||
types.RegisterMockEvidences(cdc)
|
||||
types.RegisterMockEvidences(types.GetCodec())
|
||||
|
||||
nValidators := 4
|
||||
testName := "consensus_reactor_test"
|
||||
tickerFunc := newMockTickerFunc(true)
|
||||
appFunc := newCounter
|
||||
|
||||
// heed the advice from https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction
|
||||
// to unroll unwieldy abstractions. Here we duplicate the code from:
|
||||
// css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter)
|
||||
|
||||
genDoc, privVals := randGenesisDoc(nValidators, false, 30)
|
||||
css := make([]*ConsensusState, nValidators)
|
||||
logger := consensusLogger()
|
||||
for i := 0; i < nValidators; i++ {
|
||||
stateDB := dbm.NewMemDB() // each state needs its own db
|
||||
state, _ := sm.LoadStateFromDBOrGenesisDoc(stateDB, genDoc)
|
||||
thisConfig := ResetConfig(fmt.Sprintf("%s_%d", testName, i))
|
||||
ensureDir(path.Dir(thisConfig.Consensus.WalFile()), 0700) // dir for wal
|
||||
app := appFunc()
|
||||
vals := types.TM2PB.ValidatorUpdates(state.Validators)
|
||||
app.InitChain(abci.RequestInitChain{Validators: vals})
|
||||
|
||||
pv := privVals[i]
|
||||
// duplicate code from:
|
||||
// css[i] = newConsensusStateWithConfig(thisConfig, state, privVals[i], app)
|
||||
|
||||
blockDB := dbm.NewMemDB()
|
||||
blockStore := bc.NewBlockStore(blockDB)
|
||||
|
||||
// one for mempool, one for consensus
|
||||
mtx := new(sync.Mutex)
|
||||
proxyAppConnMem := abcicli.NewLocalClient(mtx, app)
|
||||
proxyAppConnCon := abcicli.NewLocalClient(mtx, app)
|
||||
|
||||
// Make Mempool
|
||||
mempool := mempl.NewMempool(thisConfig.Mempool, proxyAppConnMem, 0)
|
||||
mempool.SetLogger(log.TestingLogger().With("module", "mempool"))
|
||||
if thisConfig.Consensus.WaitForTxs() {
|
||||
mempool.EnableTxsAvailable()
|
||||
}
|
||||
|
||||
// mock the evidence pool
|
||||
// everyone includes evidence of another double signing
|
||||
vIdx := (i + 1) % nValidators
|
||||
addr := privVals[vIdx].GetPubKey().Address()
|
||||
evpool := newMockEvidencePool(addr)
|
||||
|
||||
// Make ConsensusState
|
||||
blockExec := sm.NewBlockExecutor(stateDB, log.TestingLogger(), proxyAppConnCon, mempool, evpool)
|
||||
cs := NewConsensusState(thisConfig.Consensus, state, blockExec, blockStore, mempool, evpool)
|
||||
cs.SetLogger(log.TestingLogger().With("module", "consensus"))
|
||||
cs.SetPrivValidator(pv)
|
||||
|
||||
eventBus := types.NewEventBus()
|
||||
eventBus.SetLogger(log.TestingLogger().With("module", "events"))
|
||||
eventBus.Start()
|
||||
cs.SetEventBus(eventBus)
|
||||
|
||||
cs.SetTimeoutTicker(tickerFunc())
|
||||
cs.SetLogger(logger.With("validator", i, "module", "consensus"))
|
||||
|
||||
css[i] = cs
|
||||
}
|
||||
for i := 0; i < N; i++ {
|
||||
css[i].evpool = evpool
|
||||
}
|
||||
reactors, eventChans, eventBuses := startConsensusNet(t, css, N)
|
||||
|
||||
reactors, eventChans, eventBuses := startConsensusNet(t, css, nValidators)
|
||||
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
||||
// wait till everyone makes the first new block
|
||||
timeoutWaitGroup(t, N, func(j int) {
|
||||
<-eventChans[j]
|
||||
|
||||
// wait till everyone makes the first new block with no evidence
|
||||
timeoutWaitGroup(t, nValidators, func(j int) {
|
||||
blockI := <-eventChans[j]
|
||||
block := blockI.(types.EventDataNewBlock).Block
|
||||
assert.True(t, len(block.Evidence.Evidence) == 0)
|
||||
}, css)
|
||||
|
||||
// second block should have evidence
|
||||
timeoutWaitGroup(t, N, func(j int) {
|
||||
<-eventChans[j]
|
||||
timeoutWaitGroup(t, nValidators, func(j int) {
|
||||
blockI := <-eventChans[j]
|
||||
block := blockI.(types.EventDataNewBlock).Block
|
||||
assert.True(t, len(block.Evidence.Evidence) > 0)
|
||||
}, css)
|
||||
}
|
||||
|
||||
// mock evidence pool returns no evidence for block 1,
|
||||
// and returnes one piece for all higher blocks. The one piece
|
||||
// is for a given validator at block 1.
|
||||
type mockEvidencePool struct {
|
||||
height int
|
||||
ev []types.Evidence
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
func (m mockEvidencePool) PendingEvidence() []types.Evidence {
|
||||
func newMockEvidencePool(val []byte) *mockEvidencePool {
|
||||
return &mockEvidencePool{
|
||||
ev: []types.Evidence{types.NewMockGoodEvidence(1, 1, val)},
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: maxBytes is ignored
|
||||
func (m *mockEvidencePool) PendingEvidence(maxBytes int64) []types.Evidence {
|
||||
if m.height > 0 {
|
||||
return m.ev
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (m mockEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
||||
func (m mockEvidencePool) Update(block *types.Block, state sm.State) {
|
||||
m.height += 1
|
||||
|
||||
func (m *mockEvidencePool) AddEvidence(types.Evidence) error { return nil }
|
||||
func (m *mockEvidencePool) Update(block *types.Block, state sm.State) {
|
||||
if m.height > 0 {
|
||||
require.True(m.t, len(block.Evidence.Evidence) > 0)
|
||||
if len(block.Evidence.Evidence) == 0 {
|
||||
panic("block has no evidence")
|
||||
}
|
||||
}
|
||||
m.height++
|
||||
}
|
||||
|
||||
// Ensure a testnet sends proposal heartbeats and makes blocks when there are txs
|
||||
func TestReactorProposalHeartbeats(t *testing.T) {
|
||||
//------------------------------------
|
||||
|
||||
// Ensure a testnet makes blocks when there are txs
|
||||
func TestReactorCreatesBlockWhenEmptyBlocksFalse(t *testing.T) {
|
||||
N := 4
|
||||
css := randConsensusNet(N, "consensus_reactor_test", newMockTickerFunc(true), newCounter,
|
||||
func(c *cfg.Config) {
|
||||
@@ -146,17 +223,6 @@ func TestReactorProposalHeartbeats(t *testing.T) {
|
||||
})
|
||||
reactors, eventChans, eventBuses := startConsensusNet(t, css, N)
|
||||
defer stopConsensusNet(log.TestingLogger(), reactors, eventBuses)
|
||||
heartbeatChans := make([]chan interface{}, N)
|
||||
var err error
|
||||
for i := 0; i < N; i++ {
|
||||
heartbeatChans[i] = make(chan interface{}, 1)
|
||||
err = eventBuses[i].Subscribe(context.Background(), testSubscriber, types.EventQueryProposalHeartbeat, heartbeatChans[i])
|
||||
require.NoError(t, err)
|
||||
}
|
||||
// wait till everyone sends a proposal heartbeat
|
||||
timeoutWaitGroup(t, N, func(j int) {
|
||||
<-heartbeatChans[j]
|
||||
}, css)
|
||||
|
||||
// send a tx
|
||||
if err := css[3].mempool.CheckTx([]byte{1, 2, 3}, nil); err != nil {
|
||||
@@ -169,110 +235,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")
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------
|
||||
@@ -288,7 +269,8 @@ func TestReactorVotingPowerChange(t *testing.T) {
|
||||
// map of active validators
|
||||
activeVals := make(map[string]struct{})
|
||||
for i := 0; i < nVals; i++ {
|
||||
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
||||
addr := css[i].privValidator.GetPubKey().Address()
|
||||
activeVals[string(addr)] = struct{}{}
|
||||
}
|
||||
|
||||
// wait till everyone makes block 1
|
||||
@@ -351,7 +333,8 @@ func TestReactorValidatorSetChanges(t *testing.T) {
|
||||
// map of active validators
|
||||
activeVals := make(map[string]struct{})
|
||||
for i := 0; i < nVals; i++ {
|
||||
activeVals[string(css[i].privValidator.GetAddress())] = struct{}{}
|
||||
addr := css[i].privValidator.GetPubKey().Address()
|
||||
activeVals[string(addr)] = struct{}{}
|
||||
}
|
||||
|
||||
// wait till everyone makes block 1
|
||||
@@ -465,7 +448,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)
|
||||
|
@@ -73,7 +73,7 @@ func (cs *ConsensusState) readReplayMessage(msg *TimedWALMessage, newStepCh chan
|
||||
case *ProposalMessage:
|
||||
p := msg.Proposal
|
||||
cs.Logger.Info("Replay: Proposal", "height", p.Height, "round", p.Round, "header",
|
||||
p.BlockPartsHeader, "pol", p.POLRound, "peer", peerID)
|
||||
p.BlockID.PartsHeader, "pol", p.POLRound, "peer", peerID)
|
||||
case *BlockPartMessage:
|
||||
cs.Logger.Info("Replay: BlockPart", "height", msg.Height, "round", msg.Round, "peer", peerID)
|
||||
case *VoteMessage:
|
||||
@@ -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})
|
||||
res, err := proxyApp.Query().InfoSync(proxy.RequestInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error calling Info: %v", err)
|
||||
}
|
||||
@@ -238,9 +238,16 @@ 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)
|
||||
sm.SaveState(h.stateDB, h.initialState)
|
||||
|
||||
// Replay blocks up to the latest in the blockstore.
|
||||
_, err = h.ReplayBlocks(h.initialState, appHash, blockHeight, proxyApp)
|
||||
@@ -258,21 +265,30 @@ func (h *Handshaker) Handshake(proxyApp proxy.AppConns) error {
|
||||
|
||||
// Replay all blocks since appBlockHeight and ensure the result matches the current state.
|
||||
// Returns the final AppHash or an error.
|
||||
func (h *Handshaker) ReplayBlocks(state sm.State, appHash []byte, appBlockHeight int64, proxyApp proxy.AppConns) ([]byte, error) {
|
||||
|
||||
func (h *Handshaker) ReplayBlocks(
|
||||
state sm.State,
|
||||
appHash []byte,
|
||||
appBlockHeight int64,
|
||||
proxyApp proxy.AppConns,
|
||||
) ([]byte, error) {
|
||||
storeBlockHeight := h.store.Height()
|
||||
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)
|
||||
validators := make([]*types.Validator, len(h.genDoc.Validators))
|
||||
for i, val := range h.genDoc.Validators {
|
||||
validators[i] = types.NewValidator(val.PubKey, val.Power)
|
||||
}
|
||||
validatorSet := types.NewValidatorSet(validators)
|
||||
nextVals := types.TM2PB.ValidatorUpdates(validatorSet)
|
||||
csParams := types.TM2PB.ConsensusParams(h.genDoc.ConsensusParams)
|
||||
req := abci.RequestInitChain{
|
||||
Time: h.genDoc.GenesisTime.Unix(), // TODO
|
||||
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,23 +296,30 @@ 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 len(res.Validators) > 0 {
|
||||
vals, err := types.PB2TM.Validators(res.Validators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if stateBlockHeight == 0 { //we only update state when we are in initial state
|
||||
// If the app returned validators or consensus params, update the state.
|
||||
if len(res.Validators) > 0 {
|
||||
vals, err := types.PB2TM.ValidatorUpdates(res.Validators)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
state.Validators = types.NewValidatorSet(vals)
|
||||
state.NextValidators = types.NewValidatorSet(vals)
|
||||
} else {
|
||||
// If validator set is not set in genesis and still empty after InitChain, exit.
|
||||
if len(h.genDoc.Validators) == 0 {
|
||||
return nil, fmt.Errorf("Validator set is nil in genesis and still empty after InitChain")
|
||||
}
|
||||
}
|
||||
state.Validators = types.NewValidatorSet(vals)
|
||||
|
||||
if res.ConsensusParams != nil {
|
||||
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
|
||||
}
|
||||
sm.SaveState(h.stateDB, state)
|
||||
}
|
||||
if res.ConsensusParams != nil {
|
||||
state.ConsensusParams = types.PB2TM.ConsensusParams(res.ConsensusParams)
|
||||
}
|
||||
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 +329,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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,18 @@ func (cs *ConsensusState) ReplayFile(file string, console bool) error {
|
||||
if err != nil {
|
||||
return errors.Errorf("failed to subscribe %s to %v", subscriber, types.EventQueryNewRoundStep)
|
||||
}
|
||||
defer cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
defer func() {
|
||||
// drain newStepCh to make sure we don't block
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-newStepCh:
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
}()
|
||||
|
||||
// just open the file for reading, no need to use wal
|
||||
fp, err := os.OpenFile(file, os.O_RDONLY, 0600)
|
||||
@@ -221,7 +232,18 @@ func (pb *playback) replayConsoleLoop() int {
|
||||
if err != nil {
|
||||
cmn.Exit(fmt.Sprintf("failed to subscribe %s to %v", subscriber, types.EventQueryNewRoundStep))
|
||||
}
|
||||
defer pb.cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
defer func() {
|
||||
// drain newStepCh to make sure we don't block
|
||||
LOOP:
|
||||
for {
|
||||
select {
|
||||
case <-newStepCh:
|
||||
default:
|
||||
break LOOP
|
||||
}
|
||||
}
|
||||
pb.cs.eventBus.Unsubscribe(ctx, subscriber, types.EventQueryNewRoundStep)
|
||||
}()
|
||||
|
||||
if len(tokens) == 1 {
|
||||
if err := pb.replayReset(1, newStepCh); err != nil {
|
||||
@@ -298,16 +320,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"
|
||||
@@ -18,17 +17,17 @@ import (
|
||||
|
||||
"github.com/tendermint/tendermint/abci/example/kvstore"
|
||||
abci "github.com/tendermint/tendermint/abci/types"
|
||||
crypto "github.com/tendermint/tendermint/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)
|
||||
@@ -324,30 +315,23 @@ func testHandshakeReplay(t *testing.T, nBlocks int, mode uint) {
|
||||
config := ResetConfig("proxy_test_")
|
||||
|
||||
walBody, err := WALWithNBlocks(NUM_BLOCKS)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
walFile := tempWALWithData(walBody)
|
||||
config.Consensus.SetWalFile(walFile)
|
||||
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorFile())
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
|
||||
wal, err := NewWAL(walFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
wal.SetLogger(log.TestingLogger())
|
||||
if err := wal.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = wal.Start()
|
||||
require.NoError(t, err)
|
||||
defer wal.Stop()
|
||||
|
||||
chain, commits, err := makeBlockchainFromWAL(wal)
|
||||
if err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey())
|
||||
stateDB, state, store := stateAndStore(config, privVal.GetPubKey(), kvstore.ProtocolVersion)
|
||||
store.chain = chain
|
||||
store.commits = commits
|
||||
|
||||
@@ -361,22 +345,25 @@ 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{""})
|
||||
res, err := proxyApp.Query().InfoSync(abci.RequestInfo{Version: ""})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -399,7 +386,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 +405,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 +436,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 +481,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 +513,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 +536,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 +546,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)
|
||||
@@ -581,7 +568,7 @@ func readPieceFromWAL(msg *TimedWALMessage) interface{} {
|
||||
case msgInfo:
|
||||
switch msg := m.Msg.(type) {
|
||||
case *ProposalMessage:
|
||||
return &msg.Proposal.BlockPartsHeader
|
||||
return &msg.Proposal.BlockID.PartsHeader
|
||||
case *BlockPartMessage:
|
||||
return msg.Part
|
||||
case *VoteMessage:
|
||||
@@ -595,9 +582,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 +610,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 +629,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())
|
||||
privVal := privval.LoadFilePV(config.PrivValidatorKeyFile(), config.PrivValidatorStateFile())
|
||||
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,16 +659,10 @@ func TestInitChainUpdateValidators(t *testing.T) {
|
||||
assert.Equal(t, newValAddr, expectValAddr)
|
||||
}
|
||||
|
||||
func newInitChainApp(vals []abci.Validator) *initChainApp {
|
||||
return &initChainApp{
|
||||
vals: vals,
|
||||
}
|
||||
}
|
||||
|
||||
// returns the vals on InitChain
|
||||
type initChainApp struct {
|
||||
abci.BaseApplication
|
||||
vals []abci.Validator
|
||||
vals []abci.ValidatorUpdate
|
||||
}
|
||||
|
||||
func (ica *initChainApp) InitChain(req abci.RequestInitChain) abci.ResponseInitChain {
|
||||
|
File diff suppressed because it is too large
Load Diff
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
|
||||
@@ -50,19 +50,20 @@ func TestPeerCatchupRounds(t *testing.T) {
|
||||
|
||||
func makeVoteHR(t *testing.T, height int64, round int, privVals []types.PrivValidator, valIndex int) *types.Vote {
|
||||
privVal := privVals[valIndex]
|
||||
addr := privVal.GetPubKey().Address()
|
||||
vote := &types.Vote{
|
||||
ValidatorAddress: privVal.GetAddress(),
|
||||
ValidatorAddress: addr,
|
||||
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"
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -55,3 +55,31 @@ func (prs PeerRoundState) StringIndented(indent string) string {
|
||||
indent, prs.CatchupCommit, prs.CatchupCommitRound,
|
||||
indent)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// These methods are for Protobuf Compatibility
|
||||
|
||||
// Size returns the size of the amino encoding, in bytes.
|
||||
func (ps *PeerRoundState) Size() int {
|
||||
bs, _ := ps.Marshal()
|
||||
return len(bs)
|
||||
}
|
||||
|
||||
// Marshal returns the amino encoding.
|
||||
func (ps *PeerRoundState) Marshal() ([]byte, error) {
|
||||
return cdc.MarshalBinaryBare(ps)
|
||||
}
|
||||
|
||||
// MarshalTo calls Marshal and copies to the given buffer.
|
||||
func (ps *PeerRoundState) MarshalTo(data []byte) (int, error) {
|
||||
bs, err := ps.Marshal()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return copy(data, bs), nil
|
||||
}
|
||||
|
||||
// Unmarshal deserializes from amino encoded form.
|
||||
func (ps *PeerRoundState) Unmarshal(bs []byte) error {
|
||||
return cdc.UnmarshalBinaryBare(bs, ps)
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/tendermint/tendermint/types"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -26,8 +26,15 @@ const (
|
||||
RoundStepPrecommitWait = RoundStepType(0x07) // Did receive any +2/3 precommits, start timeout
|
||||
RoundStepCommit = RoundStepType(0x08) // Entered commit state machine
|
||||
// NOTE: RoundStepNewHeight acts as RoundStepCommitWait.
|
||||
|
||||
// NOTE: Update IsValid method if you change this!
|
||||
)
|
||||
|
||||
// IsValid returns true if the step is valid, false if unknown/undefined.
|
||||
func (rs RoundStepType) IsValid() bool {
|
||||
return uint8(rs) >= 0x01 && uint8(rs) <= 0x08
|
||||
}
|
||||
|
||||
// String returns a string
|
||||
func (rs RoundStepType) String() string {
|
||||
switch rs {
|
||||
@@ -105,18 +112,50 @@ func (rs *RoundState) RoundStateSimple() RoundStateSimple {
|
||||
}
|
||||
}
|
||||
|
||||
// NewRoundEvent returns the RoundState with proposer information as an event.
|
||||
func (rs *RoundState) NewRoundEvent() types.EventDataNewRound {
|
||||
addr := rs.Validators.GetProposer().Address
|
||||
idx, _ := rs.Validators.GetByAddress(addr)
|
||||
|
||||
return types.EventDataNewRound{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Step: rs.Step.String(),
|
||||
Proposer: types.ValidatorInfo{
|
||||
Address: addr,
|
||||
Index: idx,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// CompleteProposalEvent returns information about a proposed block as an event.
|
||||
func (rs *RoundState) CompleteProposalEvent() types.EventDataCompleteProposal {
|
||||
// We must construct BlockID from ProposalBlock and ProposalBlockParts
|
||||
// cs.Proposal is not guaranteed to be set when this function is called
|
||||
blockId := types.BlockID{
|
||||
Hash: rs.ProposalBlock.Hash(),
|
||||
PartsHeader: rs.ProposalBlockParts.Header(),
|
||||
}
|
||||
|
||||
return types.EventDataCompleteProposal{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Step: rs.Step.String(),
|
||||
BlockID: blockId,
|
||||
}
|
||||
}
|
||||
|
||||
// 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{
|
||||
return types.EventDataRoundState{
|
||||
Height: rs.Height,
|
||||
Round: rs.Round,
|
||||
Step: rs.Step.String(),
|
||||
RoundState: &rsCopy,
|
||||
}
|
||||
return edrs
|
||||
}
|
||||
|
||||
// String returns a string
|
||||
@@ -162,3 +201,31 @@ func (rs *RoundState) StringShort() string {
|
||||
return fmt.Sprintf(`RoundState{H:%v R:%v S:%v ST:%v}`,
|
||||
rs.Height, rs.Round, rs.Step, rs.StartTime)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// These methods are for Protobuf Compatibility
|
||||
|
||||
// Size returns the size of the amino encoding, in bytes.
|
||||
func (rs *RoundStateSimple) Size() int {
|
||||
bs, _ := rs.Marshal()
|
||||
return len(bs)
|
||||
}
|
||||
|
||||
// Marshal returns the amino encoding.
|
||||
func (rs *RoundStateSimple) Marshal() ([]byte, error) {
|
||||
return cdc.MarshalBinaryBare(rs)
|
||||
}
|
||||
|
||||
// MarshalTo calls Marshal and copies to the given buffer.
|
||||
func (rs *RoundStateSimple) MarshalTo(data []byte) (int, error) {
|
||||
bs, err := rs.Marshal()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return copy(data, bs), nil
|
||||
}
|
||||
|
||||
// Unmarshal deserializes from amino encoded form.
|
||||
func (rs *RoundStateSimple) Unmarshal(bs []byte) error {
|
||||
return cdc.UnmarshalBinaryBare(bs, rs)
|
||||
}
|
||||
|
@@ -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 := ed25519.SignatureEd25519{}
|
||||
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,19 +62,16 @@ func BenchmarkRoundStateDeepCopy(b *testing.B) {
|
||||
parts := block.MakePartSet(4096)
|
||||
// Random Proposal
|
||||
proposal := &types.Proposal{
|
||||
Timestamp: time.Now(),
|
||||
BlockPartsHeader: types.PartSetHeader{
|
||||
Hash: cmn.RandBytes(20),
|
||||
},
|
||||
POLBlockID: blockID,
|
||||
Signature: sig,
|
||||
Timestamp: tmtime.Now(),
|
||||
BlockID: blockID,
|
||||
Signature: sig,
|
||||
}
|
||||
// Random HeightVoteSet
|
||||
// TODO: hvs :=
|
||||
|
||||
rs := &RoundState{
|
||||
StartTime: time.Now(),
|
||||
CommitTime: time.Now(),
|
||||
StartTime: tmtime.Now(),
|
||||
CommitTime: tmtime.Now(),
|
||||
Validators: vset,
|
||||
Proposal: proposal,
|
||||
ProposalBlock: block,
|
||||
|
@@ -1,14 +1,12 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"github.com/tendermint/go-amino"
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
|
||||
func init() {
|
||||
cryptoAmino.RegisterAmino(cdc)
|
||||
types.RegisterEvidences(cdc)
|
||||
types.RegisterBlockAmino(cdc)
|
||||
}
|
||||
|
@@ -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,15 @@ 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/libs/log"
|
||||
"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 +74,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
|
||||
}
|
||||
@@ -94,6 +96,11 @@ func (wal *baseWAL) Group() *auto.Group {
|
||||
return wal.group
|
||||
}
|
||||
|
||||
func (wal *baseWAL) SetLogger(l log.Logger) {
|
||||
wal.BaseService.Logger = l
|
||||
wal.group.SetLogger(l)
|
||||
}
|
||||
|
||||
func (wal *baseWAL) OnStart() error {
|
||||
size, err := wal.group.Head.Size()
|
||||
if err != nil {
|
||||
@@ -119,8 +126,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 +141,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,34 @@ 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
|
||||
privValidatorFile := config.PrivValidatorFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorFile)
|
||||
// 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.
|
||||
privValidatorKeyFile := config.PrivValidatorKeyFile()
|
||||
privValidatorStateFile := config.PrivValidatorStateFile()
|
||||
privValidator := privval.LoadOrGenFilePV(privValidatorKeyFile, privValidatorStateFile)
|
||||
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 +81,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 +88,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,69 @@ 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"
|
||||
"github.com/tendermint/tendermint/libs/log"
|
||||
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")
|
||||
require.NoError(t, 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),
|
||||
)
|
||||
require.NoError(t, err)
|
||||
wal.SetLogger(log.TestingLogger())
|
||||
err = wal.Start()
|
||||
require.NoError(t, err)
|
||||
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)
|
||||
require.NoError(t, 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}},
|
||||
@@ -48,14 +97,13 @@ func TestWALSearchForEndHeight(t *testing.T) {
|
||||
walFile := tempWALWithData(walBody)
|
||||
|
||||
wal, err := NewWAL(walFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
wal.SetLogger(log.TestingLogger())
|
||||
|
||||
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 +112,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 +141,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()
|
||||
|
||||
|
@@ -2,7 +2,7 @@ package consensus
|
||||
|
||||
import (
|
||||
"github.com/tendermint/go-amino"
|
||||
cryptoAmino "github.com/tendermint/tendermint/crypto/encoding/amino"
|
||||
"github.com/tendermint/tendermint/types"
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
@@ -10,5 +10,5 @@ var cdc = amino.NewCodec()
|
||||
func init() {
|
||||
RegisterConsensusMessages(cdc)
|
||||
RegisterWALMessages(cdc)
|
||||
cryptoAmino.RegisterAmino(cdc)
|
||||
types.RegisterBlockAmino(cdc)
|
||||
}
|
||||
|
@@ -24,9 +24,7 @@ crypto `.Bytes()` uses Amino:binary encoding, but Amino:JSON is also supported.
|
||||
Example Amino:JSON encodings:
|
||||
|
||||
ed25519.PrivKeyEd25519 - {"type":"954568A3288910","value":"EVkqJO/jIXp3rkASXfh9YnyToYXRXhBr6g9cQVxPFnQBP/5povV4HTjvsy530kybxKHwEi85iU8YL0qQhSYVoQ=="}
|
||||
crypto.SignatureEd25519 - {"type":"6BF5903DA1DB28","value":"77sQNZOrf7ltExpf7AV1WaYPCHbyRLgjBsoWVzcduuLk+jIGmYk+s5R6Emm29p12HeiNAuhUJgdFGmwkpeGJCA=="}
|
||||
ed25519.PubKeyEd25519 - {"type":"AC26791624DE60","value":"AT/+aaL1eB0477Mud9JMm8Sh8BIvOYlPGC9KkIUmFaE="}
|
||||
crypto.PrivKeySecp256k1 - {"type":"019E82E1B0F798","value":"zx4Pnh67N+g2V+5vZbQzEyRerX9c4ccNZOVzM9RvJ0Y="}
|
||||
crypto.SignatureSecp256k1 - {"type":"6D1EA416E1FEE8","value":"MEUCIQCIg5TqS1l7I+MKTrSPIuUN2+4m5tA29dcauqn3NhEJ2wIgICaZ+lgRc5aOTVahU/XoLopXKn8BZcl0bnuYWLvohR8="}
|
||||
crypto.PubKeySecp256k1 - {"type":"F8CCEAEB5AE980","value":"A8lPKJXcNl5VHt1FK8a244K9EJuS4WX1hFBnwisi0IJx"}
|
||||
```
|
||||
|
@@ -1,32 +1,36 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
type PrivKey interface {
|
||||
Bytes() []byte
|
||||
Sign(msg []byte) (Signature, error)
|
||||
PubKey() PubKey
|
||||
Equals(PrivKey) bool
|
||||
}
|
||||
const (
|
||||
// AddressSize is the size of a pubkey address.
|
||||
AddressSize = tmhash.TruncatedSize
|
||||
)
|
||||
|
||||
// An address is a []byte, but hex-encoded even in JSON.
|
||||
// []byte leaves us the option to change the address length.
|
||||
// Use an alias so Unmarshal methods (with ptr receivers) are available too.
|
||||
type Address = cmn.HexBytes
|
||||
|
||||
func AddressHash(bz []byte) Address {
|
||||
return Address(tmhash.SumTruncated(bz))
|
||||
}
|
||||
|
||||
type PubKey interface {
|
||||
Address() Address
|
||||
Bytes() []byte
|
||||
VerifyBytes(msg []byte, sig Signature) bool
|
||||
VerifyBytes(msg []byte, sig []byte) bool
|
||||
Equals(PubKey) bool
|
||||
}
|
||||
|
||||
type Signature interface {
|
||||
type PrivKey interface {
|
||||
Bytes() []byte
|
||||
IsZero() bool
|
||||
Equals(Signature) bool
|
||||
Sign(msg []byte) ([]byte, error)
|
||||
PubKey() PubKey
|
||||
Equals(PrivKey) bool
|
||||
}
|
||||
|
||||
type Symmetric interface {
|
||||
|
26
crypto/ed25519/bench_test.go
Normal file
26
crypto/ed25519/bench_test.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package ed25519
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/internal/benchmarking"
|
||||
)
|
||||
|
||||
func BenchmarkKeyGeneration(b *testing.B) {
|
||||
benchmarkKeygenWrapper := func(reader io.Reader) crypto.PrivKey {
|
||||
return genPrivKey(reader)
|
||||
}
|
||||
benchmarking.BenchmarkKeyGeneration(b, benchmarkKeygenWrapper)
|
||||
}
|
||||
|
||||
func BenchmarkSigning(b *testing.B) {
|
||||
priv := GenPrivKey()
|
||||
benchmarking.BenchmarkSigning(b, priv)
|
||||
}
|
||||
|
||||
func BenchmarkVerification(b *testing.B) {
|
||||
priv := GenPrivKey()
|
||||
benchmarking.BenchmarkVerification(b, priv)
|
||||
}
|
@@ -4,13 +4,13 @@ import (
|
||||
"bytes"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/tendermint/ed25519"
|
||||
"github.com/tendermint/ed25519/extra25519"
|
||||
amino "github.com/tendermint/go-amino"
|
||||
"golang.org/x/crypto/ed25519"
|
||||
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
"github.com/tendermint/tendermint/crypto/tmhash"
|
||||
cmn "github.com/tendermint/tendermint/libs/common"
|
||||
)
|
||||
|
||||
//-------------------------------------
|
||||
@@ -18,9 +18,11 @@ import (
|
||||
var _ crypto.PrivKey = PrivKeyEd25519{}
|
||||
|
||||
const (
|
||||
Ed25519PrivKeyAminoRoute = "tendermint/PrivKeyEd25519"
|
||||
Ed25519PubKeyAminoRoute = "tendermint/PubKeyEd25519"
|
||||
Ed25519SignatureAminoRoute = "tendermint/SignatureEd25519"
|
||||
PrivKeyAminoName = "tendermint/PrivKeyEd25519"
|
||||
PubKeyAminoName = "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.
|
||||
SignatureSize = 64
|
||||
)
|
||||
|
||||
var cdc = amino.NewCodec()
|
||||
@@ -28,15 +30,11 @@ var cdc = amino.NewCodec()
|
||||
func init() {
|
||||
cdc.RegisterInterface((*crypto.PubKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PubKeyEd25519{},
|
||||
Ed25519PubKeyAminoRoute, nil)
|
||||
PubKeyAminoName, nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||
cdc.RegisterConcrete(PrivKeyEd25519{},
|
||||
Ed25519PrivKeyAminoRoute, nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.Signature)(nil), nil)
|
||||
cdc.RegisterConcrete(SignatureEd25519{},
|
||||
Ed25519SignatureAminoRoute, nil)
|
||||
PrivKeyAminoName, nil)
|
||||
}
|
||||
|
||||
// PrivKeyEd25519 implements crypto.PrivKey.
|
||||
@@ -48,10 +46,15 @@ func (privKey PrivKeyEd25519) Bytes() []byte {
|
||||
}
|
||||
|
||||
// Sign produces a signature on the provided message.
|
||||
func (privKey PrivKeyEd25519) Sign(msg []byte) (crypto.Signature, error) {
|
||||
privKeyBytes := [64]byte(privKey)
|
||||
signatureBytes := ed25519.Sign(&privKeyBytes, msg)
|
||||
return SignatureEd25519(*signatureBytes), nil
|
||||
// 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) {
|
||||
signatureBytes := ed25519.Sign(privKey[:], msg)
|
||||
return signatureBytes[:], nil
|
||||
}
|
||||
|
||||
// PubKey gets the corresponding public key from the private key.
|
||||
@@ -67,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.
|
||||
@@ -87,28 +90,25 @@ 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.
|
||||
func GenPrivKey() PrivKeyEd25519 {
|
||||
privKey := new([64]byte)
|
||||
copy(privKey[:32], crypto.CRandBytes(32))
|
||||
// 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)
|
||||
return genPrivKey(crypto.CReader())
|
||||
}
|
||||
|
||||
// genPrivKey generates a new ed25519 private key using the provided reader.
|
||||
func genPrivKey(rand io.Reader) PrivKeyEd25519 {
|
||||
seed := make([]byte, 32)
|
||||
_, err := io.ReadFull(rand, seed[:])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
privKey := ed25519.NewKeyFromSeed(seed)
|
||||
var privKeyEd PrivKeyEd25519
|
||||
copy(privKeyEd[:], privKey)
|
||||
return privKeyEd
|
||||
}
|
||||
|
||||
// GenPrivKeyFromSecret hashes the secret with SHA2, and uses
|
||||
@@ -116,14 +116,12 @@ func GenPrivKey() 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
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
@@ -138,7 +136,7 @@ type PubKeyEd25519 [PubKeyEd25519Size]byte
|
||||
|
||||
// Address is the SHA256-20 of the raw pubkey bytes.
|
||||
func (pubKey PubKeyEd25519) Address() crypto.Address {
|
||||
return crypto.Address(tmhash.Sum(pubKey[:]))
|
||||
return crypto.Address(tmhash.SumTruncated(pubKey[:]))
|
||||
}
|
||||
|
||||
// Bytes marshals the PubKey using amino encoding.
|
||||
@@ -150,30 +148,12 @@ func (pubKey PubKeyEd25519) Bytes() []byte {
|
||||
return bz
|
||||
}
|
||||
|
||||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig_ crypto.Signature) bool {
|
||||
func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig []byte) bool {
|
||||
// make sure we use the same algorithm to sign
|
||||
sig, ok := sig_.(SignatureEd25519)
|
||||
if !ok {
|
||||
if len(sig) != SignatureSize {
|
||||
return false
|
||||
}
|
||||
pubKeyBytes := [PubKeyEd25519Size]byte(pubKey)
|
||||
sigBytes := [SignatureEd25519Size]byte(sig)
|
||||
return ed25519.Verify(&pubKeyBytes, msg, &sigBytes)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
@@ -188,40 +168,3 @@ func (pubKey PubKeyEd25519) Equals(other crypto.PubKey) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------
|
||||
|
||||
var _ crypto.Signature = SignatureEd25519{}
|
||||
|
||||
// Size of an Edwards25519 signature. Namely the size of a compressed
|
||||
// Edwards25519 point, and a field element. Both of which are 32 bytes.
|
||||
const SignatureEd25519Size = 64
|
||||
|
||||
// SignatureEd25519 implements crypto.Signature
|
||||
type SignatureEd25519 [SignatureEd25519Size]byte
|
||||
|
||||
func (sig SignatureEd25519) Bytes() []byte {
|
||||
bz, err := cdc.MarshalBinaryBare(sig)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return bz
|
||||
}
|
||||
|
||||
func (sig SignatureEd25519) IsZero() bool { return len(sig) == 0 }
|
||||
|
||||
func (sig SignatureEd25519) String() string { return fmt.Sprintf("/%X.../", cmn.Fingerprint(sig[:])) }
|
||||
|
||||
func (sig SignatureEd25519) Equals(other crypto.Signature) bool {
|
||||
if otherEd, ok := other.(SignatureEd25519); ok {
|
||||
return subtle.ConstantTimeCompare(sig[:], otherEd[:]) == 1
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func SignatureEd25519FromBytes(data []byte) crypto.Signature {
|
||||
var sig SignatureEd25519
|
||||
copy(sig[:], data)
|
||||
return sig
|
||||
}
|
||||
|
@@ -23,9 +23,7 @@ func TestSignAndValidateEd25519(t *testing.T) {
|
||||
|
||||
// Mutate the signature, just one bit.
|
||||
// TODO: Replace this with a much better fuzzer, tendermint/ed25519/issues/10
|
||||
sigEd := sig.(ed25519.SignatureEd25519)
|
||||
sigEd[7] ^= byte(0x01)
|
||||
sig = sigEd
|
||||
sig[7] ^= byte(0x01)
|
||||
|
||||
assert.False(t, pubKey.VerifyBytes(msg, sig))
|
||||
}
|
||||
|
@@ -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()
|
||||
|
||||
// nameTable is used to map public key concrete types back
|
||||
// to their registered amino names. This should eventually be handled
|
||||
// by amino. Example usage:
|
||||
// nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName
|
||||
var nameTable = 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
|
||||
nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName
|
||||
nameTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoName
|
||||
nameTable[reflect.TypeOf(multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute
|
||||
}
|
||||
|
||||
// PubkeyAminoName returns the amino route of a pubkey
|
||||
// cdc is currently passed in, as eventually this will not be using
|
||||
// a package level codec.
|
||||
func PubkeyAminoName(cdc *amino.Codec, key crypto.PubKey) (string, bool) {
|
||||
route, found := nameTable[reflect.TypeOf(key)]
|
||||
return route, found
|
||||
}
|
||||
|
||||
// RegisterAmino registers all crypto related types in the given (amino) codec.
|
||||
@@ -24,21 +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.PubKeyAminoName, nil)
|
||||
cdc.RegisterConcrete(secp256k1.PubKeySecp256k1{},
|
||||
"tendermint/PubKeySecp256k1", nil)
|
||||
secp256k1.PubKeyAminoName, nil)
|
||||
cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{},
|
||||
multisig.PubKeyMultisigThresholdAminoRoute, nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.PrivKey)(nil), nil)
|
||||
cdc.RegisterConcrete(ed25519.PrivKeyEd25519{},
|
||||
"tendermint/PrivKeyEd25519", nil)
|
||||
ed25519.PrivKeyAminoName, nil)
|
||||
cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{},
|
||||
"tendermint/PrivKeySecp256k1", nil)
|
||||
|
||||
cdc.RegisterInterface((*crypto.Signature)(nil), nil)
|
||||
cdc.RegisterConcrete(ed25519.SignatureEd25519{},
|
||||
"tendermint/SignatureEd25519", nil)
|
||||
cdc.RegisterConcrete(secp256k1.SignatureSecp256k1{},
|
||||
"tendermint/SignatureSecp256k1", nil)
|
||||
secp256k1.PrivKeyAminoName, nil)
|
||||
}
|
||||
|
||||
func PrivKeyFromBytes(privKeyBytes []byte) (privKey crypto.PrivKey, err error) {
|
||||
@@ -50,8 +69,3 @@ func PubKeyFromBytes(pubKeyBytes []byte) (pubKey crypto.PubKey, err error) {
|
||||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey)
|
||||
return
|
||||
}
|
||||
|
||||
func SignatureFromBytes(pubKeyBytes []byte) (pubKey crypto.Signature, err error) {
|
||||
err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey)
|
||||
return
|
||||
}
|
||||
|
@@ -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"
|
||||
)
|
||||
|
||||
@@ -15,12 +16,14 @@ type byter interface {
|
||||
Bytes() []byte
|
||||
}
|
||||
|
||||
func checkAminoBinary(t *testing.T, src byter, dst interface{}, size int) {
|
||||
func checkAminoBinary(t *testing.T, src, dst interface{}, size int) {
|
||||
// Marshal to binary bytes.
|
||||
bz, err := cdc.MarshalBinaryBare(src)
|
||||
require.Nil(t, err, "%+v", err)
|
||||
// Make sure this is compatible with current (Bytes()) encoding.
|
||||
assert.Equal(t, src.Bytes(), bz, "Amino binary vs Bytes() mismatch")
|
||||
if byterSrc, ok := src.(byter); ok {
|
||||
// Make sure this is compatible with current (Bytes()) encoding.
|
||||
assert.Equal(t, byterSrc.Bytes(), bz, "Amino binary vs Bytes() mismatch")
|
||||
}
|
||||
// Make sure we have the expected length.
|
||||
if size != -1 {
|
||||
assert.Equal(t, size, len(bz), "Amino binary size mismatch")
|
||||
@@ -51,26 +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 | |
|
||||
//| SignatureEd25519 | tendermint/SignatureEd25519 | 0x2031EA53 | 0x40 | |
|
||||
//| SignatureSecp256k1 | tendermint/SignatureSecp256k1 | 0x7FC4A495 | variable | |
|
||||
}
|
||||
|
||||
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,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -84,13 +88,11 @@ func TestKeyEncodings(t *testing.T) {
|
||||
assert.EqualValues(t, tc.privKey, priv3, "tc #%d", tcIndex)
|
||||
|
||||
// Check (de/en)codings of Signatures.
|
||||
var sig1, sig2, sig3 crypto.Signature
|
||||
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)
|
||||
checkAminoJSON(t, sig1, &sig3, false) // TODO also check Prefix bytes.
|
||||
assert.EqualValues(t, sig1, sig3, "tc #%d", tcIndex)
|
||||
|
||||
// Check (de/en)codings of PubKeys.
|
||||
pubKey := tc.privKey.PubKey()
|
||||
@@ -105,7 +107,7 @@ func TestKeyEncodings(t *testing.T) {
|
||||
func TestNilEncodings(t *testing.T) {
|
||||
|
||||
// Check nil Signature.
|
||||
var a, b crypto.Signature
|
||||
var a, b []byte
|
||||
checkAminoJSON(t, &a, &b, true)
|
||||
assert.EqualValues(t, a, b)
|
||||
|
||||
@@ -118,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 TestPubkeyAminoName(t *testing.T) {
|
||||
tests := []struct {
|
||||
key crypto.PubKey
|
||||
want string
|
||||
found bool
|
||||
}{
|
||||
{ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoName, true},
|
||||
{secp256k1.PubKeySecp256k1{}, secp256k1.PubKeyAminoName, true},
|
||||
{multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, true},
|
||||
}
|
||||
for i, tc := range tests {
|
||||
got, found := PubkeyAminoName(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,106 +0,0 @@
|
||||
// Package hkdfchacha20poly1305 creates an AEAD using hkdf, chacha20, and poly1305
|
||||
// When sealing and opening, the hkdf is used to obtain the nonce and subkey for
|
||||
// chacha20. Other than the change for the how the subkey and nonce for chacha
|
||||
// are obtained, this is the same as chacha20poly1305
|
||||
package hkdfchacha20poly1305
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// Implements crypto.AEAD
|
||||
type hkdfchacha20poly1305 struct {
|
||||
key [KeySize]byte
|
||||
}
|
||||
|
||||
const (
|
||||
// KeySize is the size of the key used by this AEAD, in bytes.
|
||||
KeySize = 32
|
||||
// NonceSize is the size of the nonce used with this AEAD, in bytes.
|
||||
NonceSize = 24
|
||||
// TagSize is the size added from poly1305
|
||||
TagSize = 16
|
||||
// MaxPlaintextSize is the max size that can be passed into a single call of Seal
|
||||
MaxPlaintextSize = (1 << 38) - 64
|
||||
// MaxCiphertextSize is the max size that can be passed into a single call of Open,
|
||||
// this differs from plaintext size due to the tag
|
||||
MaxCiphertextSize = (1 << 38) - 48
|
||||
// HkdfInfo is the parameter used internally for Hkdf's info parameter.
|
||||
HkdfInfo = "TENDERMINT_SECRET_CONNECTION_FRAME_KEY_DERIVE"
|
||||
)
|
||||
|
||||
//New xChaChapoly1305 AEAD with 24 byte nonces
|
||||
func New(key []byte) (cipher.AEAD, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("chacha20poly1305: bad key length")
|
||||
}
|
||||
ret := new(hkdfchacha20poly1305)
|
||||
copy(ret.key[:], key)
|
||||
return ret, nil
|
||||
|
||||
}
|
||||
func (c *hkdfchacha20poly1305) NonceSize() int {
|
||||
return NonceSize
|
||||
}
|
||||
|
||||
func (c *hkdfchacha20poly1305) Overhead() int {
|
||||
return TagSize
|
||||
}
|
||||
|
||||
func (c *hkdfchacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
if len(nonce) != NonceSize {
|
||||
panic("hkdfchacha20poly1305: bad nonce length passed to Seal")
|
||||
}
|
||||
|
||||
if uint64(len(plaintext)) > MaxPlaintextSize {
|
||||
panic("hkdfchacha20poly1305: plaintext too large")
|
||||
}
|
||||
|
||||
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
|
||||
|
||||
aead, err := chacha20poly1305.New(subKey[:])
|
||||
if err != nil {
|
||||
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
|
||||
}
|
||||
|
||||
return aead.Seal(dst, chachaNonce[:], plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (c *hkdfchacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
if len(nonce) != NonceSize {
|
||||
return nil, errors.New("hkdfchacha20poly1305: bad nonce length passed to Open")
|
||||
}
|
||||
if uint64(len(ciphertext)) > MaxCiphertextSize {
|
||||
return nil, errors.New("hkdfchacha20poly1305: ciphertext too large")
|
||||
}
|
||||
|
||||
subKey, chachaNonce := getSubkeyAndChachaNonceFromHkdf(&c.key, &nonce)
|
||||
|
||||
aead, err := chacha20poly1305.New(subKey[:])
|
||||
if err != nil {
|
||||
panic("hkdfchacha20poly1305: failed to initialize chacha20poly1305")
|
||||
}
|
||||
|
||||
return aead.Open(dst, chachaNonce[:], ciphertext, additionalData)
|
||||
}
|
||||
|
||||
func getSubkeyAndChachaNonceFromHkdf(cKey *[32]byte, nonce *[]byte) (
|
||||
subKey [KeySize]byte, chachaNonce [chacha20poly1305.NonceSize]byte) {
|
||||
hash := sha256.New
|
||||
hkdf := hkdf.New(hash, (*cKey)[:], *nonce, []byte(HkdfInfo))
|
||||
_, err := io.ReadFull(hkdf, subKey[:])
|
||||
if err != nil {
|
||||
panic("hkdfchacha20poly1305: failed to read subkey from hkdf")
|
||||
}
|
||||
_, err = io.ReadFull(hkdf, chachaNonce[:])
|
||||
if err != nil {
|
||||
panic("hkdfchacha20poly1305: failed to read chachaNonce from hkdf")
|
||||
}
|
||||
return
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user