Unverified Commit 34c89f73 authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #852 from caopingcp/issue774_tendermint

tendermint add aggregate signature
parents 4b5cd0a0 b23f5c28
......@@ -23,6 +23,7 @@ require (
github.com/miguelmota/go-solidity-sha3 v0.1.0
github.com/mr-tron/base58 v1.1.3
github.com/pborman/uuid v1.2.0
github.com/phoreproject/bls v0.0.0-20200525203911-a88a5ae26844
github.com/pkg/errors v0.8.1
github.com/prometheus/client_golang v0.9.2 // indirect
github.com/prometheus/common v0.4.1
......
......@@ -79,6 +79,7 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/campoy/embedmd v1.0.0/go.mod h1:oxyr9RCiSXg0M3VJ3ks0UGfp98BpSSGr0kpiX3MzVl8=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
......@@ -128,6 +129,7 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlespiau/covertool v0.0.0-20180314162135-b0c4c6d0583a/go.mod h1:/eQMcW3eA1bzKx23ZYI2H3tXPdJB5JWYTHzoUPBvQY4=
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/dop251/goja v0.0.0-20200219165308-d1232e640a87/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
......@@ -192,6 +194,7 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/pprof v0.0.0-20190309163659-77426154d546/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
......@@ -219,6 +222,7 @@ github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOo
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
......@@ -430,6 +434,7 @@ github.com/minio/sha256-simd v0.1.1 h1:5QHSlgo3nt5yKOJrC7W8w7X+NFl8cMPZm96iu8kKU
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mmcloughlin/avo v0.0.0-20190318053554-7a0eb66183da/go.mod h1:lf5GMZxA5kz8dnCweJuER5Rmbx6dDu6qvw0fO3uYKK8=
github.com/mr-tron/base58 v1.1.0 h1:Y51FGVJ91WBqCEabAi5OPUz38eAx8DakuAm5svLcsfQ=
github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
github.com/mr-tron/base58 v1.1.1/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8=
......@@ -493,6 +498,8 @@ github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
github.com/phoreproject/bls v0.0.0-20200525203911-a88a5ae26844 h1:Yflyn+XFLEu7RPzxovgEVLP6Es8JLJrHqdXunpm2ak4=
github.com/phoreproject/bls v0.0.0-20200525203911-a88a5ae26844/go.mod h1:xHJKf2TLXUA39Dhv8k5QmQOxLsbrb1KeTS/3ERfLeqc=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
......@@ -574,6 +581,7 @@ github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef h1:wHSqTBrZW24CsNJDfeh9Ex6Pm0Rcpc7qrgKBiL44vF4=
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
......@@ -610,6 +618,8 @@ go.uber.org/multierr v1.2.0 h1:6I+W7f5VwC5SV9dNrZ3qXrDB9mD0dyGOi/ZJmYw03T4=
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/arch v0.0.0-20181203225421-5a4828bb7045/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8=
golang.org/x/arch v0.0.0-20190312162104-788fe5ffcd8c/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
......@@ -617,6 +627,7 @@ golang.org/x/crypto v0.0.0-20190103213133-ff983b9c42bc/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190225124518-7f87c0fbb88b/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
......@@ -641,6 +652,7 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190227160552-c95aed5357e7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190326090315-15845e8f865b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
......@@ -666,6 +678,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
......@@ -685,9 +698,11 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181130052023-1c3d964395ce/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190106171756-3ef68632349c/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
......@@ -739,3 +754,4 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
......@@ -43,6 +43,8 @@ dbPath="datadir/addrbook"
dbCache=4
grpcLogFile="grpc33.log"
[p2p.sub.dht]
channel=123
[rpc]
jrpcBindAddr="localhost:8801"
......@@ -73,6 +75,7 @@ powLimitBits = "0x1f2fffff"
[consensus.sub.tendermint]
genesis="14KEKbYtKKQm4wMthSK9J4La4nAiidGozt"
genesisAmount=100000000
genesisBlockTime=1514533394
timeoutTxAvail=1000
timeoutPropose=1000
......@@ -86,14 +89,21 @@ skipTimeoutCommit=false
createEmptyBlocks=true
createEmptyBlocksInterval=1
validatorNodes=["127.0.0.1:46656", "127.0.0.2:46656"]
fastSync=false
# Propose阶段是否预执行区块
preExec=false
# 签名算法,支持"secp256k1","ed25519","sm2","bls",默认为"ed25519"
signName="ed25519"
# 是否使用聚合签名,签名算法需支持该特性,比如"bls"
useAggregateSignature=false
[store]
name="mavl"
name="kvmvcc"
driver="leveldb"
dbPath="datadir/mavltree"
dbPath="datadir/kvmvcc"
dbCache=128
[store.sub.mavl]
[store.sub.kvmvcc]
enableMavlPrefix=false
enableMVCC=false
......@@ -126,3 +136,9 @@ signType="auth_ecdsa"
superManager=[
"14KEKbYtKKQm4wMthSK9J4La4nAiidGozt",
]
[metrics]
#是否使能发送metrics数据的发送
enableMetrics=false
#数据保存模式
dataEmitMode="influxdb"
......@@ -31,6 +31,7 @@ var (
ErrInvalidProposalSignature = errors.New("Error invalid proposal signature")
ErrInvalidProposalPOLRound = errors.New("Error invalid proposal POL round")
ErrAddingVote = errors.New("Error adding vote")
ErrAddingAggVote = errors.New("Error adding aggregate vote")
ErrVoteHeightMismatch = errors.New("Error vote height mismatch")
)
......@@ -85,6 +86,7 @@ type ConsensusState struct {
setProposal func(proposal *tmtypes.Proposal) error
broadcastChannel chan<- MsgInfo
unicastChannel chan<- MsgInfo
ourID ID
status uint32 // 0-stop, 1-start
quit chan struct{}
......@@ -131,6 +133,11 @@ func (cs *ConsensusState) SetBroadcastChannel(broadcastChannel chan<- MsgInfo) {
cs.broadcastChannel = broadcastChannel
}
// SetBroadcastChannel method
func (cs *ConsensusState) SetUnicastChannel(unicastChannel chan<- MsgInfo) {
cs.unicastChannel = unicastChannel
}
// IsRunning method
func (cs *ConsensusState) IsRunning() bool {
return atomic.LoadUint32(&cs.status) == 1
......@@ -162,6 +169,15 @@ func (cs *ConsensusState) GetValidators() (int64, []*ttypes.Validator) {
return cs.state.LastBlockHeight, cs.state.Validators.Copy().Validators
}
// GetPrivValidator returns the private validator account for signing votes.
func (cs *ConsensusState) GetPrivValidator() ttypes.PrivValidator {
cs.mtx.Lock()
defer cs.mtx.Unlock()
pv := cs.privValidator
return pv
}
// SetPrivValidator sets the private validator account for signing votes.
func (cs *ConsensusState) SetPrivValidator(priv ttypes.PrivValidator) {
cs.mtx.Lock()
......@@ -264,6 +280,13 @@ func (cs *ConsensusState) reconstructLastCommit(state State) {
panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
}
}
if seenCommit.AggVote != nil {
aggVote := &ttypes.AggVote{AggVote: seenCommit.AggVote}
added, err := lastPrecommits.AddAggVote(aggVote)
if !added || err != nil {
panic(fmt.Sprintf("Failed to reconstruct LastCommit: %v", err))
}
}
if !lastPrecommits.HasTwoThirdsMajority() {
panic("Failed to reconstruct LastCommit: Does not have +2/3 maj")
}
......@@ -429,6 +452,8 @@ func (cs *ConsensusState) handleMsg(mi MsgInfo) {
// TODO: If rs.Height == vote.Height && rs.Round < vote.Round,
// the peer is sending us CatchupCommit precommits.
// We could make note of this and help filter in broadcastHasVoteMessage().
case *tmtypes.AggVote:
err = cs.tryAddAggVote(msg, peerID)
default:
tendermintlog.Error("Unknown msg type", msg.String(), "peerid", peerID, "peerip", peerIP)
}
......@@ -486,7 +511,7 @@ func (cs *ConsensusState) checkTxsAvailable() {
}
cs.txsAvailable <- height
case <-cs.client.StopC():
tendermintlog.Debug("checkTxsAvailable exit")
tendermintlog.Info("checkTxsAvailable quit")
return
}
}
......@@ -589,7 +614,7 @@ func (cs *ConsensusState) proposalHeartbeat(height int64, round int) {
ValidatorIndex: int32(valIndex),
}
heartbeatMsg := &ttypes.Heartbeat{Heartbeat: heartbeat}
err := cs.privValidator.SignHeartbeat(chainID, heartbeatMsg)
err := cs.GetPrivValidator().SignHeartbeat(chainID, heartbeatMsg)
if err != nil {
tendermintlog.Error("SignHeartbeat failed", "err", err)
continue
......@@ -661,12 +686,22 @@ func (cs *ConsensusState) isProposer() bool {
return bytes.Equal(cs.Validators.GetProposer().Address, cs.privValidator.GetAddress())
}
func (cs *ConsensusState) getProposerID() ID {
pub := cs.Validators.GetProposer().PubKey
pubkey, err := ttypes.ConsensusCrypto.PubKeyFromBytes(pub)
if err != nil {
tendermintlog.Error("getProposerID fail", "err", err)
}
return GenIDByPubKey(pubkey)
}
func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
var block *ttypes.TendermintBlock
// Decide on block
if cs.ValidBlock != nil {
if preExec {
// If there is valid block, PreExec that.
pblockNew := cs.client.PreExecBlock(cs.ValidBlock.Data, false)
if pblockNew == nil {
......@@ -674,6 +709,7 @@ func (cs *ConsensusState) defaultDecideProposal(height int64, round int) {
return
}
cs.ValidBlock.Data = pblockNew
}
block = cs.ValidBlock
} else {
// Create a new proposal block from state/txs from the mempool.
......@@ -756,12 +792,14 @@ func (cs *ConsensusState) createProposalBlock() (block *ttypes.TendermintBlock)
}
block.Data.TxHash = merkle.CalcMerkleRoot(cfg, block.Data.Height, block.Data.Txs)
if preExec {
pblockNew := cs.client.PreExecBlock(block.Data, false)
if pblockNew == nil {
tendermintlog.Error("createProposalBlock PreExecBlock fail")
return nil
}
block.Data = pblockNew
}
return block
}
......@@ -800,6 +838,11 @@ func (cs *ConsensusState) enterPrevote(height int64, round int) {
cs.newStep()
}()
if useAggSig {
// Wait for some more prevotes; enterPrecommit
cs.scheduleTimeout(cs.Prevote(round)*2, height, round, ttypes.RoundStepPrevoteWait)
}
tendermintlog.Info(fmt.Sprintf("enterPrevote(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step), "cost", types.Since(cs.begCons))
// Sign and broadcast vote as necessary
......@@ -833,7 +876,7 @@ func (cs *ConsensusState) defaultDoPrevote(height int64, round int) {
return
}
if !cs.isProposer() {
if preExec && !cs.isProposer() {
// PreExec proposal block
blockCopy := *cs.ProposalBlock.Data
blockNew := cs.client.PreExecBlock(&blockCopy, true)
......@@ -898,6 +941,11 @@ func (cs *ConsensusState) enterPrecommit(height int64, round int) {
cs.newStep()
}()
if useAggSig {
// Wait for some more precommits; enterNewRound
cs.scheduleTimeout(cs.Precommit(round)*2, height, round, ttypes.RoundStepPrecommitWait)
}
blockID, ok := cs.Votes.Prevotes(round).TwoThirdsMajority()
// If we don't have a polka, we must precommit nil
......@@ -1010,7 +1058,7 @@ func (cs *ConsensusState) enterCommit(height int64, commitRound int) {
blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority()
if !ok {
panic("RunActionCommit() expects +2/3 precommits")
panic("enterCommit expects +2/3 precommits")
}
// The Locked* fields no longer matter.
......@@ -1114,6 +1162,10 @@ func (cs *ConsensusState) finalizeCommit(height int64) {
tendermintlog.Info(fmt.Sprintf("Not-Proposer reach consensus. Current: %v/%v/%v", cs.Height, cs.Round, cs.Step), "CommitRound", cs.CommitRound,
"tx-len", len(commitBlock.Txs), "cost", types.Since(cs.begCons), "proposer-addr", fmt.Sprintf("%X", ttypes.Fingerprint(block.TendermintBlock.Header.ProposerAddr)))
}
reqblock, err := cs.client.RequestBlock(height)
if err == nil {
stateCopy.LastResultsHash = reqblock.Hash(cs.client.GetAPI().GetConfig())
}
//check whether need update validator nodes
valNodes, err := cs.client.QueryValidatorsByHeight(block.Header.Height)
......@@ -1278,6 +1330,121 @@ func (cs *ConsensusState) addProposalBlock(proposalBlock *tmtypes.TendermintBloc
return nil
}
// Attempt to add aggregate vote.
func (cs *ConsensusState) tryAddAggVote(aggVoteRaw *tmtypes.AggVote, peerID string) error {
aggVote := &ttypes.AggVote{AggVote: aggVoteRaw}
if aggVote.Height != cs.Height {
tendermintlog.Debug("AggVote ignored and not added", "voteHeight", aggVote.Height, "csHeight", cs.Height, "peerID", peerID)
return ErrVoteHeightMismatch
}
if cs.begCons.IsZero() {
cs.begCons = time.Now()
}
height := cs.Height
_, err := cs.Votes.AddAggVote(aggVote, peerID)
if err != nil {
tendermintlog.Error("Error attempting to add aggregate vote", "err", err)
return ErrAddingAggVote
}
switch aggVote.Type {
case uint32(ttypes.VoteTypePrevote):
prevotes := cs.Votes.Prevotes(int(aggVote.Round))
tendermintlog.Info("Added aggregate prevote", "aggVote", aggVote, "prevotes", prevotes.StringShort())
// If +2/3 prevotes for a block or nil for *any* round:
if blockID, ok := prevotes.TwoThirdsMajority(); ok {
// There was a polka!
// If we're locked but this is a recent polka, unlock.
// If it matches our ProposalBlock, update the ValidBlock
// Unlock if `cs.LockedRound < vote.Round <= cs.Round`
// NOTE: If vote.Round > cs.Round, we'll deal with it when we get to vote.Round
if (cs.LockedBlock != nil) &&
(cs.LockedRound < int(aggVote.Round)) &&
(int(aggVote.Round) <= cs.Round) &&
!cs.LockedBlock.HashesTo(blockID.Hash) {
tendermintlog.Info("Unlocking because of POL.", "lockedRound", cs.LockedRound, "POLRound", aggVote.Round)
cs.LockedRound = -1
cs.LockedBlock = nil
}
// Update Valid* if we can.
// NOTE: our proposal block may be nil or not what received a polka..
if len(blockID.Hash) != 0 && (cs.ValidRound < int(aggVote.Round)) && (int(aggVote.Round) == cs.Round) {
if cs.ProposalBlock.HashesTo(blockID.Hash) {
tendermintlog.Info("Updating ValidBlock because of POL.", "validRound", cs.ValidRound, "POLRound", aggVote.Round)
cs.ValidRound = int(aggVote.Round)
cs.ValidBlock = cs.ProposalBlock
} else {
tendermintlog.Info(
"Valid block we don't know about. Set ProposalBlock=nil",
"proposal", fmt.Sprintf("%X", cs.ProposalBlock.Hash()), "blockId", fmt.Sprintf("%X", blockID.Hash))
// We're getting the wrong block.
cs.ProposalBlock = nil
}
cs.ProposalBlockHash = blockID.Hash
validBlockMsg := &tmtypes.ValidBlockMsg{
Height: aggVote.Height,
Round: aggVote.Round,
Blockhash: cs.ProposalBlockHash,
IsCommit: false,
}
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.ValidBlockID, Msg: validBlockMsg, PeerID: cs.ourID, PeerIP: ""}
}
}
// If +2/3 prevotes for *anything* for this or future round:
switch {
case cs.Round < int(aggVote.Round) && prevotes.HasTwoThirdsAny():
// Round-skip if there is any 2/3+ of votes ahead of us
cs.enterNewRound(height, int(aggVote.Round))
case cs.Round == int(aggVote.Round) && ttypes.RoundStepPrevote <= cs.Step: // current round
blockID, ok := prevotes.TwoThirdsMajority()
if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) {
cs.enterPrecommit(height, int(aggVote.Round))
} else if prevotes.HasTwoThirdsAny() {
cs.enterPrevoteWait(height, int(aggVote.Round))
}
case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == aggVote.Round:
// If the proposal is now complete, enter prevote of cs.Round.
if cs.isProposalComplete() {
cs.enterPrevote(height, cs.Round)
}
}
case uint32(ttypes.VoteTypePrecommit):
precommits := cs.Votes.Precommits(int(aggVote.Round))
tendermintlog.Info("Added aggregate precommit", "aggVote", aggVote, "precommits", precommits.StringShort())
blockID, ok := precommits.TwoThirdsMajority()
if ok {
// Executed as TwoThirdsMajority could be from a higher round
cs.enterNewRound(height, int(aggVote.Round))
cs.enterPrecommit(height, int(aggVote.Round))
if len(blockID.Hash) != 0 {
cs.enterCommit(height, int(aggVote.Round))
if skipTimeoutCommit && precommits.HasAll() {
cs.enterNewRound(cs.Height, 0)
}
} else {
cs.enterPrecommitWait(height, int(aggVote.Round))
}
} else if cs.Round <= int(aggVote.Round) && precommits.HasTwoThirdsAny() {
cs.enterNewRound(height, int(aggVote.Round))
cs.enterPrecommitWait(height, int(aggVote.Round))
}
default:
panic(fmt.Sprintf("Unexpected aggVote type %X", aggVote.Type)) // Should not happen.
}
return nil
}
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
func (cs *ConsensusState) tryAddVote(voteRaw *tmtypes.Vote, peerID string, peerIP string) error {
vote := &ttypes.Vote{Vote: voteRaw}
......@@ -1344,7 +1511,7 @@ func (cs *ConsensusState) addVote(vote *ttypes.Vote, peerID string, peerIP strin
// Not necessarily a bad peer, but not favourable behaviour.
if vote.Height != cs.Height {
err = ErrVoteHeightMismatch
tendermintlog.Info("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID)
tendermintlog.Debug("Vote ignored and not added", "voteHeight", vote.Height, "csHeight", cs.Height, "peerID", peerID)
return
}
......@@ -1426,6 +1593,16 @@ func (cs *ConsensusState) addVote(vote *ttypes.Vote, peerID string, peerIP strin
case cs.Round == int(vote.Round) && ttypes.RoundStepPrevote <= cs.Step: // current round
blockID, ok := prevotes.TwoThirdsMajority()
if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) {
if useAggSig && prevotes.GetAggVote() == nil {
err := prevotes.SetAggVote()
if err != nil {
tendermintlog.Error("prevotes SetAggVote fail", "err", err)
break
}
aggVoteMsg := prevotes.GetAggVote().AggVote
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.AggVoteID, Msg: aggVoteMsg, PeerID: cs.ourID, PeerIP: ""}
tendermintlog.Info("Send aggregate prevote", "aggVote", prevotes.GetAggVote())
}
cs.enterPrecommit(height, int(vote.Round))
} else if prevotes.HasTwoThirdsAny() {
cs.enterPrevoteWait(height, int(vote.Round))
......@@ -1447,6 +1624,16 @@ func (cs *ConsensusState) addVote(vote *ttypes.Vote, peerID string, peerIP strin
cs.enterNewRound(height, int(vote.Round))
cs.enterPrecommit(height, int(vote.Round))
if len(blockID.Hash) != 0 {
if useAggSig && precommits.GetAggVote() == nil {
err := precommits.SetAggVote()
if err != nil {
tendermintlog.Error("precommits SetAggVote fail", "err", err)
break
}
aggVoteMsg := precommits.GetAggVote().AggVote
cs.broadcastChannel <- MsgInfo{TypeID: ttypes.AggVoteID, Msg: aggVoteMsg, PeerID: cs.ourID, PeerIP: ""}
tendermintlog.Info("Send aggregate precommit", "aggVote", precommits.GetAggVote())
}
cs.enterCommit(height, int(vote.Round))
if skipTimeoutCommit && precommits.HasAll() {
cs.enterNewRound(cs.Height, 0)
......@@ -1467,7 +1654,6 @@ func (cs *ConsensusState) addVote(vote *ttypes.Vote, peerID string, peerIP strin
}
func (cs *ConsensusState) signVote(voteType byte, hash []byte) (*ttypes.Vote, error) {
addr := cs.privValidator.GetAddress()
valIndex, _ := cs.Validators.GetByAddress(addr)
tVote := &tmtypes.Vote{}
......@@ -1482,11 +1668,10 @@ func (cs *ConsensusState) signVote(voteType byte, hash []byte) (*ttypes.Vote, er
Type: uint32(voteType),
BlockID: &tmtypes.BlockID{Hash: hash},
Signature: nil,
UseAggSig: useAggSig,
},
}
beg := time.Now()
err := cs.privValidator.SignVote(cs.state.ChainID, vote)
tendermintlog.Debug("signVote", "height", cs.Height, "cost", types.Since(beg))
return vote, err
}
......@@ -1498,14 +1683,18 @@ func (cs *ConsensusState) signAddVote(voteType byte, hash []byte) *ttypes.Vote {
}
vote, err := cs.signVote(voteType, hash)
if err == nil {
// send to self
cs.sendInternalMessage(MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: cs.ourID, PeerIP: ""})
tendermintlog.Info("Signed and pushed vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
if useAggSig {
// send to proposer
cs.unicastChannel <- MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: cs.getProposerID(), PeerIP: ""}
}
tendermintlog.Info("Sign and send vote", "height", cs.Height, "round", cs.Round, "vote", vote)
return vote
}
tendermintlog.Error("Error signing vote", "height", cs.Height, "round", cs.Round, "vote", vote, "err", err)
return nil
}
// CompareHRS method
......@@ -1570,6 +1759,9 @@ func (cs *ConsensusState) PeerQueryMaj23Sleep() time.Duration {
// IsProposer method
func (cs *ConsensusState) IsProposer() bool {
cs.mtx.Lock()
defer cs.mtx.Unlock()
return cs.isProposer()
}
......
......@@ -25,6 +25,7 @@ const (
tryListenSeconds = 5
handshakeTimeout = 20 // * time.Second,
maxSendQueueSize = 1024
minSendQueueSize = 10
defaultSendTimeout = 60 * time.Second
//MaxMsgPacketPayloadSize define
MaxMsgPacketPayloadSize = 10 * 1024 * 1024
......@@ -57,11 +58,12 @@ func Parallel(tasks ...func()) {
wg.Wait()
}
// GenAddressByPubKey method
func GenAddressByPubKey(pubkey crypto.PubKey) []byte {
// GenIDByPubKey method
func GenIDByPubKey(pubkey crypto.PubKey) ID {
//must add 3 bytes ahead to make compatibly
typeAddr := append([]byte{byte(0x01), byte(0x01), byte(0x20)}, pubkey.Bytes()...)
return crypto.Ripemd160(typeAddr)
typeAddr := append([]byte{byte(0x01), byte(0x01), byte(0x20)}, pubkey.Bytes()[:32]...)
address := crypto.Ripemd160(typeAddr)
return ID(hex.EncodeToString(address))
}
// IP2IPPort struct
......@@ -129,6 +131,7 @@ type Node struct {
state *ConsensusState
broadcastChannel chan MsgInfo
unicastChannel chan MsgInfo
started uint32 // atomic
stopped uint32 // atomic
quit chan struct{}
......@@ -136,8 +139,6 @@ type Node struct {
// NewNode method
func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivKey, network string, version string, state *ConsensusState) *Node {
address := GenAddressByPubKey(privKey.PubKey())
node := &Node{
peerSet: NewPeerSet(),
seeds: seeds,
......@@ -148,16 +149,18 @@ func NewNode(seeds []string, protocol string, lAddr string, privKey crypto.PrivK
privKey: privKey,
Network: network,
Version: version,
ID: ID(hex.EncodeToString(address)),
ID: GenIDByPubKey(privKey.PubKey()),
dialing: NewMutexMap(),
reconnecting: NewMutexMap(),
broadcastChannel: make(chan MsgInfo, maxSendQueueSize),
unicastChannel: make(chan MsgInfo, minSendQueueSize),
state: state,
localIPs: make(map[string]net.IP),
}
state.SetOurID(node.ID)
state.SetBroadcastChannel(node.broadcastChannel)
state.SetUnicastChannel(node.unicastChannel)
localIPs := getNaiveExternalAddress(true)
if len(localIPs) > 0 {
......@@ -179,7 +182,7 @@ func (node *Node) Start() {
if err == nil {
break
} else if i < tryListenSeconds-1 {
time.Sleep(time.Second * 1)
time.Sleep(time.Second)
}
}
if err != nil {
......@@ -213,6 +216,7 @@ func (node *Node) Start() {
go node.StartConsensusRoutine()
go node.BroadcastRoutine()
go node.UnicastRoutine()
}
}
......@@ -305,13 +309,33 @@ func (node *Node) BroadcastRoutine() {
for {
msg, ok := <-node.broadcastChannel
if !ok {
tendermintlog.Debug("broadcastChannel closed")
tendermintlog.Info("broadcastChannel closed")
return
}
node.Broadcast(msg)
}
}
// BroadcastRoutine receive to broadcast
func (node *Node) UnicastRoutine() {
for {
msg, ok := <-node.unicastChannel
if !ok {
tendermintlog.Info("unicastChannel closed")
return
}
for _, peer := range node.peerSet.List() {
if peer.ID() == msg.PeerID {
success := peer.Send(msg)
if !success {
tendermintlog.Error("send failure in UnicastRoutine")
}
break
}
}
}
}
func (node *Node) connectComming(inConn net.Conn) {
maxPeers := maxNumPeers
if maxPeers <= node.peerSet.Size() {
......@@ -640,7 +664,7 @@ func dial(addr string) (net.Conn, error) {
func newOutboundPeerConn(addr string, ourNodePrivKey crypto.PrivKey, onPeerError func(Peer, interface{}), state *ConsensusState) (*peerConn, error) {
conn, err := dial(addr)
if err != nil {
return &peerConn{}, fmt.Errorf("Error creating peer:%v", err)
return &peerConn{}, fmt.Errorf("newOutboundPeerConn dial fail:%v", err)
}
pc, err := newPeerConn(conn, true, true, ourNodePrivKey, onPeerError, state)
......@@ -684,7 +708,7 @@ func newPeerConn(
// Encrypt connection
conn, err = MakeSecretConnection(conn, ourNodePrivKey)
if err != nil {
return pc, fmt.Errorf("Error creating peer:%v", err)
return pc, fmt.Errorf("MakeSecretConnection fail:%v", err)
}
// Only the information we already have
......
......@@ -54,17 +54,19 @@ func TestParallel(t *testing.T) {
assert.Equal(t, 6, sum)
}
func TestGenAddressByPubKey(t *testing.T) {
func TestGenIDByPubKey(t *testing.T) {
tmp, err := hex.DecodeString(privKey)
assert.Nil(t, err)
priv, err := secureConnCrypto.PrivKeyFromBytes(tmp)
assert.Nil(t, err)
addr := GenAddressByPubKey(priv.PubKey())
id := GenIDByPubKey(priv.PubKey())
addr, err := hex.DecodeString(string(id))
assert.Nil(t, err)
strAddr := fmt.Sprintf("%X", addr)
assert.Equal(t, expectAddress, strAddr)
fmt.Println("TestGenAddressByPubKey ok")
fmt.Println("TestGenIDByPubKey ok")
}
func TestIP2IPPort(t *testing.T) {
......@@ -154,7 +156,6 @@ func testUpdateStateRoutine(t *testing.T, pc *peerConn) {
},
}
ps := pc.state
pc.waitQuit.Add(1)
go pc.updateStateRoutine()
//NewRoundStepID msg
......@@ -249,7 +250,6 @@ func testUpdateStateRoutine(t *testing.T, pc *peerConn) {
assert.NotNil(t, ps.getVoteBitArray(3, 2, ttypes.VoteTypePrecommit))
pc.quitUpdate <- struct{}{}
pc.waitQuit.Wait()
fmt.Println("testUpdateStateRoutine ok")
}
......@@ -8,13 +8,11 @@ import (
"bufio"
"bytes"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net"
"reflect"
"runtime/debug"
"sync"
"sync/atomic"
"time"
......@@ -56,8 +54,6 @@ type Peer interface {
Stop()
SetTransferChannel(chan MsgInfo)
//Set(string, interface{})
//Get(string) interface{}
}
// PeerConnState struct
......@@ -85,10 +81,8 @@ type peerConn struct {
started uint32 //atomic
stopped uint32 // atomic
quitSend chan struct{}
quitUpdate chan struct{}
quitBeat chan struct{}
waitQuit sync.WaitGroup
transferChannel chan MsgInfo
......@@ -222,8 +216,7 @@ func (pc *peerConn) ID() ID {
if len(pc.id) != 0 {
return pc.id
}
address := GenAddressByPubKey(pc.conn.(*SecretConnection).RemotePubKey())
pc.id = ID(hex.EncodeToString(address))
pc.id = GenIDByPubKey(pc.conn.(*SecretConnection).RemotePubKey())
return pc.id
}
......@@ -265,6 +258,11 @@ func (pc *peerConn) SetTransferChannel(transferChannel chan MsgInfo) {
pc.transferChannel = transferChannel
}
func (pc *peerConn) String() string {
return fmt.Sprintf("PeerConn{outbound:%v persistent:%v ip:%s id:%s started:%v stopped:%v}",
pc.outbound, pc.persistent, pc.ip.String(), pc.id, pc.started, pc.stopped)
}
func (pc *peerConn) CloseConn() {
err := pc.conn.Close() // nolint: errcheck
if err != nil {
......@@ -358,7 +356,7 @@ func (pc *peerConn) Send(msg MsgInfo) bool {
atomic.AddInt32(&pc.sendQueueSize, 1)
return true
case <-time.After(defaultSendTimeout):
tendermintlog.Error("send msg timeout", "peerip", msg.PeerIP, "msg", msg.Msg)
tendermintlog.Error("send msg timeout", "peerip", msg.PeerIP, "msg", msg)
return false
}
}
......@@ -379,9 +377,25 @@ func (pc *peerConn) TrySend(msg MsgInfo) bool {
// PickSendVote picks a vote and sends it to the peer.
// Returns true if vote was sent.
func (pc *peerConn) PickSendVote(votes ttypes.VoteSetReader) bool {
if vote, ok := pc.state.PickVoteToSend(votes); ok {
if useAggSig {
if pc.state.AggPrecommit {
time.Sleep(pc.myState.PeerGossipSleep())
return false
}
aggVote := votes.GetAggVote()
if aggVote != nil {
msg := MsgInfo{TypeID: ttypes.AggVoteID, Msg: aggVote.AggVote, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug("Sending aggregate vote message", "msg", msg)
if pc.Send(msg) {
pc.state.SetHasAggPrecommit(aggVote)
time.Sleep(pc.myState.PeerGossipSleep())
return true
}
}
return false
} else if vote, ok := pc.state.PickVoteToSend(votes); ok {
msg := MsgInfo{TypeID: ttypes.VoteID, Msg: vote.Vote, PeerID: pc.id, PeerIP: pc.ip.String()}
tendermintlog.Debug("Sending vote message", "vote", msg)
tendermintlog.Debug("Sending vote message", "msg", msg)
if pc.Send(msg) {
pc.state.SetHasVote(vote)
return true
......@@ -406,7 +420,6 @@ func (pc *peerConn) Start() error {
pc.pongChannel = make(chan struct{})
pc.sendQueue = make(chan MsgInfo, maxSendQueueSize)
pc.sendBuffer = make([]byte, 0, MaxMsgPacketPayloadSize)
pc.quitSend = make(chan struct{})
pc.quitUpdate = make(chan struct{})
pc.quitBeat = make(chan struct{})
pc.state = &PeerConnState{ip: pc.ip, PeerRoundState: ttypes.PeerRoundState{
......@@ -417,7 +430,6 @@ func (pc *peerConn) Start() error {
}}
pc.updateStateQueue = make(chan MsgInfo, maxSendQueueSize)
pc.heartbeatQueue = make(chan proto.Message, 100)
pc.waitQuit.Add(5) //heartbeatRoutine, updateStateRoutine,gossipDataRoutine,gossipVotesRoutine,queryMaj23Routine
go pc.sendRoutine()
go pc.recvRoutine()
......@@ -434,27 +446,8 @@ func (pc *peerConn) Start() error {
func (pc *peerConn) Stop() {
if atomic.CompareAndSwapUint32(&pc.stopped, 0, 1) {
pc.quitSend <- struct{}{}
pc.quitUpdate <- struct{}{}
pc.quitBeat <- struct{}{}
pc.waitQuit.Wait()
tendermintlog.Info("peerConn stop waitQuit", "peerIP", pc.ip.String())
close(pc.sendQueue)
pc.sendQueue = nil
pc.transferChannel = nil
pc.CloseConn()
tendermintlog.Info("peerConn stop finish", "peerIP", pc.ip.String())
}
}
// Catch panics, usually caused by remote disconnects.
func (pc *peerConn) _recover() {
if r := recover(); r != nil {
stack := debug.Stack()
err := StackError{r, stack}
pc.stopForError(err)
tendermintlog.Info("peerConn close connection", "peerIP", pc.ip.String())
}
}
......@@ -468,12 +461,9 @@ func (pc *peerConn) stopForError(r interface{}) {
}
func (pc *peerConn) sendRoutine() {
defer pc._recover()
FOR_LOOP:
for {
select {
case <-pc.quitSend:
break FOR_LOOP
case msg := <-pc.sendQueue:
bytes, err := proto.Marshal(msg.Msg)
if err != nil {
......@@ -525,7 +515,6 @@ FOR_LOOP:
}
func (pc *peerConn) recvRoutine() {
defer pc._recover()
FOR_LOOP:
for {
//typeID+msgLen+msg
......@@ -563,7 +552,7 @@ FOR_LOOP:
continue
}
if pc.transferChannel != nil && (pkt.TypeID == ttypes.ProposalID || pkt.TypeID == ttypes.VoteID ||
pkt.TypeID == ttypes.ProposalBlockID) {
pkt.TypeID == ttypes.ProposalBlockID || pkt.TypeID == ttypes.AggVoteID) {
pc.transferChannel <- MsgInfo{pkt.TypeID, realMsg.(proto.Message), pc.ID(), pc.ip.String()}
if pkt.TypeID == ttypes.ProposalID {
proposal := realMsg.(*tmtypes.Proposal)
......@@ -577,6 +566,9 @@ FOR_LOOP:
block := &ttypes.TendermintBlock{TendermintBlock: realMsg.(*tmtypes.TendermintBlock)}
tendermintlog.Debug("Receiving proposal block", "block-height", block.Header.Height, "peerip", pc.ip.String())
pc.state.SetHasProposalBlock(block)
} else if pkt.TypeID == ttypes.AggVoteID {
aggVote := &ttypes.AggVote{AggVote: realMsg.(*tmtypes.AggVote)}
tendermintlog.Debug("Receiving aggregate vote", "aggVote-height", aggVote.Height, "peerip", pc.ip.String())
}
} else if pkt.TypeID == ttypes.ProposalHeartbeatID {
pc.heartbeatQueue <- realMsg.(*tmtypes.Heartbeat)
......@@ -591,10 +583,8 @@ FOR_LOOP:
}
}
}
close(pc.pongChannel)
close(pc.heartbeatQueue)
close(pc.updateStateQueue)
pc.quitUpdate <- struct{}{}
pc.quitBeat <- struct{}{}
tendermintlog.Info("peerConn stop recvRoutine", "peerIP", pc.ip.String())
}
......@@ -603,7 +593,6 @@ FOR_LOOP:
for {
select {
case <-pc.quitUpdate:
pc.waitQuit.Done()
break FOR_LOOP
case msg := <-pc.updateStateQueue:
typeID := msg.TypeID
......@@ -662,6 +651,7 @@ FOR_LOOP:
}
}
}
close(pc.updateStateQueue)
tendermintlog.Info("peerConn stop updateStateRoutine", "peerIP", pc.ip.String())
}
......@@ -670,15 +660,17 @@ FOR_LOOP:
for {
select {
case <-pc.quitBeat:
pc.waitQuit.Done()
break FOR_LOOP
case heartbeat := <-pc.heartbeatQueue:
msg := heartbeat.(*tmtypes.Heartbeat)
msg, ok := heartbeat.(*tmtypes.Heartbeat)
if ok {
tendermintlog.Debug("Received proposal heartbeat message",
"height", msg.Height, "round", msg.Round, "sequence", msg.Sequence,
"valIdx", msg.ValidatorIndex, "valAddr", msg.ValidatorAddress)
}
}
}
close(pc.heartbeatQueue)
tendermintlog.Info("peerConn stop heartbeatRoutine", "peerIP", pc.ip.String())
}
......@@ -687,7 +679,6 @@ OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if !pc.IsRunning() {
pc.waitQuit.Done()
tendermintlog.Info("peerConn stop gossipDataRoutine", "peerIP", pc.ip.String())
return
}
......@@ -789,7 +780,6 @@ OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if !pc.IsRunning() {
pc.waitQuit.Done()
tendermintlog.Info("peerConn stop gossipVotesRoutine", "peerIP", pc.ip.String())
return
}
......@@ -806,7 +796,7 @@ OUTER_LOOP:
// If height matches, then send LastCommit, Prevotes, Precommits.
if rs.Height == prs.Height {
if pc.gossipVotesForHeight(rs, &prs.PeerRoundState) {
if !useAggSig && pc.gossipVotesForHeight(rs, &prs.PeerRoundState) {
continue OUTER_LOOP
}
}
......@@ -914,7 +904,6 @@ OUTER_LOOP:
for {
// Manage disconnects from self or peer.
if !pc.IsRunning() {
pc.waitQuit.Done()
tendermintlog.Info("peerConn stop queryMaj23Routine", "peerIP", pc.ip.String())
return
}
......@@ -1003,20 +992,6 @@ OUTER_LOOP:
}
}
// StackError struct
type StackError struct {
Err interface{}
Stack []byte
}
func (se StackError) String() string {
return fmt.Sprintf("Error: %v\nStack: %s", se.Err, se.Stack)
}
func (se StackError) Error() string {
return se.String()
}
// GetRoundState returns an atomic snapshot of the PeerRoundState.
// There's no point in mutating it since it won't change PeerState.
func (ps *PeerConnState) GetRoundState() *ttypes.PeerRoundState {
......@@ -1073,6 +1048,23 @@ func (ps *PeerConnState) SetHasProposalBlock(block *ttypes.TendermintBlock) {
ps.ProposalBlock = true
}
// SetHasAggPrecommit sets the given aggregate precommit as known for the peer.
func (ps *PeerConnState) SetHasAggPrecommit(aggVote *ttypes.AggVote) {
ps.mtx.Lock()
defer ps.mtx.Unlock()
if ps.Height != aggVote.Height || ps.Round != int(aggVote.Round) {
return
}
if ps.AggPrecommit {
return
}
tendermintlog.Debug("Peer set aggregate precommit", "peerip", ps.ip.String(),
"peer-state", fmt.Sprintf("%v/%v/%v", ps.Height, ps.Round, ps.Step),
"aggVote(H/R)", fmt.Sprintf("%v/%v", aggVote.Height, aggVote.Round))
ps.AggPrecommit = true
}
// PickVoteToSend picks a vote to send to the peer.
// Returns true if a vote was picked.
// NOTE: `votes` must be the correct Size() for the Height().
......@@ -1266,6 +1258,7 @@ func (ps *PeerConnState) ApplyNewRoundStepMessage(msg *tmtypes.NewRoundStepMsg)
// We'll update the BitArray capacity later.
ps.Prevotes = nil
ps.Precommits = nil
ps.AggPrecommit = false
}
if psHeight == msg.Height && psRound != int(msg.Round) && int(msg.Round) == psCatchupCommitRound {
// Peer caught up to CatchupCommitRound.
......
......@@ -16,12 +16,13 @@ import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/plugin/plugin/consensus/tendermint/types"
ttypes "github.com/33cn/plugin/plugin/consensus/tendermint/types"
"golang.org/x/crypto/nacl/box"
"golang.org/x/crypto/nacl/secretbox"
"golang.org/x/crypto/ripemd160"
......@@ -33,7 +34,6 @@ const (
dataMaxSize = 1024
totalFrameSize = dataMaxSize + dataLenSize
sealedFrameSize = totalFrameSize + secretbox.Overhead
authSigMsgSize = (32) + (64)
) // fixed size (length prefixed) byte arrays
// SecretConnection Implements net.Conn
......@@ -62,7 +62,7 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*
// (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
remEphPub, err := shareEphPubKey(conn, locEphPub)
if err != nil {
return nil, err
return nil, fmt.Errorf("shareEphPubKey: %v", err)
}
// Compute common shared secret.
......@@ -96,7 +96,7 @@ func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey crypto.PrivKey) (*
// Share (in secret) each other's pubkey & challenge signature
authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
if err != nil {
return nil, err
return nil, fmt.Errorf("shareAuthSignature: %v", err)
}
remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
if !remPubKey.VerifyBytes(challenge[:], remSignature) {
......@@ -205,7 +205,7 @@ func genEphKeys() (ephPub, ephPriv *[32]byte) {
var err error
ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
if err != nil {
types.PanicCrisis("Could not generate ephemeral keypairs")
ttypes.PanicCrisis("Could not generate ephemeral keypairs")
}
return
}
......@@ -282,26 +282,28 @@ type authSigMessage struct {
func shareAuthSignature(sc io.ReadWriter, pubKey crypto.PubKey, signature crypto.Signature) (*authSigMessage, error) {
var recvMsg authSigMessage
var err1, err2 error
pubLen := len(pubKey.Bytes())
sigLen := len(signature.Bytes())
Parallel(
func() {
msgByte := make([]byte, len(pubKey.Bytes())+len(signature.Bytes()))
copy(msgByte, pubKey.Bytes())
copy(msgByte[len(pubKey.Bytes()):], signature.Bytes())
msgByte := make([]byte, pubLen+sigLen)
copy(msgByte, pubKey.Bytes()[:pubLen])
copy(msgByte[pubLen:], signature.Bytes())
_, err1 = sc.Write(msgByte)
},
func() {
readBuffer := make([]byte, authSigMsgSize)
readBuffer := make([]byte, pubLen+sigLen)
_, err2 = io.ReadFull(sc, readBuffer)
if err2 != nil {
return
}
recvMsg.Key, err2 = types.ConsensusCrypto.PubKeyFromBytes(readBuffer[:32])
recvMsg.Key, err2 = ttypes.ConsensusCrypto.PubKeyFromBytes(readBuffer[:pubLen])
if err2 != nil {
return
}
recvMsg.Sig, err2 = types.ConsensusCrypto.SignatureFromBytes(readBuffer[32:])
recvMsg.Sig, err2 = ttypes.ConsensusCrypto.SignatureFromBytes(readBuffer[pubLen:])
if err2 != nil {
return
}
......
......@@ -385,7 +385,7 @@ func getprivkey(key string) crypto.PrivKey {
if err != nil {
panic(err)
}
priv, err := cr.PrivKeyFromBytes(bkey)
priv, err := cr.PrivKeyFromBytes(bkey[:32])
if err != nil {
panic(err)
}
......
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package tendermint
import (
......
......@@ -24,11 +24,14 @@ import (
"github.com/golang/protobuf/proto"
)
const tendermintVersion = "0.1.0"
const (
tendermintVersion = "0.1.0"
)
var (
tendermintlog = log15.New("module", "tendermint")
genesis string
genesisAmount int64 = 1e8
genesisBlockTime int64
timeoutTxAvail int32 = 1000
timeoutPropose int32 = 3000 // millisecond
......@@ -41,12 +44,15 @@ var (
skipTimeoutCommit = false
createEmptyBlocks = false
fastSync = false
preExec = false
createEmptyBlocksInterval int32 // second
validatorNodes = []string{"127.0.0.1:46656"}
peerGossipSleepDuration int32 = 100
peerGossipSleepDuration int32 = 200
peerQueryMaj23SleepDuration int32 = 2000
zeroHash [32]byte
random *rand.Rand
signName = "ed25519"
useAggSig = false
)
func init() {
......@@ -65,7 +71,6 @@ type Client struct {
pubKey string
csState *ConsensusState
csStore *ConsensusStore // save consensus state
crypto crypto.Crypto
node *Node
txsAvailable chan int64
stopC chan struct{}
......@@ -73,6 +78,7 @@ type Client struct {
type subConfig struct {
Genesis string `json:"genesis"`
GenesisAmount int64 `json:"genesisAmount"`
GenesisBlockTime int64 `json:"genesisBlockTime"`
TimeoutTxAvail int32 `json:"timeoutTxAvail"`
TimeoutPropose int32 `json:"timeoutPropose"`
......@@ -87,9 +93,12 @@ type subConfig struct {
CreateEmptyBlocksInterval int32 `json:"createEmptyBlocksInterval"`
ValidatorNodes []string `json:"validatorNodes"`
FastSync bool `json:"fastSync"`
PreExec bool `json:"preExec"`
SignName string `json:"signName"`
UseAggregateSignature bool `json:"useAggregateSignature"`
}
func (client *Client) applyConfig(sub []byte) {
func applyConfig(sub []byte) {
var subcfg subConfig
if sub != nil {
types.MustDecode(sub, &subcfg)
......@@ -97,6 +106,9 @@ func (client *Client) applyConfig(sub []byte) {
if subcfg.Genesis != "" {
genesis = subcfg.Genesis
}
if subcfg.GenesisAmount > 0 {
genesisAmount = subcfg.GenesisAmount
}
if subcfg.GenesisBlockTime > 0 {
genesisBlockTime = subcfg.GenesisBlockTime
}
......@@ -133,6 +145,11 @@ func (client *Client) applyConfig(sub []byte) {
validatorNodes = subcfg.ValidatorNodes
}
fastSync = subcfg.FastSync
preExec = subcfg.PreExec
if subcfg.SignName != "" {
signName = subcfg.SignName
}
useAggSig = subcfg.UseAggregateSignature
}
// DefaultDBProvider returns a database using the DBBackend and DBDir
......@@ -144,37 +161,47 @@ func DefaultDBProvider(name string) dbm.DB {
// New ...
func New(cfg *types.Consensus, sub []byte) queue.Module {
tendermintlog.Info("Start to create tendermint client")
applyConfig(sub)
//init rand
ttypes.Init()
genDoc, err := ttypes.GenesisDocFromFile("genesis.json")
if err != nil {
tendermintlog.Error("NewTendermintClient", "msg", "GenesisDocFromFile failded", "error", err)
signType, ok := ttypes.SignMap[signName]
if !ok {
tendermintlog.Error("Invalid sign name")
return nil
}
cr, err := crypto.New(types.GetSignName("", types.ED25519))
ttypes.CryptoName = types.GetSignName("", signType)
cr, err := crypto.New(ttypes.CryptoName)
if err != nil {
tendermintlog.Error("NewTendermintClient", "err", err)
return nil
}
ttypes.ConsensusCrypto = cr
priv, err := cr.GenKey()
if useAggSig {
_, err = crypto.ToAggregate(ttypes.ConsensusCrypto)
if err != nil {
tendermintlog.Error("NewTendermintClient", "GenKey err", err)
tendermintlog.Error("ConsensusCrypto not support aggregate signature", "name", ttypes.CryptoName)
return nil
}
}
genDoc, err := ttypes.GenesisDocFromFile("genesis.json")
if err != nil {
tendermintlog.Error("NewTendermintClient", "msg", "GenesisDocFromFile fail", "error", err)
return nil
}
privValidator := ttypes.LoadOrGenPrivValidatorFS("priv_validator.json")
if privValidator == nil {
tendermintlog.Error("NewTendermintClient create priv_validator file failed")
tendermintlog.Error("NewTendermintClient create priv_validator file fail")
return nil
}
ttypes.InitMessageMap()
priv := privValidator.PrivKey
pubkey := privValidator.GetPubKey().KeyString()
c := drivers.NewBaseClient(cfg)
client := &Client{
......@@ -184,21 +211,13 @@ func New(cfg *types.Consensus, sub []byte) queue.Module {
privKey: priv,
pubKey: pubkey,
csStore: NewConsensusStore(),
crypto: cr,
txsAvailable: make(chan int64, 1),
stopC: make(chan struct{}, 1),
}
c.SetChild(client)
client.applyConfig(sub)
return client
}
// PrivValidator returns the Node's PrivValidator.
func (client *Client) PrivValidator() ttypes.PrivValidator {
return client.privValidator
}
// GenesisDoc returns the Node's GenesisDoc.
func (client *Client) GenesisDoc() *ttypes.GenesisDoc {
return client.genesisDoc
......@@ -216,6 +235,7 @@ func (client *Client) GenesisState() *State {
// Close TODO:may need optimize
func (client *Client) Close() {
client.BaseClient.Close()
client.node.Stop()
client.stopC <- struct{}{}
tendermintlog.Info("consensus tendermint closed")
......@@ -335,7 +355,7 @@ func (client *Client) CreateGenesisTx() (ret []*types.Transaction) {
//gen payload
g := &cty.CoinsAction_Genesis{}
g.Genesis = &types.AssetsGenesis{}
g.Genesis.Amount = 1e8 * types.Coin
g.Genesis.Amount = genesisAmount * types.Coin
tx.Payload = types.Encode(&cty.CoinsAction{Value: g, Ty: cty.CoinsActionGenesis})
ret = append(ret, &tx)
return
......@@ -405,10 +425,13 @@ func (client *Client) ProcEvent(msg *queue.Message) bool {
// CreateBlock a routine monitor whether some transactions available and tell client by available channel
func (client *Client) CreateBlock() {
issleep := true
for {
if client.IsClosed() {
tendermintlog.Info("CreateBlock quit")
break
}
if !client.csState.IsRunning() {
tendermintlog.Error("consensus not running now")
tendermintlog.Info("consensus not running")
time.Sleep(time.Second)
continue
}
......@@ -483,7 +506,6 @@ func (client *Client) BuildBlock() *types.Block {
client.AddTxsToBlock(&newblock, txs)
//固定难度
newblock.Difficulty = cfg.GetP(0).PowLimitBits
//newblock.TxHash = merkle.CalcMerkleRoot(newblock.Txs)
newblock.BlockTime = types.Now().Unix()
if lastBlock.BlockTime >= newblock.BlockTime {
newblock.BlockTime = lastBlock.BlockTime + 1
......@@ -498,6 +520,9 @@ func (client *Client) CommitBlock(block *types.Block) error {
if retErr != nil {
tendermintlog.Info("CommitBlock fail", "err", retErr)
if client.WaitBlock(block.Height) {
if !preExec {
return nil
}
curBlock, err := client.RequestBlock(block.Height)
if err == nil {
if bytes.Equal(curBlock.Hash(cfg), block.Hash(cfg)) {
......@@ -636,7 +661,7 @@ func (client *Client) Query_NodeInfo(req *types.ReqNil) (types.Message, error) {
return &tmtypes.ValidatorSet{Validators: validators, Proposer: &tmtypes.Validator{}}, nil
}
//比较newBlock是不是最优区块
// CmpBestBlock 比较newBlock是不是最优区块
func (client *Client) CmpBestBlock(newBlock *types.Block, cmpBlock *types.Block) bool {
return false
}
......@@ -272,13 +272,12 @@ func CheckState(t *testing.T, client *Client) {
assert.Equal(t, client.csState.Prevote(0), 1000*time.Millisecond)
assert.Equal(t, client.csState.Precommit(0), 1000*time.Millisecond)
assert.Equal(t, client.csState.PeerGossipSleep(), 100*time.Millisecond)
assert.Equal(t, client.csState.PeerGossipSleep(), 200*time.Millisecond)
assert.Equal(t, client.csState.PeerQueryMaj23Sleep(), 2000*time.Millisecond)
assert.Equal(t, client.csState.IsProposer(), true)
assert.Nil(t, client.csState.GetPrevotesState(state.LastBlockHeight, 0, nil))
assert.Nil(t, client.csState.GetPrecommitsState(state.LastBlockHeight, 0, nil))
assert.NotEmpty(t, client.PrivValidator())
assert.NotEmpty(t, client.csState.GetPrivValidator())
assert.Len(t, client.GenesisDoc().Validators, 1)
msg1, err := client.Query_IsHealthy(&types.ReqNil{})
......
......@@ -139,7 +139,7 @@ func Put(ip string, size string, privkey string) {
fmt.Fprintln(os.Stderr, err)
return
}
url := "http://" + ip + ":9801"
url := "http://" + ip + ":8801"
if privkey == "" {
_, priv := genaddress()
privkey = common.ToHex(priv.Bytes())
......
......@@ -13,18 +13,11 @@ import (
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/common/merkle"
"github.com/33cn/chain33/types"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
)
var (
blocklog = log15.New("module", "tendermint-block")
// ConsensusCrypto define
ConsensusCrypto crypto.Crypto
)
// BlockID struct
type BlockID struct {
tmtypes.BlockID
......@@ -88,11 +81,6 @@ func (b *TendermintBlock) ValidateBasic() error {
return errors.New("Zero Header.Height")
}
newTxs := int64(len(b.Data.Txs))
if b.Header.NumTxs != newTxs {
return fmt.Errorf("Wrong Header.NumTxs. Expected %v, got %v", newTxs, b.Header.NumTxs)
}
if b.Header.TotalTxs < 0 {
return errors.New("Negative Header.TotalTxs")
}
......@@ -192,7 +180,7 @@ func (h *Header) Hash() []byte {
}
bytes, err := json.Marshal(h)
if err != nil {
blocklog.Error("block header Hash() marshal failed", "error", err)
ttlog.Error("block header Hash() marshal failed", "error", err)
return nil
}
return crypto.Ripemd160(bytes)
......@@ -254,6 +242,9 @@ func (commit *Commit) FirstPrecommit() *tmtypes.Vote {
// Height returns the height of the commit
func (commit *Commit) Height() int64 {
if commit.AggVote != nil {
return commit.AggVote.Height
}
if len(commit.Precommits) == 0 {
return 0
}
......@@ -262,6 +253,9 @@ func (commit *Commit) Height() int64 {
// Round returns the round of the commit
func (commit *Commit) Round() int {
if commit.AggVote != nil {
return int(commit.AggVote.Round)
}
if len(commit.Precommits) == 0 {
return 0
}
......@@ -283,6 +277,10 @@ func (commit *Commit) Size() int {
// BitArray returns a BitArray of which validators voted in this commit
func (commit *Commit) BitArray() *BitArray {
if commit.AggVote != nil {
bitArray := &BitArray{TendermintBitArray: commit.AggVote.ValidatorArray}
return bitArray.copy()
}
if commit.bitArray == nil {
commit.bitArray = NewBitArray(len(commit.Precommits))
for i, precommit := range commit.Precommits {
......@@ -301,7 +299,16 @@ func (commit *Commit) GetByIndex(index int) *Vote {
// IsCommit returns true if there is at least one vote
func (commit *Commit) IsCommit() bool {
return len(commit.Precommits) != 0
return len(commit.Precommits) != 0 || commit.AggVote != nil
}
// GetAggVote ...
func (commit *Commit) GetAggVote() *AggVote {
if commit == nil {
return nil
}
aggVote := &AggVote{commit.AggVote}
return aggVote.Copy()
}
// ValidateBasic performs basic validation that doesn't involve state data.
......@@ -338,12 +345,28 @@ func (commit *Commit) ValidateBasic() error {
round, precommit.Round)
}
}
// validate the aggVote
if commit.AggVote != nil {
if commit.AggVote.Type != uint32(VoteTypePrecommit) {
return fmt.Errorf("Invalid aggVote type. Expected Precommit, got %v", commit.AggVote.Type)
}
if commit.AggVote.Height != height {
return fmt.Errorf("Invalid aggVote height. Expected %v, got %v", height, commit.AggVote.Height)
}
if int(commit.AggVote.Round) != round {
return fmt.Errorf("Invalid aggVote round. Expected %v, got %v", round, commit.AggVote.Round)
}
}
return nil
}
// Hash returns the hash of the commit
func (commit *Commit) Hash() []byte {
if commit.hash == nil {
if commit.AggVote != nil {
aggVote := &AggVote{AggVote: commit.AggVote}
commit.hash = aggVote.Hash()
} else {
bs := make([][]byte, len(commit.Precommits))
for i, item := range commit.Precommits {
precommit := Vote{Vote: item}
......@@ -351,6 +374,7 @@ func (commit *Commit) Hash() []byte {
}
commit.hash = merkle.GetMerkleRoot(bs)
}
}
return commit.hash
}
......@@ -366,9 +390,11 @@ func (commit *Commit) StringIndented(indent string) string {
return Fmt(`Commit{
%s BlockID: %v
%s Precommits: %v
%s AggVote: %v
%s}#%v`,
indent, commit.BlockID,
indent, strings.Join(precommitStrings, "\n"+indent+" "),
indent, commit.AggVote.String(),
indent, commit.hash)
}
......
......@@ -4,7 +4,17 @@
package types
import "errors"
import (
"errors"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
)
const (
AuthBLS = 259
)
var (
// ErrHeightLessThanOne error type
......@@ -18,3 +28,17 @@ var (
// ErrLastBlockID error type
ErrLastBlockID = errors.New("ErrLastBlockID")
)
var (
ttlog = log15.New("module", "tendermint-types")
// ConsensusCrypto define
ConsensusCrypto crypto.Crypto
CryptoName string
// SignMap define sign type
SignMap = map[string]int{
"secp256k1": types.SECP256K1,
"ed25519": types.ED25519,
"sm2": types.SM2,
"bls": AuthBLS,
}
)
......@@ -138,6 +138,30 @@ func (hvs *HeightVoteSet) AddVote(vote *Vote, peerID string) (added bool, err er
return
}
// AddAggVote Duplicate votes return added=false, err=nil.
// By convention, peerKey is "" if origin is self.
func (hvs *HeightVoteSet) AddAggVote(vote *AggVote, peerID string) (added bool, err error) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if !IsVoteTypeValid(byte(vote.Type)) {
return
}
round := int(vote.Round)
voteSet := hvs.getVoteSet(round, byte(vote.Type))
if voteSet == nil {
if rndz := hvs.peerCatchupRounds[peerID]; len(rndz) < 2 {
hvs.addRound(int(vote.Round))
voteSet = hvs.getVoteSet(round, byte(vote.Type))
hvs.peerCatchupRounds[peerID] = append(rndz, round)
} else {
err = errors.New("Peer has sent a aggregate vote that does not match our round for more than one round")
return
}
}
added, err = voteSet.AddAggVote(vote)
return
}
// Prevotes ...
func (hvs *HeightVoteSet) Prevotes(round int) *VoteSet {
hvs.mtx.Lock()
......
......@@ -113,7 +113,7 @@ func (params *ConsensusParams) Validate() error {
func (params *ConsensusParams) Hash() []byte {
bytes, err := json.Marshal(params)
if err != nil {
blocklog.Error("block header Hash() marshal failed", "error", err)
ttlog.Error("block header Hash() marshal failed", "error", err)
return nil
}
return crypto.Ripemd160(bytes)
......
......@@ -6,6 +6,7 @@ package types
import (
"bytes"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
......@@ -14,8 +15,6 @@ import (
"sync"
"time"
"encoding/hex"
"github.com/33cn/chain33/common/crypto"
)
......@@ -296,15 +295,15 @@ func (pv *PrivValidatorImp) save() {
LastStep: pv.LastStep,
LastSignature: nil,
}
privValFS.PrivKey = KeyText{Kind: "ed25519", Data: Fmt("%X", pv.PrivKey.Bytes()[:])}
privValFS.PubKey = KeyText{Kind: "ed25519", Data: pv.PubKey.KeyString()}
privValFS.PrivKey = KeyText{Kind: CryptoName, Data: Fmt("%X", pv.PrivKey.Bytes()[:])}
privValFS.PubKey = KeyText{Kind: CryptoName, Data: pv.PubKey.KeyString()}
if len(pv.LastSignBytes) != 0 {
tmp := Fmt("%X", pv.LastSignBytes[:])
privValFS.LastSignBytes = tmp
}
if pv.LastSignature != nil {
sig := Fmt("%X", pv.LastSignature.Bytes()[:])
privValFS.LastSignature = &KeyText{Kind: "ed25519", Data: sig}
privValFS.LastSignature = &KeyText{Kind: CryptoName, Data: sig}
}
jsonBytes, err := json.Marshal(privValFS)
if err != nil {
......
......@@ -42,6 +42,7 @@ const (
ProposalHeartbeatID = byte(0x08)
ProposalBlockID = byte(0x09)
ValidBlockID = byte(0x0a)
AggVoteID = byte(0x0b)
PacketTypePing = byte(0xff)
PacketTypePong = byte(0xfe)
......@@ -60,6 +61,7 @@ func InitMessageMap() {
ProposalHeartbeatID: reflect.TypeOf(tmtypes.Heartbeat{}),
ProposalBlockID: reflect.TypeOf(tmtypes.TendermintBlock{}),
ValidBlockID: reflect.TypeOf(tmtypes.ValidBlockMsg{}),
AggVoteID: reflect.TypeOf(tmtypes.AggVote{}),
}
}
......@@ -186,6 +188,7 @@ type PeerRoundState struct {
LastCommit *BitArray // All commit precommits of commit for last height.
CatchupCommitRound int // Round that we have commit for. Not necessarily unique. -1 if none.
CatchupCommit *BitArray // All commit precommits peer has for this height & CatchupCommitRound
AggPrecommit bool // True if peer has aggregate precommit for this round
}
// String returns a string representation of the PeerRoundState
......@@ -204,17 +207,19 @@ func (prs PeerRoundState) StringIndented(indent string) string {
%s Prevotes %v
%s Precommits %v
%s LastCommit %v (round %v)
%s Catchup %v (round %v)
%s CatchupCommit %v (round %v)
%s AggPrecommit %v
%s}`,
indent, prs.Height, prs.Round, prs.Step, prs.StartTime,
indent, prs.Proposal,
indent, prs.ProposalBlock,
indent, prs.ProposalBlock,
indent, prs.ProposalBlockHash,
indent, prs.ProposalPOL, prs.ProposalPOLRound,
indent, prs.Prevotes,
indent, prs.Precommits,
indent, prs.LastCommit, prs.LastCommitRound,
indent, prs.CatchupCommit, prs.CatchupCommitRound,
indent, prs.AggPrecommit,
indent)
}
......@@ -274,6 +279,12 @@ type CanonicalJSONOnceVote struct {
Vote CanonicalJSONVote `json:"vote"`
}
// CanonicalJSONOnceAggVote ...
type CanonicalJSONOnceAggVote struct {
ChainID string `json:"chain_id"`
AggVote CanonicalJSONVote `json:"agg_vote"`
}
// CanonicalJSONOnceHeartbeat ...
type CanonicalJSONOnceHeartbeat struct {
ChainID string `json:"chain_id"`
......@@ -305,11 +316,26 @@ func CanonicalProposal(proposal *Proposal) CanonicalJSONProposal {
// CanonicalVote ...
func CanonicalVote(vote *Vote) CanonicalJSONVote {
timestamp := ""
if !vote.UseAggSig {
timestamp = CanonicalTime(time.Unix(0, vote.Timestamp))
}
return CanonicalJSONVote{
BlockID: CanonicalJSONBlockID{Hash: vote.BlockID.Hash},
Height: vote.Height,
Round: int(vote.Round),
Timestamp: timestamp,
Type: byte(vote.Type),
}
}
// CanonicalAggVote ...
func CanonicalAggVote(vote *AggVote) CanonicalJSONVote {
return CanonicalJSONVote{
BlockID: CanonicalJSONBlockID{Hash: vote.BlockID.Hash},
Height: vote.Height,
Round: int(vote.Round),
Timestamp: CanonicalTime(time.Unix(0, vote.Timestamp)),
Timestamp: "",
Type: byte(vote.Type),
}
}
......
......@@ -13,7 +13,6 @@ import (
"time"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
)
......@@ -27,7 +26,7 @@ var (
ErrVoteNonDeterministicSignature = errors.New("Non-deterministic signature")
ErrVoteConflict = errors.New("Conflicting vote")
ErrVoteNil = errors.New("Nil vote")
votelog = log15.New("module", "tendermint-vote")
ErrAggVoteNil = errors.New("Nil aggregate vote")
)
// Signable is an interface for all signable things.
......@@ -156,7 +155,7 @@ func (vote *Vote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error
byteVote, e := json.Marshal(&canonical)
if e != nil {
*err = e
votelog.Error("vote WriteSignBytes marshal failed", "err", e)
ttlog.Error("vote WriteSignBytes marshal failed", "err", e)
return
}
number, writeErr := w.Write(byteVote)
......@@ -200,7 +199,7 @@ func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
sig, err := ConsensusCrypto.SignatureFromBytes(vote.Signature)
if err != nil {
votelog.Error("vote Verify failed", "err", err)
ttlog.Error("vote Verify fail", "err", err)
return err
}
......@@ -217,7 +216,109 @@ func (vote *Vote) Hash() []byte {
}
bytes, err := json.Marshal(vote)
if err != nil {
votelog.Error("vote hash marshal failed", "err", err)
ttlog.Error("vote hash marshal failed", "err", err)
return nil
}
return crypto.Ripemd160(bytes)
}
// AggVote Represents a prevote, precommit, or commit vote from validators for consensus.
type AggVote struct {
*tmtypes.AggVote
}
// WriteSignBytes ...
func (aggVote *AggVote) WriteSignBytes(chainID string, w io.Writer, n *int, err *error) {
if *err != nil {
return
}
canonical := CanonicalJSONOnceAggVote{
chainID,
CanonicalAggVote(aggVote),
}
byteVote, e := json.Marshal(&canonical)
if e != nil {
*err = e
ttlog.Error("aggVote WriteSignBytes marshal failed", "err", e)
return
}
number, writeErr := w.Write(byteVote)
*n = number
*err = writeErr
}
// Verify ...
func (aggVote *AggVote) Verify(chainID string, valSet *ValidatorSet) error {
aggSig, err := ConsensusCrypto.SignatureFromBytes(aggVote.Signature)
if err != nil {
return errors.New("invalid aggregate signature")
}
pubs := make([]crypto.PubKey, 0)
arr := &BitArray{TendermintBitArray: aggVote.ValidatorArray}
for i, val := range valSet.Validators {
if arr.GetIndex(i) {
pub, _ := ConsensusCrypto.PubKeyFromBytes(val.PubKey)
pubs = append(pubs, pub)
}
}
origVote := &Vote{&tmtypes.Vote{
BlockID: aggVote.BlockID,
Height: aggVote.Height,
Round: aggVote.Round,
Timestamp: aggVote.Timestamp,
Type: aggVote.Type,
UseAggSig: true,
}}
aggr, err := crypto.ToAggregate(ConsensusCrypto)
if err != nil {
return err
}
err = aggr.VerifyAggregatedOne(pubs, SignBytes(chainID, origVote), aggSig)
if err != nil {
ttlog.Error("aggVote Verify fail", "err", err, "aggVote", aggVote, "aggSig", aggSig)
return err
}
return nil
}
// Copy ...
func (aggVote *AggVote) Copy() *AggVote {
copy := *aggVote
return &copy
}
func (aggVote *AggVote) String() string {
if aggVote == nil {
return "nil-AggVote"
}
var typeString string
switch byte(aggVote.Type) {
case VoteTypePrevote:
typeString = "Prevote"
case VoteTypePrecommit:
typeString = "Precommit"
default:
PanicSanity("Unknown vote type")
}
bitArray := &BitArray{TendermintBitArray: aggVote.ValidatorArray}
return fmt.Sprintf("AggVote{%X %v/%02d/%v(%v) %X %X @ %s %v}",
Fingerprint(aggVote.ValidatorAddress),
aggVote.Height, aggVote.Round, aggVote.Type, typeString,
Fingerprint(aggVote.BlockID.Hash), aggVote.Signature,
CanonicalTime(time.Unix(0, aggVote.Timestamp)),
bitArray)
}
// Hash ...
func (aggVote *AggVote) Hash() []byte {
if aggVote == nil {
return nil
}
bytes, err := json.Marshal(aggVote)
if err != nil {
ttlog.Error("aggVote hash marshal failed", "err", err)
return nil
}
......
......@@ -12,13 +12,10 @@ import (
"strings"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/common/merkle"
"github.com/pkg/errors"
)
var validatorsetlog = log15.New("module", "tendermint-val")
// Validator ...
type Validator struct {
Address []byte `json:"address"`
......@@ -313,7 +310,7 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
if valSet.Size() != len(commit.Precommits) {
return fmt.Errorf("Invalid commit -- wrong set size: %v vs %v", valSet.Size(), len(commit.Precommits))
}
validatorsetlog.Debug("VerifyCommit will get commit height", "height", commit.Height())
ttlog.Debug("VerifyCommit will get commit height", "height", commit.Height())
commitHeight := commit.Height()
if height != commitHeight {
return fmt.Errorf("VerifyCommit 1 Invalid commit -- wrong height: %v vs %v", height, commitHeight)
......@@ -322,6 +319,29 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
talliedVotingPower := int64(0)
round := commit.Round()
if commit.AggVote != nil {
aggVote := &AggVote{AggVote: commit.AggVote}
// Make sure the step matches
if (aggVote.Height != height) ||
(int(aggVote.Round) != round) ||
(aggVote.Type != uint32(VoteTypePrecommit)) {
return errors.Wrapf(ErrVoteUnexpectedStep, "Got %d/%d/%d, expected %d/%d/%d",
height, round, VoteTypePrecommit,
aggVote.Height, aggVote.Round, aggVote.Type)
}
// Check signature
err := aggVote.Verify(chainID, valSet)
if err != nil {
return err
}
// calc voting power
arr := &BitArray{TendermintBitArray: aggVote.ValidatorArray}
for i, val := range valSet.Validators {
if arr.GetIndex(i) {
talliedVotingPower += val.VotingPower
}
}
} else {
for idx, item := range commit.Precommits {
// may be nil if validator skipped.
if item == nil || len(item.Signature) == 0 {
......@@ -338,8 +358,8 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
return fmt.Errorf("Invalid commit -- not precommit @ index %v", idx)
}
_, val := valSet.GetByIndex(idx)
// Validate signature
// Validate signature
precommitSignBytes := SignBytes(chainID, precommit)
sig, err := ConsensusCrypto.SignatureFromBytes(precommit.Signature)
if err != nil {
......@@ -358,6 +378,7 @@ func (valSet *ValidatorSet) VerifyCommit(chainID string, blockID BlockID, height
// Good precommit!
talliedVotingPower += val.VotingPower
}
}
if talliedVotingPower > valSet.TotalVotingPower()*2/3 {
return nil
......
......@@ -8,7 +8,9 @@ import (
"bytes"
"strings"
"sync"
"time"
"github.com/33cn/chain33/common/crypto"
tmtypes "github.com/33cn/plugin/plugin/dapp/valnode/types"
"github.com/pkg/errors"
)
......@@ -63,6 +65,7 @@ type VoteSet struct {
maj23 *tmtypes.BlockID // First 2/3 majority seen
votesByBlock map[string]*blockVotes // string(blockHash|blockParts) -> blockVotes
peerMaj23s map[string]*tmtypes.BlockID // Maj23 for each peer
aggVote *AggVote // aggregate vote
}
// NewVoteSet Constructs a new VoteSet struct used to accumulate votes for given height/round.
......@@ -82,6 +85,7 @@ func NewVoteSet(chainID string, height int64, round int, voteType byte, valSet *
maj23: nil,
votesByBlock: make(map[string]*blockVotes, valSet.Size()),
peerMaj23s: make(map[string]*tmtypes.BlockID),
aggVote: nil,
}
}
......@@ -133,7 +137,7 @@ func (voteSet *VoteSet) Size() int {
// NOTE: Vote must not be nil
func (voteSet *VoteSet) AddVote(vote *Vote) (added bool, err error) {
if voteSet == nil {
PanicSanity("AddVote() on nil VoteSet")
return false, errors.New("nil vote set")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
......@@ -291,6 +295,146 @@ func (voteSet *VoteSet) addVerifiedVote(vote *Vote, blockKey string, votingPower
return true, conflicting
}
// AddAggVote Returns added=true if aggVote is valid and new
func (voteSet *VoteSet) AddAggVote(vote *AggVote) (bool, error) {
if voteSet == nil {
return false, errors.New("nil vote set")
}
if vote == nil {
return false, ErrAggVoteNil
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
valAddr := vote.ValidatorAddress
valset := voteSet.valSet
if len(valAddr) == 0 {
return false, errors.Wrap(ErrVoteInvalidValidatorAddress, "Empty address")
}
// Make sure the step matches
if (vote.Height != voteSet.height) ||
(int(vote.Round) != voteSet.round) ||
(vote.Type != uint32(voteSet.voteType)) {
return false, errors.Wrapf(ErrVoteUnexpectedStep, "Got %d/%d/%d, expected %d/%d/%d",
voteSet.height, voteSet.round, voteSet.voteType,
vote.Height, vote.Round, vote.Type)
}
// Ensure that signer is proposer
propAddr := valset.Proposer.Address
if !bytes.Equal(valAddr, propAddr) {
return false, errors.Wrapf(ErrVoteInvalidValidatorAddress,
"aggVote.ValidatorAddress (%X) does not match proposer address (%X)",
valAddr, propAddr)
}
// If we already know of this vote, return false
if voteSet.aggVote != nil {
if bytes.Equal(voteSet.aggVote.Signature, vote.Signature) {
return false, nil // duplicate
}
return false, errors.Wrapf(ErrVoteNonDeterministicSignature, "Existing vote: %v; New vote: %v", voteSet.aggVote, vote)
}
// Check signature
err := vote.Verify(voteSet.chainID, voteSet.valSet)
if err != nil {
return false, err
}
// Check maj32
sum := int64(0)
arr := &BitArray{TendermintBitArray: vote.ValidatorArray}
for i, val := range valset.Validators {
if arr.GetIndex(i) {
sum += val.VotingPower
}
}
quorum := voteSet.valSet.TotalVotingPower()*2/3 + 1
if sum < quorum {
return false, errors.New("less than 2/3 total power")
}
voteSet.votesBitArray = arr.copy()
voteSet.aggVote = vote
voteSet.maj23 = vote.BlockID
voteSet.sum = sum
votesByBlock := newBlockVotes(false, voteSet.valSet.Size())
votesByBlock.bitArray = arr.copy()
votesByBlock.sum = sum
voteSet.votesByBlock[string(voteSet.maj23.Hash)] = votesByBlock
return true, nil
}
// SetAggVote generate aggregate vote when voteSet have 2/3 majority
func (voteSet *VoteSet) SetAggVote() error {
if voteSet == nil {
return errors.New("nil vote set")
}
if voteSet.maj23 == nil {
return errors.New("no 2/3 majority in voteSet")
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
blockKey := string(voteSet.maj23.Hash)
votesByBlock, ok := voteSet.votesByBlock[blockKey]
if !ok {
return errors.New("no 2/3 majority blockKey")
}
bitArray := votesByBlock.bitArray.copy()
sigs := make([]crypto.Signature, 0)
for _, vote := range votesByBlock.votes {
if vote != nil {
sig, err := ConsensusCrypto.SignatureFromBytes(vote.Signature)
if err != nil {
return errors.New("invalid aggregate signature")
}
sigs = append(sigs, sig)
}
}
aggr, err := crypto.ToAggregate(ConsensusCrypto)
if err != nil {
return err
}
aggSig, err := aggr.Aggregate(sigs)
if err != nil {
return err
}
aggVote := &AggVote{&tmtypes.AggVote{
ValidatorAddress: voteSet.valSet.Proposer.Address,
ValidatorArray: bitArray.TendermintBitArray,
Height: voteSet.height,
Round: int32(voteSet.round),
Timestamp: time.Now().UnixNano(),
Type: uint32(voteSet.voteType),
BlockID: voteSet.maj23,
Signature: aggSig.Bytes(),
}}
// Verify aggVote
err = aggVote.Verify(voteSet.chainID, voteSet.valSet)
if err != nil {
return err
}
voteSet.aggVote = aggVote
return nil
}
// GetAggVote ...
func (voteSet *VoteSet) GetAggVote() *AggVote {
if voteSet == nil {
return nil
}
voteSet.mtx.Lock()
defer voteSet.mtx.Unlock()
if voteSet.aggVote == nil {
return nil
}
return voteSet.aggVote.Copy()
}
// SetPeerMaj23 If a peer claims that it has 2/3 majority for given blockKey, call this.
// NOTE: if there are too many peers, or too much peer churn,
// this can cause memory issues.
......@@ -446,12 +590,12 @@ func (voteSet *VoteSet) StringIndented(indent string) string {
}
}
return Fmt(`VoteSet{
%s H:%v R:%v T:%v
%s H:%v R:%v T:%v +2/3:%X
%s %v
%s %v
%s %v
%s}`,
indent, voteSet.height, voteSet.round, voteSet.voteType,
indent, voteSet.height, voteSet.round, voteSet.voteType, voteSet.maj23,
indent, strings.Join(voteStrings, "\n"+indent+" "),
indent, voteSet.votesBitArray,
indent, voteSet.peerMaj23s,
......@@ -491,10 +635,15 @@ func (voteSet *VoteSet) MakeCommit() *tmtypes.TendermintCommit {
votesCopy[i] = &tmtypes.Vote{}
}
}
//copy(votesCopy, voteSet.votes)
var aggVote *tmtypes.AggVote
if voteSet.aggVote != nil {
copy := voteSet.aggVote.Copy()
aggVote = copy.AggVote
}
return &tmtypes.TendermintCommit{
BlockID: voteSet.maj23,
Precommits: votesCopy,
AggVote: aggVote,
}
}
......@@ -549,4 +698,5 @@ type VoteSetReader interface {
BitArray() *BitArray
GetByIndex(int) *Vote
IsCommit() bool
GetAggVote() *AggVote
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bls
import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"github.com/33cn/chain33/common/crypto"
"github.com/phoreproject/bls/g1pubs"
)
const (
BLSPrivateKeyLength = 32
BLSPublicKeyLength = 48
BLSSignatureLength = 96
)
// Driver driver
type Driver struct{}
// GenKey create private key
func (d Driver) GenKey() (crypto.PrivKey, error) {
privKeyBytes := new([BLSPrivateKeyLength]byte)
priv, err := g1pubs.RandKey(rand.Reader)
if err != nil {
return nil, err
}
privBytes := priv.Serialize()
copy(privKeyBytes[:], privBytes[:])
return PrivKeyBLS(*privKeyBytes), nil
}
// PrivKeyFromBytes create private key from bytes
func (d Driver) PrivKeyFromBytes(b []byte) (privKey crypto.PrivKey, err error) {
if len(b) != BLSPrivateKeyLength {
return nil, errors.New("invalid bls priv key byte")
}
privKeyBytes := new([BLSPrivateKeyLength]byte)
copy(privKeyBytes[:], b[:BLSPrivateKeyLength])
priv := g1pubs.DeserializeSecretKey(*privKeyBytes)
if priv == nil {
return nil, errors.New("invalid bls privkey")
}
privBytes := priv.Serialize()
copy(privKeyBytes[:], privBytes[:])
return PrivKeyBLS(*privKeyBytes), nil
}
// PubKeyFromBytes create public key from bytes
func (d Driver) PubKeyFromBytes(b []byte) (pubKey crypto.PubKey, err error) {
if len(b) != BLSPublicKeyLength {
return nil, errors.New("invalid bls pub key byte")
}
pubKeyBytes := new([BLSPublicKeyLength]byte)
copy(pubKeyBytes[:], b[:])
return PubKeyBLS(*pubKeyBytes), nil
}
// SignatureFromBytes create signature from bytes
func (d Driver) SignatureFromBytes(b []byte) (sig crypto.Signature, err error) {
sigBytes := new([BLSSignatureLength]byte)
copy(sigBytes[:], b[:])
return SignatureBLS(*sigBytes), nil
}
//Aggregate aggregates signatures together into a new signature.
func (d Driver) Aggregate(sigs []crypto.Signature) (crypto.Signature, error) {
if len(sigs) == 0 {
return nil, errors.New("no signatures to aggregate")
}
g1sigs := make([]*g1pubs.Signature, 0, len(sigs))
for i, sig := range sigs {
g1sig, err := ConvertToSignature(sig)
if err != nil {
return nil, fmt.Errorf("%v(index: %d)", err, i)
}
g1sigs = append(g1sigs, g1sig)
}
agsig := g1pubs.AggregateSignatures(g1sigs)
return SignatureBLS(agsig.Serialize()), nil
}
//AggregatePublic aggregates public keys together into a new PublicKey.
func (d Driver) AggregatePublic(pubs []crypto.PubKey) (crypto.PubKey, error) {
if len(pubs) == 0 {
return nil, errors.New("no public keys to aggregate")
}
//blank public key
g1pubs := g1pubs.NewAggregatePubkey()
for i, pub := range pubs {
g1pub, err := ConvertToPublicKey(pub)
if err != nil {
return nil, fmt.Errorf("%v(index: %d)", err, i)
}
g1pubs.Aggregate(g1pub)
}
return PubKeyBLS(g1pubs.Serialize()), nil
}
// VerifyAggregatedOne verifies each public key against a message.
func (d Driver) VerifyAggregatedOne(pubs []crypto.PubKey, m []byte, sig crypto.Signature) error {
g1pubs := make([]*g1pubs.PublicKey, 0, len(pubs))
for i, pub := range pubs {
g1pub, err := ConvertToPublicKey(pub)
if err != nil {
return fmt.Errorf("%v(index: %d)", err, i)
}
g1pubs = append(g1pubs, g1pub)
}
g1sig, err := ConvertToSignature(sig)
if err != nil {
return err
}
if g1sig.VerifyAggregateCommon(g1pubs, m) {
return nil
}
return errors.New("bls signature mismatch")
}
// VerifyAggregatedN verifies each public key against each message.
func (d Driver) VerifyAggregatedN(pubs []crypto.PubKey, ms [][]byte, sig crypto.Signature) error {
g1pubs := make([]*g1pubs.PublicKey, 0, len(pubs))
for i, pub := range pubs {
g1pub, err := ConvertToPublicKey(pub)
if err != nil {
return fmt.Errorf("%v(index: %d)", err, i)
}
g1pubs = append(g1pubs, g1pub)
}
g1sig, err := ConvertToSignature(sig)
if err != nil {
return err
}
if len(g1pubs) != len(ms) {
return fmt.Errorf("different length of pubs and messages, %d vs %d", len(g1pubs), len(ms))
}
if g1sig.VerifyAggregate(g1pubs, ms) {
return nil
}
return errors.New("bls signature mismatch")
}
// ConvertToSignature convert to BLS Signature
func ConvertToSignature(sig crypto.Signature) (*g1pubs.Signature, error) {
// unwrap if needed
if wrap, ok := sig.(SignatureS); ok {
sig = wrap.Signature
}
sigBLS, ok := sig.(SignatureBLS)
if !ok {
return nil, errors.New("invalid bls signature")
}
g1sig, err := g1pubs.DeserializeSignature(sigBLS)
if err != nil {
return nil, err
}
return g1sig, nil
}
// ConvertToPublicKey convert to BLS PublicKey
func ConvertToPublicKey(pub crypto.PubKey) (*g1pubs.PublicKey, error) {
pubBLS, ok := pub.(PubKeyBLS)
if !ok {
return nil, errors.New("invalid bls public key")
}
g1pub, err := g1pubs.DeserializePublicKey(pubBLS)
if err != nil {
return nil, err
}
return g1pub, nil
}
// PrivKeyBLS PrivKey
type PrivKeyBLS [BLSPrivateKeyLength]byte
// Bytes convert to bytes
func (privKey PrivKeyBLS) Bytes() []byte {
s := make([]byte, BLSPrivateKeyLength)
copy(s, privKey[:])
return s
}
// Sign create signature
func (privKey PrivKeyBLS) Sign(msg []byte) crypto.Signature {
priv := g1pubs.DeserializeSecretKey(privKey)
sig := g1pubs.Sign(msg, priv)
return SignatureBLS(sig.Serialize())
}
// PubKey convert to public key
func (privKey PrivKeyBLS) PubKey() crypto.PubKey {
priv := g1pubs.DeserializeSecretKey(privKey)
return PubKeyBLS(g1pubs.PrivToPub(priv).Serialize())
}
// Equals check privkey is equal
func (privKey PrivKeyBLS) Equals(other crypto.PrivKey) bool {
if otherSecp, ok := other.(PrivKeyBLS); ok {
return bytes.Equal(privKey[:], otherSecp[:])
}
return false
}
// String convert to string
func (privKey PrivKeyBLS) String() string {
return fmt.Sprintf("PrivKeyBLS{*****}")
}
// PubKeyBLS PubKey
type PubKeyBLS [BLSPublicKeyLength]byte
// Bytes convert to bytes
func (pubKey PubKeyBLS) Bytes() []byte {
s := make([]byte, BLSPublicKeyLength)
copy(s, pubKey[:])
return s
}
// VerifyBytes verify signature
func (pubKey PubKeyBLS) VerifyBytes(msg []byte, sig crypto.Signature) bool {
pub, err := g1pubs.DeserializePublicKey(pubKey)
if err != nil {
fmt.Println("invalid bls pubkey")
return false
}
g1sig, err := ConvertToSignature(sig)
if err != nil {
fmt.Println("ConvertToSignature fail:", err)
return false
}
return g1pubs.Verify(msg, pub, g1sig)
}
// String convert to string
func (pubKey PubKeyBLS) String() string {
return fmt.Sprintf("PubKeyBLS{%X}", pubKey[:])
}
// KeyString Must return the full bytes in hex.
// Used for map keying, etc.
func (pubKey PubKeyBLS) KeyString() string {
return fmt.Sprintf("%X", pubKey[:])
}
// Equals check public key is equal
func (pubKey PubKeyBLS) Equals(other crypto.PubKey) bool {
if otherSecp, ok := other.(PubKeyBLS); ok {
return bytes.Equal(pubKey[:], otherSecp[:])
}
return false
}
// SignatureBLS Signature
type SignatureBLS [BLSSignatureLength]byte
// SignatureS signature struct
type SignatureS struct {
crypto.Signature
}
// Bytes convert signature to bytes
func (sig SignatureBLS) Bytes() []byte {
s := make([]byte, len(sig))
copy(s, sig[:])
return s
}
// IsZero check signature is zero
func (sig SignatureBLS) IsZero() bool { return len(sig) == 0 }
// String convert signature to string
func (sig SignatureBLS) String() string {
fingerprint := make([]byte, len(sig[:]))
copy(fingerprint, sig[:])
return fmt.Sprintf("/%X.../", fingerprint)
}
// Equals check signature equals
func (sig SignatureBLS) Equals(other crypto.Signature) bool {
if otherEd, ok := other.(SignatureBLS); ok {
return bytes.Equal(sig[:], otherEd[:])
}
return false
}
// Name name
const Name = "bls"
// ID id
const ID = 259
func init() {
crypto.Register(Name, &Driver{})
crypto.RegisterType(Name, ID)
}
// Copyright Fuzamei Corp. 2018 All Rights Reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package bls
import (
"fmt"
"testing"
"github.com/33cn/chain33/common/crypto"
"github.com/stretchr/testify/assert"
)
var blsDrv = &Driver{}
func TestGenKey(t *testing.T) {
sk, err := blsDrv.GenKey()
assert.NoError(t, err)
assert.NotEmpty(t, sk)
pk := sk.PubKey()
assert.NotEmpty(t, pk)
sk2, _ := blsDrv.GenKey()
assert.NotEqual(t, sk.Bytes(), sk2.Bytes(), "should not generate two same key", sk, sk2)
}
func TestSignAndVerify(t *testing.T) {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
m1 := []byte("message to be signed. 将要做签名的消息")
// sign and verify
sig1 := sk.Sign(m1)
ret := pk.VerifyBytes(m1, sig1)
assert.Equal(t, true, ret)
// different message should have different signature
m2 := []byte("message to be signed. 将要做签名的消息.")
sig2 := sk.Sign(m2)
assert.NotEqual(t, sig1, sig2, "different message got the same signature", sig1, sig2)
// different key should have different signature for a same message.
sk2, _ := blsDrv.GenKey()
sig12 := sk2.Sign(m1)
ret = pk.VerifyBytes(m1, sig12)
assert.Equal(t, false, ret)
}
func TestAggregate(t *testing.T) {
m := []byte("message to be signed. 将要做签名的消息")
n := 8
pubs := make([]crypto.PubKey, 0, n)
sigs := make([]crypto.Signature, 0, n) //signatures for the same message
msgs := make([][]byte, 0, n)
dsigs := make([]crypto.Signature, 0, n) //signatures for each (key,message) pair
for i := 0; i < n; i++ {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
pubs = append(pubs, pk)
sigs = append(sigs, sk.Sign(m))
msgi := append(m, byte(i))
msgs = append(msgs, msgi)
dsigs = append(dsigs, sk.Sign(msgi))
}
asig, err := blsDrv.Aggregate(sigs)
assert.NoError(t, err)
// One
err = blsDrv.VerifyAggregatedOne(pubs, m, asig)
assert.NoError(t, err)
apub, err := blsDrv.AggregatePublic(pubs)
assert.NoError(t, err)
ret := apub.VerifyBytes(m, asig)
assert.Equal(t, true, ret)
// N
adsig, err := blsDrv.Aggregate(dsigs)
assert.NoError(t, err)
err = blsDrv.VerifyAggregatedN(pubs, msgs, adsig)
assert.NoError(t, err)
//lose some messages will cause an error
err = blsDrv.VerifyAggregatedN(pubs, msgs[1:], adsig)
assert.Error(t, err)
//with out-of-order public keys, will has no effect on VerifyAggregatedOne, but DO effects VerifyAggregatedN
pubs[0], pubs[1] = pubs[1], pubs[0]
err = blsDrv.VerifyAggregatedOne(pubs, m, asig)
assert.NoError(t, err)
err = blsDrv.VerifyAggregatedN(pubs, msgs, adsig)
assert.Error(t, err)
//invalid length
_, err = blsDrv.Aggregate(nil)
assert.Error(t, err)
_, err = blsDrv.AggregatePublic(make([]crypto.PubKey, 0))
assert.Error(t, err)
}
//benchmark
func BenchmarkBLSAggregateSignature(b *testing.B) {
msg := []byte(">16 character identical message")
n := 200
sigs := make([]crypto.Signature, 0, n) //signatures for the same message
for i := 0; i < n; i++ {
sk, _ := blsDrv.GenKey()
sigs = append(sigs, sk.Sign(msg))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
blsDrv.Aggregate(sigs) //nolint:errcheck
}
}
func BenchmarkBLSSign(b *testing.B) {
sks := make([]crypto.PrivKey, b.N)
msgs := make([][]byte, 0, b.N)
for i := range sks {
sks[i], _ = blsDrv.GenKey()
msgs = append(msgs, []byte(fmt.Sprintf("Hello world! 16 characters %d", i)))
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
sks[i].Sign(msgs[i])
}
}
func BenchmarkBLSVerify(b *testing.B) {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
m := []byte(">16 character identical message")
sig := sk.Sign(m)
b.ResetTimer()
for i := 0; i < b.N; i++ {
pk.VerifyBytes(m, sig) //nolint:errcheck
}
}
func BenchmarkBlsManager_VerifyAggregatedOne(b *testing.B) {
m := []byte("message to be signed. 将要做签名的消息")
n := 100
pubs := make([]crypto.PubKey, 0, n)
sigs := make([]crypto.Signature, 0, n) //signatures for the same message
for i := 0; i < n; i++ {
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
pubs = append(pubs, pk)
sigs = append(sigs, sk.Sign(m))
}
asig, _ := blsDrv.Aggregate(sigs)
b.ResetTimer()
for i := 0; i < b.N; i++ {
blsDrv.VerifyAggregatedOne(pubs, m, asig) //nolint:errcheck
}
}
func BenchmarkBlsManager_VerifyAggregatedN(b *testing.B) {
m := []byte("message to be signed. 将要做签名的消息")
n := 100
pubs := make([]crypto.PubKey, 0, n)
sigs := make([]crypto.Signature, 0, n)
msgs := make([][]byte, 0, n)
for i := 0; i < n; i++ {
mi := append(m, byte(i))
sk, _ := blsDrv.GenKey()
pk := sk.PubKey()
pubs = append(pubs, pk)
sigs = append(sigs, sk.Sign(mi))
msgs = append(msgs, mi)
}
asig, _ := blsDrv.Aggregate(sigs)
b.ResetTimer()
for i := 0; i < b.N; i++ {
blsDrv.VerifyAggregatedN(pubs, msgs, asig) //nolint:errcheck
}
}
package init
import (
_ "github.com/33cn/plugin/plugin/crypto/bls" //auto gen
_ "github.com/33cn/plugin/plugin/crypto/ecdsa" //auto gen
_ "github.com/33cn/plugin/plugin/crypto/sm2" //auto gen
)
......@@ -25,6 +25,7 @@ var (
strChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" // 62 characters
genFile = "genesis_file.json"
pvFile = "priv_validator_"
AuthBLS = 259
)
// ValCmd valnode cmd register
......@@ -164,8 +165,9 @@ func CreateCmd() *cobra.Command {
}
func addCreateCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("num", "n", "", "Num of the keyfile to create")
cmd.Flags().StringP("num", "n", "", "num of the keyfile to create")
cmd.MarkFlagRequired("num")
cmd.Flags().StringP("type", "t", "ed25519", "sign type of the keyfile (secp256k1, ed25519, sm2, bls)")
}
// RandStr ...
......@@ -192,10 +194,11 @@ MAIN_LOOP:
return string(chars)
}
func initCryptoImpl() error {
cr, err := crypto.New(types.GetSignName("", types.ED25519))
func initCryptoImpl(signType int) error {
ttypes.CryptoName = types.GetSignName("", signType)
cr, err := crypto.New(ttypes.CryptoName)
if err != nil {
fmt.Printf("New crypto impl failed err: %v", err)
fmt.Printf("Init crypto fail: %v", err)
return err
}
ttypes.ConsensusCrypto = cr
......@@ -204,7 +207,13 @@ func initCryptoImpl() error {
func createFiles(cmd *cobra.Command, args []string) {
// init crypto instance
err := initCryptoImpl()
ty, _ := cmd.Flags().GetString("type")
signType, ok := ttypes.SignMap[ty]
if !ok {
fmt.Println("type parameter is not valid")
return
}
err := initCryptoImpl(signType)
if err != nil {
return
}
......@@ -232,7 +241,7 @@ func createFiles(cmd *cobra.Command, args []string) {
// create genesis validator by the pubkey of private validator
gv := ttypes.GenesisValidator{
PubKey: ttypes.KeyText{Kind: "ed25519", Data: privValidator.GetPubKey().KeyString()},
PubKey: ttypes.KeyText{Kind: ttypes.CryptoName, Data: privValidator.GetPubKey().KeyString()},
Power: 10,
}
genDoc.Validators = append(genDoc.Validators, gv)
......
......@@ -35,11 +35,22 @@ func (val *ValNode) Exec_BlockInfo(blockInfo *pty.TendermintBlockInfo, tx *types
return receipt, nil
}
func getConfigKey(key string, db dbm.KV) ([]byte, error) {
configKey := types.ConfigKey(key)
value, err := db.Get([]byte(configKey))
if err != nil {
clog.Error("getConfigKey not find", "configKey", configKey, "err", err)
return nil, err
}
return value, nil
}
func getManageKey(key string, db dbm.KV) ([]byte, error) {
manageKey := types.ManageKey(key)
value, err := db.Get([]byte(manageKey))
if err != nil {
return nil, err
clog.Info("getManageKey not find", "manageKey", manageKey, "err", err)
return getConfigKey(key, db)
}
return value, nil
}
......
......@@ -5,86 +5,88 @@ import "blockchain.proto";
package types;
message BlockID {
bytes Hash = 1;
bytes hash = 1;
}
message TendermintBitArray {
int32 Bits = 1;
repeated uint64 Elems = 2;
int32 bits = 1;
repeated uint64 elems = 2;
}
message Vote {
bytes ValidatorAddress = 1;
int32 ValidatorIndex = 2;
int64 Height = 3;
int32 Round = 4;
int64 Timestamp = 5;
uint32 Type = 6;
BlockID BlockID = 7;
bytes Signature = 8;
bytes validatorAddress = 1;
int32 validatorIndex = 2;
int64 height = 3;
int32 round = 4;
int64 timestamp = 5;
uint32 type = 6;
BlockID blockID = 7;
bytes signature = 8;
bool useAggSig = 9;
}
message TendermintCommit {
BlockID BlockID = 1;
repeated Vote Precommits = 2;
BlockID blockID = 1;
repeated Vote precommits = 2;
AggVote aggVote = 3;
}
message TendermintBlockInfo {
State State = 2;
Proposal Proposal = 3;
State state = 2;
Proposal proposal = 3;
TendermintBlock block = 4;
}
message BlockSize {
int32 MaxBytes = 1;
int32 MaxTxs = 2;
int64 MaxGas = 3;
int32 maxBytes = 1;
int32 maxTxs = 2;
int64 maxGas = 3;
}
message TxSize {
int32 MaxBytes = 1;
int64 MaxGas = 2;
int32 maxBytes = 1;
int64 maxGas = 2;
}
message BlockGossip {
int32 BlockPartSizeBytes = 1;
int32 blockPartSizeBytes = 1;
}
message EvidenceParams {
int64 MaxAge = 1;
int64 maxAge = 1;
}
message ConsensusParams {
BlockSize BlockSize = 1;
TxSize TxSize = 2;
BlockGossip BlockGossip = 3;
EvidenceParams EvidenceParams = 4;
BlockSize blockSize = 1;
TxSize txSize = 2;
BlockGossip blockGossip = 3;
EvidenceParams evidenceParams = 4;
}
message Validator {
bytes Address = 1;
bytes PubKey = 2;
int64 VotingPower = 3;
int64 Accum = 4;
bytes address = 1;
bytes pubKey = 2;
int64 votingPower = 3;
int64 accum = 4;
}
message ValidatorSet {
repeated Validator Validators = 1;
Validator Proposer = 2;
repeated Validator validators = 1;
Validator proposer = 2;
}
message State {
string ChainID = 1;
int64 LastBlockHeight = 2;
int64 LastBlockTotalTx = 3;
BlockID LastBlockID = 4;
int64 LastBlockTime = 5;
ValidatorSet Validators = 6;
ValidatorSet LastValidators = 7;
int64 LastHeightValidatorsChanged = 8;
ConsensusParams ConsensusParams = 9;
int64 LastHeightConsensusParamsChanged = 10;
bytes LastResultsHash = 11;
bytes AppHash = 12;
string chainID = 1;
int64 lastBlockHeight = 2;
int64 lastBlockTotalTx = 3;
BlockID lastBlockID = 4;
int64 lastBlockTime = 5;
ValidatorSet validators = 6;
ValidatorSet lastValidators = 7;
int64 lastHeightValidatorsChanged = 8;
ConsensusParams consensusParams = 9;
int64 lastHeightConsensusParamsChanged = 10;
bytes lastResultsHash = 11;
bytes appHash = 12;
}
message TendermintBlockHeader {
......@@ -174,3 +176,14 @@ message Heartbeat {
message IsHealthy {
bool isHealthy = 1;
}
message AggVote {
bytes validatorAddress = 1;
TendermintBitArray validatorArray = 2;
int64 height = 3;
int32 round = 4;
int64 timestamp = 5;
uint32 type = 6;
BlockID blockID = 7;
bytes signature = 8;
}
\ No newline at end of file
......@@ -23,7 +23,7 @@ var _ = math.Inf
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type BlockID struct {
Hash []byte `protobuf:"bytes,1,opt,name=Hash,proto3" json:"Hash,omitempty"`
Hash []byte `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -62,8 +62,8 @@ func (m *BlockID) GetHash() []byte {
}
type TendermintBitArray struct {
Bits int32 `protobuf:"varint,1,opt,name=Bits,proto3" json:"Bits,omitempty"`
Elems []uint64 `protobuf:"varint,2,rep,packed,name=Elems,proto3" json:"Elems,omitempty"`
Bits int32 `protobuf:"varint,1,opt,name=bits,proto3" json:"bits,omitempty"`
Elems []uint64 `protobuf:"varint,2,rep,packed,name=elems,proto3" json:"elems,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -109,14 +109,15 @@ func (m *TendermintBitArray) GetElems() []uint64 {
}
type Vote struct {
ValidatorAddress []byte `protobuf:"bytes,1,opt,name=ValidatorAddress,proto3" json:"ValidatorAddress,omitempty"`
ValidatorIndex int32 `protobuf:"varint,2,opt,name=ValidatorIndex,proto3" json:"ValidatorIndex,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=Height,proto3" json:"Height,omitempty"`
Round int32 `protobuf:"varint,4,opt,name=Round,proto3" json:"Round,omitempty"`
Timestamp int64 `protobuf:"varint,5,opt,name=Timestamp,proto3" json:"Timestamp,omitempty"`
Type uint32 `protobuf:"varint,6,opt,name=Type,proto3" json:"Type,omitempty"`
BlockID *BlockID `protobuf:"bytes,7,opt,name=BlockID,proto3" json:"BlockID,omitempty"`
Signature []byte `protobuf:"bytes,8,opt,name=Signature,proto3" json:"Signature,omitempty"`
ValidatorAddress []byte `protobuf:"bytes,1,opt,name=validatorAddress,proto3" json:"validatorAddress,omitempty"`
ValidatorIndex int32 `protobuf:"varint,2,opt,name=validatorIndex,proto3" json:"validatorIndex,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"`
Round int32 `protobuf:"varint,4,opt,name=round,proto3" json:"round,omitempty"`
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Type uint32 `protobuf:"varint,6,opt,name=type,proto3" json:"type,omitempty"`
BlockID *BlockID `protobuf:"bytes,7,opt,name=blockID,proto3" json:"blockID,omitempty"`
Signature []byte `protobuf:"bytes,8,opt,name=signature,proto3" json:"signature,omitempty"`
UseAggSig bool `protobuf:"varint,9,opt,name=useAggSig,proto3" json:"useAggSig,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -203,9 +204,17 @@ func (m *Vote) GetSignature() []byte {
return nil
}
func (m *Vote) GetUseAggSig() bool {
if m != nil {
return m.UseAggSig
}
return false
}
type TendermintCommit struct {
BlockID *BlockID `protobuf:"bytes,1,opt,name=BlockID,proto3" json:"BlockID,omitempty"`
Precommits []*Vote `protobuf:"bytes,2,rep,name=Precommits,proto3" json:"Precommits,omitempty"`
BlockID *BlockID `protobuf:"bytes,1,opt,name=blockID,proto3" json:"blockID,omitempty"`
Precommits []*Vote `protobuf:"bytes,2,rep,name=precommits,proto3" json:"precommits,omitempty"`
AggVote *AggVote `protobuf:"bytes,3,opt,name=aggVote,proto3" json:"aggVote,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -250,9 +259,16 @@ func (m *TendermintCommit) GetPrecommits() []*Vote {
return nil
}
func (m *TendermintCommit) GetAggVote() *AggVote {
if m != nil {
return m.AggVote
}
return nil
}
type TendermintBlockInfo struct {
State *State `protobuf:"bytes,2,opt,name=State,proto3" json:"State,omitempty"`
Proposal *Proposal `protobuf:"bytes,3,opt,name=Proposal,proto3" json:"Proposal,omitempty"`
State *State `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"`
Proposal *Proposal `protobuf:"bytes,3,opt,name=proposal,proto3" json:"proposal,omitempty"`
Block *TendermintBlock `protobuf:"bytes,4,opt,name=block,proto3" json:"block,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
......@@ -306,9 +322,9 @@ func (m *TendermintBlockInfo) GetBlock() *TendermintBlock {
}
type BlockSize struct {
MaxBytes int32 `protobuf:"varint,1,opt,name=MaxBytes,proto3" json:"MaxBytes,omitempty"`
MaxTxs int32 `protobuf:"varint,2,opt,name=MaxTxs,proto3" json:"MaxTxs,omitempty"`
MaxGas int64 `protobuf:"varint,3,opt,name=MaxGas,proto3" json:"MaxGas,omitempty"`
MaxBytes int32 `protobuf:"varint,1,opt,name=maxBytes,proto3" json:"maxBytes,omitempty"`
MaxTxs int32 `protobuf:"varint,2,opt,name=maxTxs,proto3" json:"maxTxs,omitempty"`
MaxGas int64 `protobuf:"varint,3,opt,name=maxGas,proto3" json:"maxGas,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -361,8 +377,8 @@ func (m *BlockSize) GetMaxGas() int64 {
}
type TxSize struct {
MaxBytes int32 `protobuf:"varint,1,opt,name=MaxBytes,proto3" json:"MaxBytes,omitempty"`
MaxGas int64 `protobuf:"varint,2,opt,name=MaxGas,proto3" json:"MaxGas,omitempty"`
MaxBytes int32 `protobuf:"varint,1,opt,name=maxBytes,proto3" json:"maxBytes,omitempty"`
MaxGas int64 `protobuf:"varint,2,opt,name=maxGas,proto3" json:"maxGas,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -408,7 +424,7 @@ func (m *TxSize) GetMaxGas() int64 {
}
type BlockGossip struct {
BlockPartSizeBytes int32 `protobuf:"varint,1,opt,name=BlockPartSizeBytes,proto3" json:"BlockPartSizeBytes,omitempty"`
BlockPartSizeBytes int32 `protobuf:"varint,1,opt,name=blockPartSizeBytes,proto3" json:"blockPartSizeBytes,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -447,7 +463,7 @@ func (m *BlockGossip) GetBlockPartSizeBytes() int32 {
}
type EvidenceParams struct {
MaxAge int64 `protobuf:"varint,1,opt,name=MaxAge,proto3" json:"MaxAge,omitempty"`
MaxAge int64 `protobuf:"varint,1,opt,name=maxAge,proto3" json:"maxAge,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -486,10 +502,10 @@ func (m *EvidenceParams) GetMaxAge() int64 {
}
type ConsensusParams struct {
BlockSize *BlockSize `protobuf:"bytes,1,opt,name=BlockSize,proto3" json:"BlockSize,omitempty"`
TxSize *TxSize `protobuf:"bytes,2,opt,name=TxSize,proto3" json:"TxSize,omitempty"`
BlockGossip *BlockGossip `protobuf:"bytes,3,opt,name=BlockGossip,proto3" json:"BlockGossip,omitempty"`
EvidenceParams *EvidenceParams `protobuf:"bytes,4,opt,name=EvidenceParams,proto3" json:"EvidenceParams,omitempty"`
BlockSize *BlockSize `protobuf:"bytes,1,opt,name=blockSize,proto3" json:"blockSize,omitempty"`
TxSize *TxSize `protobuf:"bytes,2,opt,name=txSize,proto3" json:"txSize,omitempty"`
BlockGossip *BlockGossip `protobuf:"bytes,3,opt,name=blockGossip,proto3" json:"blockGossip,omitempty"`
EvidenceParams *EvidenceParams `protobuf:"bytes,4,opt,name=evidenceParams,proto3" json:"evidenceParams,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -549,10 +565,10 @@ func (m *ConsensusParams) GetEvidenceParams() *EvidenceParams {
}
type Validator struct {
Address []byte `protobuf:"bytes,1,opt,name=Address,proto3" json:"Address,omitempty"`
PubKey []byte `protobuf:"bytes,2,opt,name=PubKey,proto3" json:"PubKey,omitempty"`
VotingPower int64 `protobuf:"varint,3,opt,name=VotingPower,proto3" json:"VotingPower,omitempty"`
Accum int64 `protobuf:"varint,4,opt,name=Accum,proto3" json:"Accum,omitempty"`
Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
PubKey []byte `protobuf:"bytes,2,opt,name=pubKey,proto3" json:"pubKey,omitempty"`
VotingPower int64 `protobuf:"varint,3,opt,name=votingPower,proto3" json:"votingPower,omitempty"`
Accum int64 `protobuf:"varint,4,opt,name=accum,proto3" json:"accum,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -612,8 +628,8 @@ func (m *Validator) GetAccum() int64 {
}
type ValidatorSet struct {
Validators []*Validator `protobuf:"bytes,1,rep,name=Validators,proto3" json:"Validators,omitempty"`
Proposer *Validator `protobuf:"bytes,2,opt,name=Proposer,proto3" json:"Proposer,omitempty"`
Validators []*Validator `protobuf:"bytes,1,rep,name=validators,proto3" json:"validators,omitempty"`
Proposer *Validator `protobuf:"bytes,2,opt,name=proposer,proto3" json:"proposer,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -659,18 +675,18 @@ func (m *ValidatorSet) GetProposer() *Validator {
}
type State struct {
ChainID string `protobuf:"bytes,1,opt,name=ChainID,proto3" json:"ChainID,omitempty"`
LastBlockHeight int64 `protobuf:"varint,2,opt,name=LastBlockHeight,proto3" json:"LastBlockHeight,omitempty"`
LastBlockTotalTx int64 `protobuf:"varint,3,opt,name=LastBlockTotalTx,proto3" json:"LastBlockTotalTx,omitempty"`
LastBlockID *BlockID `protobuf:"bytes,4,opt,name=LastBlockID,proto3" json:"LastBlockID,omitempty"`
LastBlockTime int64 `protobuf:"varint,5,opt,name=LastBlockTime,proto3" json:"LastBlockTime,omitempty"`
Validators *ValidatorSet `protobuf:"bytes,6,opt,name=Validators,proto3" json:"Validators,omitempty"`
LastValidators *ValidatorSet `protobuf:"bytes,7,opt,name=LastValidators,proto3" json:"LastValidators,omitempty"`
LastHeightValidatorsChanged int64 `protobuf:"varint,8,opt,name=LastHeightValidatorsChanged,proto3" json:"LastHeightValidatorsChanged,omitempty"`
ConsensusParams *ConsensusParams `protobuf:"bytes,9,opt,name=ConsensusParams,proto3" json:"ConsensusParams,omitempty"`
LastHeightConsensusParamsChanged int64 `protobuf:"varint,10,opt,name=LastHeightConsensusParamsChanged,proto3" json:"LastHeightConsensusParamsChanged,omitempty"`
LastResultsHash []byte `protobuf:"bytes,11,opt,name=LastResultsHash,proto3" json:"LastResultsHash,omitempty"`
AppHash []byte `protobuf:"bytes,12,opt,name=AppHash,proto3" json:"AppHash,omitempty"`
ChainID string `protobuf:"bytes,1,opt,name=chainID,proto3" json:"chainID,omitempty"`
LastBlockHeight int64 `protobuf:"varint,2,opt,name=lastBlockHeight,proto3" json:"lastBlockHeight,omitempty"`
LastBlockTotalTx int64 `protobuf:"varint,3,opt,name=lastBlockTotalTx,proto3" json:"lastBlockTotalTx,omitempty"`
LastBlockID *BlockID `protobuf:"bytes,4,opt,name=lastBlockID,proto3" json:"lastBlockID,omitempty"`
LastBlockTime int64 `protobuf:"varint,5,opt,name=lastBlockTime,proto3" json:"lastBlockTime,omitempty"`
Validators *ValidatorSet `protobuf:"bytes,6,opt,name=validators,proto3" json:"validators,omitempty"`
LastValidators *ValidatorSet `protobuf:"bytes,7,opt,name=lastValidators,proto3" json:"lastValidators,omitempty"`
LastHeightValidatorsChanged int64 `protobuf:"varint,8,opt,name=lastHeightValidatorsChanged,proto3" json:"lastHeightValidatorsChanged,omitempty"`
ConsensusParams *ConsensusParams `protobuf:"bytes,9,opt,name=consensusParams,proto3" json:"consensusParams,omitempty"`
LastHeightConsensusParamsChanged int64 `protobuf:"varint,10,opt,name=lastHeightConsensusParamsChanged,proto3" json:"lastHeightConsensusParamsChanged,omitempty"`
LastResultsHash []byte `protobuf:"bytes,11,opt,name=lastResultsHash,proto3" json:"lastResultsHash,omitempty"`
AppHash []byte `protobuf:"bytes,12,opt,name=appHash,proto3" json:"appHash,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
......@@ -1566,6 +1582,101 @@ func (m *IsHealthy) GetIsHealthy() bool {
return false
}
type AggVote struct {
ValidatorAddress []byte `protobuf:"bytes,1,opt,name=validatorAddress,proto3" json:"validatorAddress,omitempty"`
ValidatorArray *TendermintBitArray `protobuf:"bytes,2,opt,name=validatorArray,proto3" json:"validatorArray,omitempty"`
Height int64 `protobuf:"varint,3,opt,name=height,proto3" json:"height,omitempty"`
Round int32 `protobuf:"varint,4,opt,name=round,proto3" json:"round,omitempty"`
Timestamp int64 `protobuf:"varint,5,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
Type uint32 `protobuf:"varint,6,opt,name=type,proto3" json:"type,omitempty"`
BlockID *BlockID `protobuf:"bytes,7,opt,name=blockID,proto3" json:"blockID,omitempty"`
Signature []byte `protobuf:"bytes,8,opt,name=signature,proto3" json:"signature,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *AggVote) Reset() { *m = AggVote{} }
func (m *AggVote) String() string { return proto.CompactTextString(m) }
func (*AggVote) ProtoMessage() {}
func (*AggVote) Descriptor() ([]byte, []int) {
return fileDescriptor_04f926c8da23c367, []int{24}
}
func (m *AggVote) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AggVote.Unmarshal(m, b)
}
func (m *AggVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_AggVote.Marshal(b, m, deterministic)
}
func (m *AggVote) XXX_Merge(src proto.Message) {
xxx_messageInfo_AggVote.Merge(m, src)
}
func (m *AggVote) XXX_Size() int {
return xxx_messageInfo_AggVote.Size(m)
}
func (m *AggVote) XXX_DiscardUnknown() {
xxx_messageInfo_AggVote.DiscardUnknown(m)
}
var xxx_messageInfo_AggVote proto.InternalMessageInfo
func (m *AggVote) GetValidatorAddress() []byte {
if m != nil {
return m.ValidatorAddress
}
return nil
}
func (m *AggVote) GetValidatorArray() *TendermintBitArray {
if m != nil {
return m.ValidatorArray
}
return nil
}
func (m *AggVote) GetHeight() int64 {
if m != nil {
return m.Height
}
return 0
}
func (m *AggVote) GetRound() int32 {
if m != nil {
return m.Round
}
return 0
}
func (m *AggVote) GetTimestamp() int64 {
if m != nil {
return m.Timestamp
}
return 0
}
func (m *AggVote) GetType() uint32 {
if m != nil {
return m.Type
}
return 0
}
func (m *AggVote) GetBlockID() *BlockID {
if m != nil {
return m.BlockID
}
return nil
}
func (m *AggVote) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
func init() {
proto.RegisterType((*BlockID)(nil), "types.BlockID")
proto.RegisterType((*TendermintBitArray)(nil), "types.TendermintBitArray")
......@@ -1591,6 +1702,7 @@ func init() {
proto.RegisterType((*VoteSetBitsMsg)(nil), "types.VoteSetBitsMsg")
proto.RegisterType((*Heartbeat)(nil), "types.Heartbeat")
proto.RegisterType((*IsHealthy)(nil), "types.IsHealthy")
proto.RegisterType((*AggVote)(nil), "types.AggVote")
}
func init() {
......@@ -1598,89 +1710,90 @@ func init() {
}
var fileDescriptor_04f926c8da23c367 = []byte{
// 1334 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0xdf, 0x6e, 0x1b, 0xc5,
0x17, 0xd6, 0xc6, 0x7f, 0x12, 0x1f, 0xe7, 0x9f, 0xa6, 0xbf, 0xf6, 0x67, 0x4a, 0x90, 0xac, 0x11,
0x20, 0xd3, 0x56, 0xa1, 0x4a, 0x2b, 0x71, 0x51, 0x8a, 0x9a, 0xa4, 0x55, 0x13, 0x48, 0xa8, 0x35,
0xb6, 0xca, 0xf5, 0xc4, 0x1e, 0xec, 0x05, 0x7b, 0xd7, 0xec, 0x8c, 0x9d, 0x18, 0x89, 0x1b, 0xde,
0x00, 0x89, 0x27, 0xe0, 0x9e, 0x2b, 0x2e, 0x78, 0x04, 0x9e, 0x80, 0x4b, 0x78, 0x16, 0x74, 0xce,
0xcc, 0xae, 0x67, 0xd7, 0x6e, 0x4a, 0x11, 0xe2, 0x6e, 0xcf, 0x37, 0xdf, 0xcc, 0xd9, 0x39, 0xe7,
0x3b, 0x67, 0x66, 0x60, 0xd7, 0xa8, 0xa8, 0xaf, 0x92, 0x71, 0x18, 0x99, 0xfd, 0x49, 0x12, 0x9b,
0x98, 0x55, 0xcc, 0x7c, 0xa2, 0xf4, 0xed, 0xdd, 0x8b, 0x51, 0xdc, 0xfb, 0xba, 0x37, 0x94, 0x61,
0x64, 0x07, 0xf8, 0x3b, 0xb0, 0x7e, 0x84, 0xd8, 0xe9, 0x53, 0xc6, 0xa0, 0x7c, 0x22, 0xf5, 0xb0,
0x11, 0x34, 0x83, 0xd6, 0xa6, 0xa0, 0x6f, 0xfe, 0x09, 0xb0, 0x6e, 0xb6, 0xd6, 0x51, 0x68, 0x0e,
0x93, 0x44, 0xce, 0x91, 0x79, 0x14, 0x1a, 0x4d, 0xcc, 0x8a, 0xa0, 0x6f, 0xf6, 0x3f, 0xa8, 0x3c,
0x1b, 0xa9, 0xb1, 0x6e, 0xac, 0x35, 0x4b, 0xad, 0xb2, 0xb0, 0x06, 0xff, 0x7e, 0x0d, 0xca, 0x2f,
0x63, 0xa3, 0xd8, 0x1d, 0xd8, 0x7d, 0x29, 0x47, 0x61, 0x5f, 0x9a, 0x38, 0x39, 0xec, 0xf7, 0x13,
0xa5, 0xb5, 0x73, 0xb4, 0x84, 0xb3, 0xf7, 0x61, 0x3b, 0xc3, 0x4e, 0xa3, 0xbe, 0xba, 0x6a, 0xac,
0x91, 0xa3, 0x02, 0xca, 0x6e, 0x41, 0xf5, 0x44, 0x85, 0x83, 0xa1, 0x69, 0x94, 0x9a, 0x41, 0xab,
0x24, 0x9c, 0x85, 0xbf, 0x22, 0xe2, 0x69, 0xd4, 0x6f, 0x94, 0x69, 0x9a, 0x35, 0xd8, 0x1e, 0xd4,
0xba, 0xe1, 0x58, 0x69, 0x23, 0xc7, 0x93, 0x46, 0x85, 0x26, 0x2c, 0x00, 0xdc, 0x52, 0x77, 0x3e,
0x51, 0x8d, 0x6a, 0x33, 0x68, 0x6d, 0x09, 0xfa, 0x66, 0xad, 0x2c, 0x36, 0x8d, 0xf5, 0x66, 0xd0,
0xaa, 0x1f, 0x6c, 0xef, 0x53, 0x18, 0xf7, 0x1d, 0x2a, 0xb2, 0xd0, 0xed, 0x41, 0xad, 0x13, 0x0e,
0x22, 0x69, 0xa6, 0x89, 0x6a, 0x6c, 0xd0, 0xb6, 0x16, 0x00, 0x0f, 0x61, 0x77, 0x11, 0xc4, 0xe3,
0x78, 0x3c, 0x0e, 0x8d, 0xbf, 0x76, 0x70, 0xfd, 0xda, 0x77, 0x01, 0xda, 0x89, 0xea, 0xd1, 0x34,
0x1b, 0xdd, 0xfa, 0x41, 0xdd, 0x91, 0x31, 0xb4, 0xc2, 0x1b, 0xe6, 0x3f, 0x06, 0x70, 0xc3, 0x4b,
0x18, 0x2d, 0x11, 0x7d, 0x19, 0x33, 0x0e, 0x95, 0x8e, 0x91, 0x46, 0x51, 0x24, 0xeb, 0x07, 0x9b,
0x6e, 0x3e, 0x61, 0xc2, 0x0e, 0xb1, 0xbb, 0xb0, 0xd1, 0x4e, 0xe2, 0x49, 0xac, 0xe5, 0x88, 0x02,
0x5a, 0x3f, 0xd8, 0x71, 0xb4, 0x14, 0x16, 0x19, 0x81, 0xdd, 0x83, 0x0a, 0x69, 0x89, 0x62, 0x5c,
0x3f, 0xb8, 0xe5, 0x98, 0x05, 0xdf, 0xc2, 0x92, 0xf8, 0x17, 0x50, 0x23, 0xbb, 0x13, 0x7e, 0xab,
0xd8, 0x6d, 0xd8, 0x38, 0x97, 0x57, 0x47, 0x73, 0xa3, 0x52, 0x05, 0x65, 0x36, 0xa6, 0xf4, 0x5c,
0x5e, 0x75, 0xaf, 0xb4, 0x4b, 0xb9, 0xb3, 0x1c, 0xfe, 0x5c, 0xea, 0x34, 0xd5, 0xd6, 0xe2, 0x1f,
0x43, 0xb5, 0x7b, 0xf5, 0x37, 0x57, 0xc5, 0xd9, 0x6b, 0xb9, 0xd9, 0x8f, 0xa1, 0x4e, 0xbf, 0xf5,
0x3c, 0xd6, 0x3a, 0x9c, 0xb0, 0x7d, 0x60, 0x64, 0xb6, 0x65, 0x62, 0x70, 0x4d, 0x7f, 0xb1, 0x15,
0x23, 0xbc, 0x05, 0xdb, 0xcf, 0x66, 0x61, 0x5f, 0x45, 0x3d, 0xd5, 0x96, 0x89, 0x1c, 0xa7, 0x8e,
0x0e, 0x07, 0x8a, 0x66, 0x59, 0x47, 0x87, 0x03, 0xc5, 0xff, 0x08, 0x60, 0xe7, 0x38, 0x8e, 0xb4,
0x8a, 0xf4, 0x54, 0x3b, 0xee, 0xbe, 0x17, 0x13, 0xa7, 0x81, 0x5d, 0x5f, 0x03, 0x88, 0x0b, 0x2f,
0x6c, 0xef, 0xa5, 0x5b, 0x75, 0x39, 0xdc, 0x4a, 0x43, 0x4e, 0xa0, 0x48, 0xe3, 0xf0, 0x30, 0xb7,
0x27, 0x97, 0x48, 0xe6, 0x2f, 0x6c, 0x47, 0x44, 0x6e, 0xeb, 0x8f, 0x8b, 0x5b, 0x71, 0x79, 0xbd,
0xe9, 0x26, 0xe6, 0x07, 0x45, 0x81, 0xcc, 0xa7, 0x50, 0xcb, 0x6a, 0x93, 0x35, 0x60, 0x3d, 0x5f,
0xe1, 0xa9, 0x89, 0xe1, 0x69, 0x4f, 0x2f, 0x3e, 0x53, 0x73, 0xda, 0xc2, 0xa6, 0x70, 0x16, 0x6b,
0x42, 0xfd, 0x65, 0x6c, 0xc2, 0x68, 0xd0, 0x8e, 0x2f, 0x55, 0xe2, 0x52, 0xec, 0x43, 0x58, 0xd2,
0x87, 0xbd, 0xde, 0x74, 0x4c, 0xbf, 0x55, 0x12, 0xd6, 0xe0, 0x11, 0x6c, 0x66, 0x6e, 0x3b, 0xca,
0xb0, 0xfb, 0x00, 0x99, 0x8d, 0xce, 0x4b, 0x5e, 0x4c, 0xb3, 0x01, 0xe1, 0x71, 0xd8, 0xbd, 0x54,
0xf3, 0x2a, 0x71, 0x61, 0x5d, 0xe6, 0x67, 0x0c, 0xfe, 0x7b, 0xd9, 0x95, 0x11, 0xee, 0xf1, 0x18,
0xbb, 0xa8, 0x2b, 0xdf, 0x9a, 0x48, 0x4d, 0xd6, 0x82, 0x9d, 0x33, 0xa9, 0xad, 0xfc, 0x5d, 0x77,
0xb2, 0xa2, 0x2b, 0xc2, 0xd8, 0x12, 0x33, 0xa8, 0x1b, 0x1b, 0x39, 0xea, 0x5e, 0xb9, 0xad, 0x2f,
0xe1, 0xec, 0x3e, 0xd4, 0x33, 0xec, 0xf4, 0xa9, 0x4b, 0x4e, 0xb1, 0x65, 0xf8, 0x14, 0xf6, 0x2e,
0x6c, 0x2d, 0x56, 0x09, 0xc7, 0xca, 0xb5, 0xbc, 0x3c, 0xc8, 0x1e, 0xe4, 0x22, 0x56, 0xa5, 0x65,
0x6f, 0x14, 0x23, 0xd0, 0x51, 0x26, 0x17, 0xb4, 0x47, 0xb0, 0x8d, 0xab, 0x78, 0x13, 0xd7, 0x5f,
0x3d, 0xb1, 0x40, 0x65, 0x4f, 0xe0, 0x6d, 0x44, 0x6c, 0x0c, 0x16, 0xf8, 0xf1, 0x50, 0x46, 0x03,
0xd5, 0xa7, 0xe6, 0x59, 0x12, 0xd7, 0x51, 0xd8, 0x93, 0xa5, 0x5a, 0x6a, 0xd4, 0x72, 0x4d, 0xa8,
0x30, 0x2a, 0x96, 0x4a, 0xef, 0x53, 0x68, 0x2e, 0x1c, 0x14, 0x06, 0xd3, 0x1f, 0x01, 0xfa, 0x91,
0xd7, 0xf2, 0xd2, 0x7c, 0x0b, 0xa5, 0xa7, 0x23, 0xa3, 0xe9, 0x00, 0xad, 0x93, 0xb8, 0x8b, 0x30,
0xd5, 0xc5, 0x64, 0x42, 0x8c, 0x4d, 0x57, 0x17, 0xd6, 0xe4, 0xbf, 0x96, 0xe0, 0x66, 0xa1, 0x73,
0x9e, 0x28, 0xd9, 0x57, 0x54, 0x4b, 0xbd, 0xbc, 0xce, 0x9c, 0x89, 0xb5, 0x34, 0xf4, 0xe5, 0xe5,
0x2c, 0xac, 0x94, 0x84, 0x0e, 0x3f, 0x2b, 0x25, 0x6b, 0xe0, 0xf1, 0x66, 0x50, 0x04, 0xb6, 0x7c,
0xe8, 0x1b, 0x57, 0x88, 0xa6, 0x63, 0xec, 0xb5, 0x56, 0x1a, 0xce, 0x42, 0xad, 0x8d, 0x3c, 0xad,
0x55, 0x57, 0x6b, 0xcd, 0xa3, 0x60, 0xef, 0x35, 0x56, 0xa8, 0x56, 0x0a, 0x25, 0x91, 0xd9, 0x78,
0x98, 0x23, 0xd5, 0x1e, 0x7b, 0xb4, 0x79, 0x7b, 0x3e, 0x16, 0x50, 0xe4, 0xcd, 0xb2, 0x54, 0x13,
0xaf, 0x66, 0x79, 0x79, 0x14, 0x75, 0xdd, 0x4b, 0x33, 0x41, 0x34, 0x20, 0x5a, 0x1e, 0xc4, 0xb8,
0x49, 0x17, 0x6b, 0x9b, 0x8d, 0xd4, 0xc4, 0x7c, 0x8d, 0x0a, 0xf9, 0xb2, 0xd9, 0x28, 0xc2, 0x8c,
0xc3, 0xe6, 0xc4, 0x55, 0x3e, 0x36, 0xb0, 0xc6, 0x16, 0xd1, 0x72, 0x18, 0xff, 0x29, 0x80, 0x9d,
0x42, 0xe6, 0xd8, 0x43, 0xcc, 0x0c, 0x66, 0xcf, 0x75, 0xf5, 0xbd, 0xd5, 0x67, 0xa3, 0xcd, 0xb0,
0x70, 0x5c, 0xd6, 0x84, 0x72, 0x5f, 0x1a, 0x59, 0x38, 0xa0, 0xed, 0x29, 0x4a, 0x23, 0xec, 0x23,
0x80, 0x45, 0xcc, 0x5c, 0x0b, 0xf8, 0xff, 0xd2, 0xda, 0x76, 0x58, 0x78, 0x54, 0xfe, 0x67, 0xb0,
0x38, 0xd9, 0x3d, 0xdd, 0x04, 0xab, 0x75, 0x63, 0x0f, 0x5e, 0xa7, 0x9b, 0x3d, 0xa8, 0x99, 0xec,
0xd2, 0x64, 0x15, 0xb5, 0x00, 0x30, 0xef, 0xed, 0x17, 0x67, 0xfe, 0x5d, 0x2b, 0xb3, 0xd9, 0x3e,
0x40, 0xfb, 0xc5, 0x59, 0x2a, 0xa2, 0xca, 0x4a, 0x11, 0x79, 0x0c, 0xf4, 0xa4, 0xb3, 0x2b, 0x54,
0xd5, 0x5e, 0xa1, 0x32, 0x00, 0x47, 0xe9, 0x26, 0x31, 0xc4, 0x7c, 0xad, 0xdb, 0xd1, 0x0c, 0xe0,
0xbf, 0x04, 0xb0, 0xf3, 0xb9, 0xba, 0x24, 0xc7, 0x1d, 0xa3, 0x26, 0xe7, 0x7a, 0xf0, 0x86, 0xfb,
0x64, 0x50, 0xd6, 0x46, 0xd9, 0x2d, 0x56, 0x04, 0x7d, 0xb3, 0x87, 0x70, 0x53, 0xab, 0x5e, 0x1c,
0xf5, 0x75, 0x27, 0x8c, 0x7a, 0xaa, 0x63, 0x64, 0x62, 0xba, 0x69, 0x11, 0x55, 0xc4, 0xea, 0xc1,
0x54, 0x5f, 0x2e, 0x0d, 0xe4, 0xa9, 0x42, 0xfc, 0x22, 0xcc, 0x2f, 0x61, 0x8b, 0x9a, 0x1b, 0x45,
0xe0, 0xcd, 0x7f, 0x39, 0x17, 0x92, 0x52, 0x21, 0x24, 0x98, 0x9a, 0x50, 0x7b, 0x52, 0xd9, 0x10,
0x99, 0xcd, 0x7f, 0x08, 0x60, 0x3b, 0xd5, 0x43, 0xfb, 0xc5, 0xd9, 0x75, 0xae, 0xef, 0xc0, 0xee,
0x64, 0xc1, 0x14, 0xde, 0x5f, 0x2c, 0xe1, 0xec, 0x11, 0xd4, 0x3d, 0xcc, 0xdd, 0x3c, 0xde, 0x5a,
0x16, 0xbf, 0x7b, 0x45, 0x08, 0x9f, 0xcd, 0xfb, 0x00, 0x27, 0x52, 0xe3, 0x7d, 0xf6, 0x1f, 0x25,
0x0f, 0x9d, 0xa4, 0xc9, 0xc3, 0x6f, 0x64, 0x86, 0xf4, 0x74, 0x70, 0x6f, 0x00, 0x32, 0xf8, 0x77,
0xb0, 0x83, 0x2e, 0x3a, 0xca, 0x9c, 0xcb, 0xaf, 0x0e, 0x1e, 0xfc, 0x3b, 0xae, 0x5a, 0xb0, 0x7e,
0x71, 0xed, 0xb9, 0x9c, 0x0e, 0xf3, 0x9f, 0x03, 0xd8, 0x76, 0xfe, 0xf1, 0xcd, 0xf4, 0x1f, 0xbb,
0x67, 0x1f, 0x42, 0x65, 0x16, 0xe3, 0x95, 0xb6, 0xf2, 0xba, 0xd4, 0x58, 0x1e, 0xff, 0x2d, 0x80,
0xda, 0x89, 0x92, 0x89, 0xb9, 0x50, 0x92, 0xb4, 0x30, 0x7b, 0xc5, 0x13, 0x6e, 0xb6, 0xe2, 0x09,
0x37, 0x5b, 0xf9, 0x84, 0x9b, 0x2d, 0x3d, 0xe1, 0x86, 0xb9, 0x27, 0x5c, 0x71, 0xfb, 0x65, 0x7f,
0xfb, 0xb7, 0x61, 0x43, 0xab, 0x6f, 0xa6, 0x78, 0xf1, 0x74, 0x45, 0x95, 0xd9, 0xd7, 0xf7, 0x0f,
0xfe, 0x01, 0xd4, 0x4e, 0xf5, 0x89, 0x92, 0x23, 0x33, 0x9c, 0x23, 0x35, 0x4c, 0x0d, 0xda, 0xc1,
0x86, 0x58, 0x00, 0x17, 0x55, 0x7a, 0x18, 0x3f, 0xf8, 0x2b, 0x00, 0x00, 0xff, 0xff, 0xbc, 0x0d,
0x4d, 0x91, 0x45, 0x0f, 0x00, 0x00,
// 1352 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x58, 0xdd, 0x8e, 0x1b, 0x35,
0x14, 0xd6, 0x6c, 0x32, 0xf9, 0x39, 0xc9, 0x26, 0x2b, 0xf7, 0x87, 0xb0, 0x14, 0x29, 0xb2, 0x00,
0x85, 0xb6, 0x5a, 0xaa, 0x6d, 0x25, 0x2e, 0x4a, 0x51, 0xb3, 0x2d, 0xea, 0x2e, 0xb4, 0x34, 0x72,
0x56, 0xe5, 0xda, 0x49, 0x4c, 0x32, 0x90, 0xf9, 0x61, 0xec, 0x6c, 0x77, 0x91, 0x78, 0x08, 0x04,
0x0f, 0x80, 0xb8, 0xe7, 0x8a, 0x0b, 0x1e, 0x81, 0x27, 0xe0, 0x12, 0x9e, 0x05, 0xf9, 0xd8, 0xf3,
0x9b, 0x34, 0xa5, 0x08, 0x21, 0xb8, 0x9b, 0xf3, 0xf9, 0xb3, 0x8f, 0xcf, 0x8f, 0x8f, 0x8f, 0x07,
0xf6, 0x94, 0x08, 0x66, 0x22, 0xf6, 0xbd, 0x40, 0x1d, 0x44, 0x71, 0xa8, 0x42, 0xe2, 0xaa, 0x8b,
0x48, 0xc8, 0xfd, 0xbd, 0xc9, 0x32, 0x9c, 0x7e, 0x39, 0x5d, 0x70, 0x2f, 0x30, 0x03, 0xf4, 0x4d,
0xa8, 0x1f, 0x69, 0xec, 0xe4, 0x21, 0x21, 0x50, 0x5d, 0x70, 0xb9, 0xe8, 0x39, 0x7d, 0x67, 0xd0,
0x66, 0xf8, 0x4d, 0x3f, 0x04, 0x72, 0x9a, 0xae, 0x75, 0xe4, 0xa9, 0x61, 0x1c, 0xf3, 0x0b, 0xcd,
0x9c, 0x78, 0x4a, 0x22, 0xd3, 0x65, 0xf8, 0x4d, 0x2e, 0x83, 0x2b, 0x96, 0xc2, 0x97, 0xbd, 0x9d,
0x7e, 0x65, 0x50, 0x65, 0x46, 0xa0, 0x3f, 0xec, 0x40, 0xf5, 0x59, 0xa8, 0x04, 0xb9, 0x0e, 0x7b,
0x67, 0x7c, 0xe9, 0xcd, 0xb8, 0x0a, 0xe3, 0xe1, 0x6c, 0x16, 0x0b, 0x29, 0xad, 0xa2, 0x35, 0x9c,
0xbc, 0x03, 0x9d, 0x14, 0x3b, 0x09, 0x66, 0xe2, 0xbc, 0xb7, 0x83, 0x8a, 0x4a, 0x28, 0xb9, 0x0a,
0xb5, 0x85, 0xf0, 0xe6, 0x0b, 0xd5, 0xab, 0xf4, 0x9d, 0x41, 0x85, 0x59, 0x49, 0x6f, 0x25, 0x0e,
0x57, 0xc1, 0xac, 0x57, 0xc5, 0x69, 0x46, 0x20, 0xd7, 0xa0, 0xa9, 0x3c, 0x5f, 0x48, 0xc5, 0xfd,
0xa8, 0xe7, 0xe2, 0x84, 0x0c, 0xd0, 0x26, 0x69, 0x17, 0xf5, 0x6a, 0x7d, 0x67, 0xb0, 0xcb, 0xf0,
0x9b, 0x0c, 0xa0, 0x3e, 0x31, 0xbe, 0xe9, 0xd5, 0xfb, 0xce, 0xa0, 0x75, 0xd8, 0x39, 0x40, 0x37,
0x1e, 0x58, 0x8f, 0xb1, 0x64, 0x58, 0xaf, 0x2d, 0xbd, 0x79, 0xc0, 0xd5, 0x2a, 0x16, 0xbd, 0x06,
0x9a, 0x95, 0x01, 0x7a, 0x74, 0x25, 0xc5, 0x70, 0x3e, 0x1f, 0x7b, 0xf3, 0x5e, 0xb3, 0xef, 0x0c,
0x1a, 0x2c, 0x03, 0xe8, 0x77, 0x0e, 0xec, 0x65, 0x3e, 0x7e, 0x10, 0xfa, 0xbe, 0xa7, 0xf2, 0xaa,
0x9d, 0xed, 0xaa, 0x6f, 0x00, 0x44, 0xb1, 0x98, 0xe2, 0x34, 0xe3, 0xfc, 0xd6, 0x61, 0xcb, 0x92,
0xb5, 0xe7, 0x59, 0x6e, 0x58, 0x2f, 0xcb, 0xe7, 0x73, 0x0d, 0xa3, 0xcb, 0xb2, 0x65, 0x87, 0x06,
0x65, 0xc9, 0x30, 0xfd, 0xde, 0x81, 0x4b, 0xb9, 0xc8, 0xa3, 0xb2, 0xe0, 0xf3, 0x90, 0x50, 0x70,
0xa5, 0xe2, 0x4a, 0x60, 0x48, 0x5a, 0x87, 0x6d, 0x3b, 0x7f, 0xac, 0x31, 0x66, 0x86, 0xc8, 0x0d,
0x68, 0x44, 0x71, 0x18, 0x85, 0x92, 0x2f, 0xad, 0x9a, 0xae, 0xa5, 0x8d, 0x2c, 0xcc, 0x52, 0x02,
0xb9, 0x09, 0x2e, 0x9a, 0x82, 0xc1, 0x6a, 0x1d, 0x5e, 0xb5, 0xcc, 0x92, 0x6e, 0x66, 0x48, 0xf4,
0x33, 0x68, 0xa2, 0x3c, 0xf6, 0xbe, 0x16, 0x64, 0x1f, 0x1a, 0x3e, 0x3f, 0x3f, 0xba, 0x50, 0x22,
0x49, 0xc5, 0x54, 0xd6, 0xb9, 0xe1, 0xf3, 0xf3, 0xd3, 0x73, 0x69, 0x73, 0xc7, 0x4a, 0x16, 0x7f,
0xc4, 0x65, 0x92, 0x33, 0x46, 0xa2, 0x1f, 0x40, 0xed, 0xf4, 0xfc, 0x2f, 0xae, 0xaa, 0x67, 0xef,
0x14, 0x66, 0xdf, 0x83, 0x16, 0x6e, 0xeb, 0x51, 0x28, 0xa5, 0x17, 0x91, 0x03, 0x20, 0xb8, 0xdd,
0x11, 0x8f, 0x95, 0x5e, 0x33, 0xbf, 0xd8, 0x86, 0x11, 0x3a, 0x80, 0xce, 0x47, 0x67, 0xde, 0x4c,
0x04, 0x53, 0x31, 0xe2, 0x31, 0xf7, 0x13, 0x45, 0xc3, 0xb9, 0xc0, 0x59, 0x46, 0xd1, 0x70, 0x2e,
0xe8, 0xef, 0x0e, 0x74, 0x1f, 0x84, 0x81, 0x14, 0x81, 0x5c, 0x49, 0xcb, 0x3d, 0x80, 0xe6, 0x24,
0xf1, 0x89, 0xcd, 0x96, 0xbd, 0x7c, 0xb6, 0x68, 0x9c, 0x65, 0x14, 0xf2, 0x36, 0xd4, 0x14, 0x9a,
0x6a, 0x63, 0xb8, 0x9b, 0xb8, 0x1c, 0x41, 0x66, 0x07, 0xc9, 0x1d, 0x68, 0x4d, 0x32, 0x9b, 0x6c,
0x20, 0x49, 0x7e, 0x61, 0x33, 0xc2, 0xf2, 0x34, 0x72, 0x0f, 0x3a, 0xa2, 0x60, 0x8a, 0x8d, 0xeb,
0x15, 0x3b, 0xb1, 0x68, 0x27, 0x2b, 0x91, 0xe9, 0x0a, 0x9a, 0xcf, 0x92, 0x43, 0x4e, 0x7a, 0x50,
0xe7, 0x85, 0x52, 0x91, 0x88, 0xda, 0x3d, 0xd1, 0x6a, 0xf2, 0x89, 0xb8, 0x40, 0x13, 0xda, 0xcc,
0x4a, 0xa4, 0x0f, 0xad, 0xb3, 0x50, 0x79, 0xc1, 0x7c, 0x14, 0x3e, 0x17, 0xb1, 0x0d, 0x71, 0x1e,
0xd2, 0xb5, 0x81, 0x4f, 0xa7, 0x2b, 0x1f, 0xb7, 0x55, 0x61, 0x46, 0xa0, 0x01, 0xb4, 0x53, 0xb5,
0x63, 0xa1, 0xc8, 0x2d, 0x80, 0xb4, 0xd6, 0x68, 0xe5, 0x95, 0x9c, 0x4f, 0x53, 0x22, 0xcb, 0x71,
0xc8, 0xcd, 0x24, 0xe7, 0x45, 0x6c, 0xdd, 0xba, 0xce, 0x4f, 0x19, 0xf4, 0xb7, 0x2a, 0xb8, 0x78,
0x64, 0xb4, 0x8d, 0x58, 0x8e, 0xed, 0x41, 0x6f, 0xb2, 0x44, 0x24, 0x03, 0xe8, 0x2e, 0xb9, 0x34,
0xe9, 0x7f, 0x6c, 0xca, 0x9c, 0x49, 0xba, 0x32, 0xac, 0x6b, 0x6b, 0x0a, 0x9d, 0x86, 0x8a, 0x2f,
0x4f, 0xcf, 0xad, 0xe9, 0x6b, 0x38, 0xb9, 0x05, 0xad, 0x14, 0x3b, 0x79, 0x68, 0x83, 0x53, 0x2e,
0x2e, 0x79, 0x0a, 0x79, 0x0b, 0x76, 0xb3, 0x55, 0x3c, 0x5f, 0xd8, 0xda, 0x59, 0x04, 0xc9, 0xed,
0x82, 0xc7, 0x6a, 0xb8, 0xec, 0xa5, 0xb2, 0x07, 0xc6, 0x42, 0x15, 0x9c, 0x76, 0x17, 0x3a, 0x7a,
0x95, 0x67, 0xd9, 0xc4, 0xfa, 0x8b, 0x27, 0x96, 0xa8, 0xe4, 0x3e, 0xbc, 0xa1, 0x11, 0xe3, 0x83,
0x0c, 0x7f, 0xb0, 0xe0, 0xc1, 0x5c, 0xcc, 0xb0, 0x0a, 0x57, 0xd8, 0x36, 0x0a, 0xb9, 0x0f, 0xdd,
0x69, 0xf1, 0x2c, 0x61, 0x75, 0xce, 0x8a, 0x50, 0xe9, 0xa4, 0xb1, 0x32, 0x9d, 0x7c, 0x0c, 0xfd,
0x4c, 0x41, 0x89, 0x9d, 0x6c, 0x04, 0x70, 0x23, 0x2f, 0xe5, 0x25, 0xf1, 0x66, 0x42, 0xae, 0x96,
0x4a, 0x1e, 0xeb, 0x9b, 0xb8, 0x85, 0xc9, 0x5d, 0x86, 0xf1, 0x5c, 0x44, 0x11, 0x32, 0xda, 0xf6,
0x5c, 0x18, 0x91, 0xfe, 0x52, 0x81, 0x2b, 0xa5, 0xca, 0x79, 0x2c, 0xf8, 0x4c, 0xc4, 0x5b, 0xf2,
0x2c, 0xbb, 0x45, 0x77, 0x36, 0xdf, 0xa2, 0x26, 0x95, 0xec, 0x2d, 0xaa, 0xef, 0x49, 0x9d, 0x04,
0xe6, 0xf8, 0xe0, 0xb7, 0x5e, 0x21, 0x58, 0xf9, 0xba, 0xd6, 0x9a, 0xd4, 0xb0, 0x52, 0x39, 0xd7,
0x6a, 0x2f, 0xcf, 0xb5, 0x7d, 0x68, 0x28, 0x93, 0xa8, 0x26, 0x15, 0x2a, 0x2c, 0x95, 0x75, 0x57,
0xa0, 0xa9, 0xe6, 0x82, 0x44, 0xe3, 0xcd, 0x45, 0x5b, 0x42, 0x0b, 0xdd, 0x83, 0x71, 0x63, 0xd3,
0xf0, 0x8a, 0xa8, 0xce, 0xeb, 0x34, 0x9c, 0x48, 0x03, 0xa4, 0x15, 0xc1, 0xbc, 0xaf, 0x5b, 0x05,
0x5f, 0x6f, 0x8a, 0x57, 0x7b, 0x73, 0xbc, 0x28, 0xb4, 0x93, 0x93, 0xaf, 0x5b, 0x9c, 0xde, 0x2e,
0xd2, 0x0a, 0x18, 0xfd, 0xd1, 0x81, 0x6e, 0x29, 0x72, 0xe4, 0x8e, 0x8e, 0x8c, 0x8e, 0x9e, 0xad,
0xea, 0xd7, 0x36, 0xdf, 0x8d, 0x26, 0xc2, 0xcc, 0x72, 0x49, 0x1f, 0xaa, 0x33, 0xae, 0x78, 0xe9,
0x82, 0x36, 0xb7, 0x28, 0x8e, 0x90, 0xf7, 0x01, 0x32, 0x9f, 0xd9, 0x12, 0xf0, 0xda, 0xda, 0xda,
0x66, 0x98, 0xe5, 0xa8, 0xf4, 0x0f, 0x07, 0x1a, 0xc9, 0x15, 0x9e, 0xcb, 0x1b, 0x67, 0x73, 0xde,
0xec, 0xbc, 0xb0, 0xfb, 0xaa, 0x94, 0xbb, 0xaf, 0x7d, 0x68, 0x8c, 0x9e, 0x3e, 0x66, 0xb9, 0xa6,
0x2d, 0x95, 0xc9, 0x01, 0xc0, 0xe8, 0xe9, 0xe3, 0x24, 0x89, 0xdc, 0x8d, 0x49, 0x94, 0x63, 0x14,
0x7b, 0xb1, 0xda, 0x86, 0x5e, 0x0c, 0xaf, 0x2b, 0xec, 0x74, 0xeb, 0x66, 0x34, 0x05, 0xe8, 0xcf,
0x0e, 0x74, 0x3f, 0x15, 0xcf, 0x51, 0xf1, 0x58, 0x89, 0xe8, 0x89, 0x9c, 0xbf, 0xa2, 0x9d, 0x04,
0xaa, 0x52, 0x09, 0x63, 0xa2, 0xcb, 0xf0, 0x9b, 0xdc, 0x81, 0x2b, 0x52, 0x4c, 0xc3, 0x60, 0x26,
0xc7, 0x5e, 0x30, 0x15, 0x63, 0xc5, 0x63, 0x75, 0x9a, 0x1c, 0x22, 0x97, 0x6d, 0x1e, 0x4c, 0xf2,
0xcb, 0x86, 0x01, 0x35, 0xb9, 0xc8, 0x2f, 0xc3, 0xf4, 0x39, 0xec, 0x62, 0x71, 0x43, 0x0f, 0xbc,
0xfa, 0x96, 0x0b, 0x2e, 0xa9, 0x94, 0x5c, 0xa2, 0x43, 0xe3, 0xc9, 0x5c, 0xaa, 0x34, 0x58, 0x2a,
0xd3, 0x6f, 0x1d, 0xe8, 0x24, 0xf9, 0x30, 0x7a, 0xfa, 0x78, 0x9b, 0xea, 0xeb, 0xb0, 0x17, 0x65,
0x4c, 0x96, 0xdb, 0xc5, 0x1a, 0x4e, 0xee, 0x42, 0x2b, 0x87, 0xd9, 0xce, 0xe3, 0xf5, 0xf5, 0xe4,
0xb7, 0xcf, 0x11, 0x96, 0x67, 0xd3, 0x19, 0xc0, 0x31, 0x97, 0xba, 0x87, 0xfd, 0x5b, 0xc1, 0xc3,
0x47, 0x80, 0x0d, 0x1e, 0x3e, 0x02, 0x2e, 0x83, 0xeb, 0xe1, 0x1b, 0xc4, 0x3e, 0x26, 0x50, 0xa0,
0xdf, 0x40, 0x57, 0xab, 0x18, 0x0b, 0xf5, 0x84, 0x7f, 0x71, 0x78, 0xfb, 0x9f, 0x51, 0x95, 0x6b,
0xfa, 0xab, 0x5b, 0x9b, 0x7e, 0xfa, 0x93, 0x03, 0x1d, 0xab, 0xff, 0xc8, 0x53, 0xf2, 0x5f, 0x56,
0x4f, 0xde, 0x03, 0xf7, 0x2c, 0xd4, 0x2d, 0xad, 0xfb, 0xb2, 0xd0, 0x18, 0x1e, 0xfd, 0xd5, 0x81,
0xe6, 0xb1, 0xe0, 0xb1, 0x9a, 0x08, 0xae, 0xfe, 0x03, 0x6f, 0xc1, 0x7d, 0x68, 0x48, 0xf1, 0xd5,
0x4a, 0x37, 0x9e, 0xf6, 0x50, 0xa5, 0xf2, 0xf6, 0xfa, 0x41, 0xdf, 0x85, 0xe6, 0x89, 0x3c, 0x16,
0x7c, 0xa9, 0x16, 0x17, 0x9a, 0xea, 0x25, 0x02, 0x5a, 0xd0, 0x60, 0x19, 0xa0, 0xdf, 0xbe, 0x75,
0xfb, 0xae, 0x7a, 0x25, 0x93, 0x87, 0x39, 0x93, 0xd1, 0x8b, 0xb6, 0x94, 0x6f, 0x71, 0x73, 0x69,
0xc2, 0xff, 0xe5, 0x65, 0x3c, 0xa9, 0xe1, 0x4f, 0x88, 0xdb, 0x7f, 0x06, 0x00, 0x00, 0xff, 0xff,
0xfd, 0xb0, 0x20, 0xb2, 0xb1, 0x10, 0x00, 0x00,
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment