Unverified Commit 8c4040cc authored by vipwzw's avatar vipwzw Committed by GitHub

Merge pull request #932 from bysomeone/add-dapp-vote

Add dapp vote
parents 48909645 03270ff2
......@@ -48,7 +48,7 @@ for y in $objects; do
# find the objects location in the repository tree
other=$(echo "${allObjects}" | grep "$sha")
#lineBreak=`echo -e "\n"`
output="${output}\n${size},${compressedSize},${other}"
output="${output}\\n${size},${compressedSize},${other}"
done
echo -e "$output" | column -t -s ', '
......
......@@ -24,13 +24,16 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETFUpAzWW2ep1Y=
github.com/NebulousLabs/Sia v1.3.7 h1:gYfYnXVyeaEzyyVwjpQjszBcENNZ8DPIJc/pgOiLuGA=
github.com/NebulousLabs/Sia v1.3.7/go.mod h1:SCASk6mV8QdEojKyecjj/Jd0OGSXkZonkhow7XXKk6Q=
github.com/NebulousLabs/errors v0.0.0-20181203160057-9f787ce8f69e h1:9UwdEr0AFI021vXG+hXDGH9ZHGuNtPCdJkRTqcIhJ3A=
github.com/NebulousLabs/errors v0.0.0-20181203160057-9f787ce8f69e/go.mod h1:J7tUI9Fg4YuFLsqeLE5uIp93Fot9oBCw2vwZJvLmWso=
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e h1:n+DcnTNkQnHlwpsrHoQtkrJIO7CBx029fw6oR4vIob4=
github.com/NebulousLabs/fastrand v0.0.0-20181203155948-6fb6489aac4e/go.mod h1:Bdzq+51GR4/0DIhaICZEOm+OHvXGwwB2trKZ8B4Y6eQ=
github.com/NebulousLabs/merkletree v0.0.0-20181203152040-08d5d54b07f5 h1:pk9SclNGplPbF6YDIDKMhHh9SaUWcoxPkMr7zdu1hfk=
github.com/NebulousLabs/merkletree v0.0.0-20181203152040-08d5d54b07f5/go.mod h1:Cn056wBLKay+uIS9LJn7ymwhgC5mqbOtG6iOhEvyy4M=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.5 h1:zl/OfRA6nftbBK9qTohYBJ5xvw6C/oNKizR7cZGl3cI=
github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/VictoriaMetrics/fastcache v1.5.3 h1:2odJnXLbFZcoV9KYtQ+7TH1UOq3dn3AssMgieaezkR4=
github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE=
......@@ -41,6 +44,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5Vpd
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d h1:b/FqDLjWXDQI6XBYvWDVgEKv3xOTs68qRkuqyU37lBc=
github.com/apache/thrift v0.0.0-20171203172758-327ebb6c2b6d/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
......@@ -54,6 +58,7 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
github.com/btcsuite/btcd v0.0.0-20190109040709-5bda5314ca95/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
......@@ -77,6 +82,7 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtE
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 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk=
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
......@@ -140,16 +146,22 @@ github.com/ethereum/go-ethereum v1.8.20/go.mod h1:PwpWDrCLZrV+tfrhqqF6kPknbISMHa
github.com/ethereum/go-ethereum v1.9.9 h1:jnoBvjH8aMH++iH14XmiJdAsnRcmZUM+B5fsnEZBVE0=
github.com/ethereum/go-ethereum v1.9.9/go.mod h1:a9TqabFudpDu1nucId+k9S8R9whYaHnGBLKFouA5EAo=
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc h1:jtW8jbpkO4YirRSyepBOH8E+2HEw6/hKkBvFPwhUN8c=
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-interpreter/wagon v0.6.0 h1:BBxDxjiJiHgw9EdkYXAWs8NHhwnazZ5P2EWBW5hFNWw=
github.com/go-interpreter/wagon v0.6.0/go.mod h1:5+b/MBYkclRZngKF5s6qrgWxSLgE9F5dFdO1hAueZLc=
github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
......@@ -162,6 +174,7 @@ github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3 h1:
github.com/golang-collections/collections v0.0.0-20130729185459-604e922904d3/go.mod h1:nPpo7qLxd6XL3hWJG/O60sR8ZKfMCiIoNap5GvD12KU=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
......@@ -179,6 +192,7 @@ github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf h1:gFVkHXmVAhEbxZV
github.com/golang/snappy v0.0.2-0.20190904063534-ff6b7dc882cf/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/gopacket v1.1.17 h1:rMrlX2ZY2UbvT+sdz3+6J+pp2z+msCq9MxTU6ymxbBY=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
......@@ -206,6 +220,7 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/holiman/uint256 v1.1.1 h1:4JywC80b+/hSfljFlEBLHrrh+CIONLDz9NuFl0af4Mw=
github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huin/goupnp v0.0.0-20161224104101-679507af18f3/go.mod h1:MZ2ZmwcBpvOoJ22IJsc7va19ZwoheaBk43rKg12SKag=
github.com/huin/goupnp v1.0.0 h1:wg75sLpL6DZqwHQN6E1Cfk6mtfzS45z8OV+ic+DtHRo=
......@@ -231,6 +246,7 @@ github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRV
github.com/ipfs/go-datastore v0.4.1/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-datastore v0.4.4 h1:rjvQ9+muFaJ+QZ7dN5B1MSDNQ0JVZKkkES/rMZmA8X8=
github.com/ipfs/go-datastore v0.4.4/go.mod h1:SX/xMIKoCszPqp+z9JhPYCmoOoXTvaa13XEbGtsFUhA=
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ds-badger v0.0.2/go.mod h1:Y3QpeSFWQf6MopLTiZD+VT6IC1yZqaGmjvRcKeSGij8=
github.com/ipfs/go-ds-badger v0.0.5/go.mod h1:g5AuuCGmr7efyzQhLL8MzwqcauPojGPUaHzfGTzuE3s=
......@@ -262,6 +278,7 @@ github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
github.com/jbenet/go-cienv v0.0.0-20150120210510-1bb1476777ec/go.mod h1:rGaEvXB4uRSZMmzKNLoXvTu1sfx+1kv/DojUlPrSZGs=
github.com/jbenet/go-cienv v0.1.0 h1:Vc/s0QbQtoxX8MwwSLWWh+xNNZvM3Lw7NsTcHrvvhMc=
github.com/jbenet/go-cienv v0.1.0/go.mod h1:TqNnHUmJgXau0nCzC7kXWeotg3J9W34CUv5Djy1+FlA=
github.com/jbenet/go-temp-err-catcher v0.0.0-20150120210811-aac704a3f4f2/go.mod h1:8GXXJV31xl8whumTzdZsTt3RnUIiPqzkyf7mxToRCMs=
github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk=
......@@ -288,13 +305,17 @@ github.com/klauspost/compress v1.8.2 h1:Bx0qjetmNjdFXASH02NSAREKpiaDwkO1DRZ3dV2K
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d h1:68u9r4wEvL3gYg2jvAOgROwZ3H+Y3hIDk4tbbmIjcYQ=
github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/libp2p/go-addr-util v0.0.1/go.mod h1:4ac6O7n9rIAKB1dnd+s8IbbMXkt+oBpzX4/+RACcnlQ=
......@@ -364,6 +385,7 @@ github.com/libp2p/go-libp2p-mplex v0.2.4/go.mod h1:mI7iOezdWFOisvUwaYd3IDrJ4oVmg
github.com/libp2p/go-libp2p-nat v0.0.5/go.mod h1:1qubaE5bTZMJE+E/uu2URroMbzdubFz1ChgiN79yKPE=
github.com/libp2p/go-libp2p-nat v0.0.6 h1:wMWis3kYynCbHoyKLPBEMu4YRLltbm8Mk08HGSfvTkU=
github.com/libp2p/go-libp2p-nat v0.0.6/go.mod h1:iV59LVhB3IkFvS6S6sauVTSOrNEANnINbI/fkaLimiw=
github.com/libp2p/go-libp2p-netutil v0.1.0 h1:zscYDNVEcGxyUpMd0JReUZTrpMfia8PmLKcKF72EAMQ=
github.com/libp2p/go-libp2p-netutil v0.1.0/go.mod h1:3Qv/aDqtMLTUyQeundkKsA+YCThNdbQD54k3TqjpbFU=
github.com/libp2p/go-libp2p-peer v0.0.1/go.mod h1:nXQvOBbwVqoP+T5Y5nCjeH4sP9IX/J0AMzcDUVruVoo=
github.com/libp2p/go-libp2p-peer v0.2.0/go.mod h1:RCffaCvUyW2CJmG2gAWVqwePwW7JMgxjsHm7+J5kjWY=
......@@ -425,6 +447,7 @@ github.com/libp2p/go-openssl v0.0.2/go.mod h1:v8Zw2ijCSWBQi8Pq5GAixw6DbFfa9u6VIY
github.com/libp2p/go-openssl v0.0.3/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/libp2p/go-openssl v0.0.4/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/libp2p/go-openssl v0.0.5/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/libp2p/go-openssl v0.0.7 h1:eCAzdLejcNVBzP/iZM9vqHnQm+XyCEbSSIheIPRGNsw=
github.com/libp2p/go-openssl v0.0.7/go.mod h1:unDrJpgy3oFr+rqXsarWifmJuNnJR4chtO1HmaZjggc=
github.com/libp2p/go-reuseport v0.0.1 h1:7PhkfH73VXfPJYKQ6JwS5I/eVcoyYi9IMNGc6FWpFLw=
github.com/libp2p/go-reuseport v0.0.1/go.mod h1:jn6RmB1ufnQwl0Q1f+YxAj8isJgDCQzaaxIFYDhcYEA=
......@@ -550,10 +573,12 @@ github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
......@@ -609,6 +634,7 @@ github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smola/gocompat v0.2.0/go.mod h1:1B0MlxbmoZNo3h8guHp8HztB3BSYR5itql9qtVc0ypY=
github.com/spacemonkeygo/openssl v0.0.0-20181017203307-c2dcc5cca94a/go.mod h1:7AyxJNCJ7SBZ1MfVQCWD6Uqo2oubI2Eq2y2eqf+A5r0=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.0.1-0.20190317074736-539464a789e9/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
......@@ -644,6 +670,7 @@ github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJ
github.com/tjfoc/gmsm v1.3.1/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc h1:RTUQlKzoZZVG3umWNzOYeFecQLIh+dbxXvJp1zPQJTI=
github.com/twitchyliquid64/golang-asm v0.0.0-20190126203739-365674df15fc/go.mod h1:NoCfSFWosfqMqmmD7hApkirIK9ozpHjxRnRxs1l413A=
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=
......@@ -684,10 +711,12 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/goleak v1.0.0 h1:qsup4IcBdlmsnGfqyLl4Ntn3C2XCCuKAE7DwHpScyUo=
go.uber.org/goleak v1.0.0/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.14.1/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
......@@ -717,10 +746,12 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
......@@ -748,6 +779,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a h1:WXEvlFVvvGxCJLG6REjsT03iWnKLEWinaScsxF2Vm2o=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
......@@ -796,6 +828,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200615222825-6aa8f57aacd9 h1:cwgUY+1ja2qxWb2dyaCoixaA66WGWmrijSlxaM+JM/g=
golang.org/x/tools v0.0.0-20200615222825-6aa8f57aacd9/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
......@@ -803,6 +836,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IV
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0 h1:Tfd7cKwKbFRsI8RMAD3oqqw7JPFRrvFlOsfbgVkjOOw=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
......@@ -823,29 +857,37 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQ
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/webhooks.v5 v5.2.0/go.mod h1:LZbya/qLVdbqDR1aKrGuWV6qbia2zCYSR5dpom2SInQ=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3 h1:AFxeG48hTWHhDTQDk/m2gorfVHUEa9vo3tp3D7TzwjI=
gopkg.in/natefinch/lumberjack.v2 v2.0.0-20170531160350-a96e63847dc3/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772 h1:hhsSf/5z74Ck/DJYc+R8zpq8KGm7uJvpdLRQED/IedA=
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20190213234257-ec84240a7772/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/src-d/go-cli.v0 v0.0.0-20181105080154-d492247bbc0d/go.mod h1:z+K8VcOYVYcSwSjGebuDL6176A1XskgbtNl64NSg+n8=
gopkg.in/src-d/go-log.v1 v1.0.1/go.mod h1:GN34hKP0g305ysm2/hctJ0Y8nWP3zxXXJ8GFabTyABE=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
......@@ -30,6 +30,7 @@ import (
_ "github.com/33cn/plugin/plugin/dapp/trade" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/unfreeze" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/valnode" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/vote" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/wasm" //auto gen
_ "github.com/33cn/plugin/plugin/dapp/x2ethereum" //auto gen
)
all:
bash build.sh $(OUT) $(FLAG)
#!/bin/bash
# 官方ci集成脚本
strpwd=$(pwd)
strcmd=${strpwd##*dapp/}
strapp=${strcmd%/cmd*}
OUT_DIR="${1}/$strapp"
#FLAG=$2
mkdir -p "${OUT_DIR}"
cp ./build/* "${OUT_DIR}"
/*Package commands implement dapp client commands*/
package commands
import (
jsonrpc "github.com/33cn/chain33/rpc/jsonclient"
rpctypes "github.com/33cn/chain33/rpc/types"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/spf13/cobra"
)
/*
* 实现合约对应客户端
*/
// Cmd vote client command
func Cmd() *cobra.Command {
cmd := &cobra.Command{
Use: "vote",
Short: "vote command",
Args: cobra.MinimumNArgs(1),
}
cmd.AddCommand(
//create tx
createGroupCMD(),
updateGroupCMD(),
createVoteCMD(),
commitVoteCMD(),
closeVoteCMD(),
updateMemberCMD(),
//query rpc
groupInfoCMD(),
voteInfoCMD(),
memberInfoCMD(),
listGroupCMD(),
listVoteCMD(),
listMemberCMD(),
)
return cmd
}
func markRequired(cmd *cobra.Command, params ...string) {
for _, param := range params {
_ = cmd.MarkFlagRequired(param)
}
}
func sendCreateTxRPC(cmd *cobra.Command, actionName string, req types.Message) {
title, _ := cmd.Flags().GetString("title")
cfg := types.GetCliSysParam(title)
rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
payLoad := types.MustPBToJSON(req)
pm := &rpctypes.CreateTxIn{
Execer: cfg.ExecName(vty.VoteX),
ActionName: actionName,
Payload: payLoad,
}
var res string
ctx := jsonrpc.NewRPCCtx(rpcAddr, "Chain33.CreateTransaction", pm, &res)
ctx.RunWithoutMarshal()
}
func sendQueryRPC(cmd *cobra.Command, funcName string, req, reply types.Message) {
title, _ := cmd.Flags().GetString("title")
cfg := types.GetCliSysParam(title)
rpcAddr, _ := cmd.Flags().GetString("rpc_laddr")
payLoad := types.MustPBToJSON(req)
query := &rpctypes.Query4Jrpc{
Execer: cfg.ExecName(vty.VoteX),
FuncName: funcName,
Payload: payLoad,
}
ctx := jsonrpc.NewRPCCtx(rpcAddr, "Chain33.Query", query, reply)
ctx.Run()
}
package commands
import (
"fmt"
"os"
"time"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/spf13/cobra"
)
func createGroupCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "createGroup",
Aliases: []string{"cg"},
Short: "create tx(create vote group)",
Run: createGroup,
Example: "createGroup -n=group1 -a=admin1 -m=member1 -m=member2",
}
createGroupFlags(cmd)
return cmd
}
func createGroupFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "group name")
cmd.Flags().StringArrayP("admins", "a", nil, "group admin address array, default set creator as admin")
cmd.Flags().StringArrayP("members", "m", nil, "group member address array")
cmd.Flags().UintSliceP("weights", "w", nil, "member vote weight array")
cmd.Flags().StringArrayP("nicks", "c", nil, "group member nick name array")
markRequired(cmd, "name")
}
func formatAddMembers(addrs, nickNames []string, voteWeights []uint) []*vty.GroupMember {
if len(voteWeights) == 0 {
voteWeights = make([]uint, len(addrs))
}
if len(voteWeights) != len(voteWeights) {
fmt.Fprintf(os.Stderr, "member address array length should equal with vote weight array length")
return nil
}
if len(nickNames) == 0 {
nickNames = make([]string, len(addrs))
}
if len(nickNames) != len(addrs) {
fmt.Fprintf(os.Stderr, "member nick name array, add member addr array should have same length")
return nil
}
members := make([]*vty.GroupMember, 0)
for i, addr := range addrs {
members = append(members, &vty.GroupMember{Addr: addr, VoteWeight: uint32(voteWeights[i]), NickName: nickNames[i]})
}
return members
}
func createGroup(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
admins, _ := cmd.Flags().GetStringArray("admins")
memberAddrs, _ := cmd.Flags().GetStringArray("members")
weights, _ := cmd.Flags().GetUintSlice("weights")
nicks, _ := cmd.Flags().GetStringArray("nicks")
if name == "" {
fmt.Fprintf(os.Stderr, "ErrNilGroupName")
return
}
members := formatAddMembers(memberAddrs, nicks, weights)
if members == nil {
return
}
params := &vty.CreateGroup{
Name: name,
Admins: admins,
Members: members,
}
sendCreateTxRPC(cmd, vty.NameCreateGroupAction, params)
}
func updateGroupCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "updateGroup",
Aliases: []string{"ug"},
Short: "create tx(update group members or admin)",
Run: updateGroup,
Example: "updateGroup -g=id -a=addMember1 -a=addMember2 -r=removeMember1 ...",
}
updateGroupFlags(cmd)
return cmd
}
func updateGroupFlags(cmd *cobra.Command) {
cmd.Flags().StringP("groupID", "g", "", "group id")
cmd.Flags().StringArrayP("addMembers", "m", nil, "group member address array for adding")
cmd.Flags().UintSliceP("weights", "w", nil, "member vote weight array for adding")
cmd.Flags().StringArrayP("nicks", "c", nil, "group member nick name array")
cmd.Flags().StringArrayP("removeMembers", "v", nil, "group member address array for removing")
cmd.Flags().StringArrayP("addAdmins", "a", nil, "group admin address array for adding")
cmd.Flags().StringArrayP("removeAdmins", "r", nil, "group admin address array for removing")
markRequired(cmd, "groupID")
}
func updateGroup(cmd *cobra.Command, args []string) {
groupID, _ := cmd.Flags().GetString("groupID")
addAddrs, _ := cmd.Flags().GetStringArray("addMembers")
weights, _ := cmd.Flags().GetUintSlice("weights")
nicks, _ := cmd.Flags().GetStringArray("nicks")
removeAddrs, _ := cmd.Flags().GetStringArray("removeMembers")
addAdmins, _ := cmd.Flags().GetStringArray("addAdmins")
removeAdmins, _ := cmd.Flags().GetStringArray("removeAdmins")
if groupID == "" {
fmt.Fprintf(os.Stderr, "ErrNilGroupID")
return
}
addMembers := formatAddMembers(addAddrs, nicks, weights)
if addMembers == nil {
return
}
params := &vty.UpdateGroup{
GroupID: groupID,
RemoveMembers: removeAddrs,
AddMembers: addMembers,
AddAdmins: addAdmins,
RemoveAdmins: removeAdmins,
}
sendCreateTxRPC(cmd, vty.NameUpdateGroupAction, params)
}
func createVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "createVote",
Aliases: []string{"ctv"},
Short: "create tx(create vote)",
Run: createVote,
}
createVoteFlags(cmd)
return cmd
}
func createVoteFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "vote name")
cmd.Flags().StringP("groupID", "g", "", "belonging group id")
cmd.Flags().StringArrayP("options", "o", nil, "vote option array")
cmd.Flags().Int64P("beginTime", "b", 0, "vote begin unix timestamp, default set current time")
cmd.Flags().StringP("duration", "d", "24h", "vote duration time, such as(10s, 10m, 10h), default 24h")
markRequired(cmd, "name", "groupID", "options")
}
func createVote(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
groupID, _ := cmd.Flags().GetString("groupID")
options, _ := cmd.Flags().GetStringArray("options")
beginTime, _ := cmd.Flags().GetInt64("beginTime")
duration, _ := cmd.Flags().GetString("duration")
if name == "" {
fmt.Fprintf(os.Stderr, "ErrNilVoteName")
return
}
if len(groupID) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilGroupID")
return
}
if len(options) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilOptions")
return
}
if beginTime == 0 {
beginTime = types.Now().Unix()
}
dt, err := time.ParseDuration(duration)
if err != nil {
fmt.Fprintf(os.Stderr, "InvalidDurationTime:"+err.Error())
return
}
endTime := beginTime + int64(dt/time.Second)
params := &vty.CreateVote{
Name: name,
GroupID: groupID,
VoteOptions: options,
BeginTimestamp: beginTime,
EndTimestamp: endTime,
}
sendCreateTxRPC(cmd, vty.NameCreateVoteAction, params)
}
func commitVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "commitVote",
Aliases: []string{"cmv"},
Short: "create tx(commit vote)",
Run: commitVote,
}
commitVoteFlags(cmd)
return cmd
}
func commitVoteFlags(cmd *cobra.Command) {
cmd.Flags().StringP("voteID", "v", "", "vote id")
cmd.Flags().Uint32P("optionIndex", "o", 0, "voting option index in option array")
markRequired(cmd, "voteID", "optionIndex")
}
func commitVote(cmd *cobra.Command, args []string) {
voteID, _ := cmd.Flags().GetString("voteID")
optionIndex, _ := cmd.Flags().GetUint32("optionIndex")
if voteID == "" {
fmt.Fprintf(os.Stderr, "ErrNilVoteID")
return
}
params := &vty.CommitVote{
VoteID: voteID,
OptionIndex: optionIndex,
}
sendCreateTxRPC(cmd, vty.NameCommitVoteAction, params)
}
func closeVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "closeVote",
Aliases: []string{"csv"},
Short: "create tx(close vote)",
Run: closeVote,
}
closeVoteFlags(cmd)
return cmd
}
func closeVoteFlags(cmd *cobra.Command) {
cmd.Flags().StringP("voteID", "v", "", "vote id")
markRequired(cmd, "voteID")
}
func closeVote(cmd *cobra.Command, args []string) {
voteID, _ := cmd.Flags().GetString("voteID")
if voteID == "" {
fmt.Fprintf(os.Stderr, "ErrNilVoteID")
return
}
params := &vty.CloseVote{
VoteID: voteID,
}
sendCreateTxRPC(cmd, vty.NameCloseVoteAction, params)
}
func updateMemberCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "updateMember",
Aliases: []string{"um"},
Short: "create tx(update member name)",
Run: updateMember,
}
updateMemberFlags(cmd)
return cmd
}
func updateMemberFlags(cmd *cobra.Command) {
cmd.Flags().StringP("name", "n", "", "member name")
markRequired(cmd, "name")
}
func updateMember(cmd *cobra.Command, args []string) {
name, _ := cmd.Flags().GetString("name")
if name == "" {
fmt.Fprintf(os.Stderr, "ErrNilMemberName")
return
}
params := &vty.UpdateMember{
Name: name,
}
sendCreateTxRPC(cmd, vty.NameUpdateMemberAction, params)
}
package commands
import (
"fmt"
"os"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/spf13/cobra"
)
func groupInfoCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "groupInfo",
Aliases: []string{"gf"},
Short: "get group infos",
Run: groupInfo,
Example: "groupInfo -g=id1 -g=id2...",
}
groupInfoFlags(cmd)
return cmd
}
func groupInfoFlags(cmd *cobra.Command) {
cmd.Flags().StringArrayP("groupIDs", "g", nil, "group id array")
markRequired(cmd, "groupIDs")
}
func groupInfo(cmd *cobra.Command, args []string) {
groupIDs, _ := cmd.Flags().GetStringArray("groupIDs")
if len(groupIDs) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilGroupIDs")
return
}
params := &vty.ReqStrings{
Items: groupIDs,
}
info := &vty.GroupInfos{}
sendQueryRPC(cmd, "GetGroups", params, info)
}
func voteInfoCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "voteInfo",
Aliases: []string{"vf"},
Short: "get vote info",
Run: voteInfo,
}
voteInfoFlags(cmd)
return cmd
}
func voteInfoFlags(cmd *cobra.Command) {
cmd.Flags().StringArrayP("voteIDs", "v", nil, "vote id array")
markRequired(cmd, "voteID")
}
func voteInfo(cmd *cobra.Command, args []string) {
voteIDs, _ := cmd.Flags().GetStringArray("voteIDs")
if len(voteIDs) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilVoteID")
return
}
params := &vty.ReqStrings{
Items: voteIDs,
}
info := &vty.ReplyVoteList{}
sendQueryRPC(cmd, "GetVotes", params, info)
}
func memberInfoCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "memberInfo",
Aliases: []string{"mf"},
Short: "get member info",
Run: memberInfo,
}
memberInfoFlags(cmd)
return cmd
}
func memberInfoFlags(cmd *cobra.Command) {
cmd.Flags().StringArrayP("addrs", "a", nil, "member address array")
markRequired(cmd, "addr")
}
func memberInfo(cmd *cobra.Command, args []string) {
addrs, _ := cmd.Flags().GetStringArray("addrs")
if len(addrs) == 0 {
fmt.Fprintf(os.Stderr, "ErrNilAddress")
return
}
params := &vty.ReqStrings{
Items: addrs,
}
info := &vty.MemberInfos{}
sendQueryRPC(cmd, "GetMembers", params, info)
}
func listGroupCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "listGroup",
Aliases: []string{"lg"},
Short: "show group list",
Run: listGroup,
}
listCmdFlags(cmd)
return cmd
}
func listGroup(cmd *cobra.Command, args []string) {
runListCMD(cmd, "ListGroup", &vty.GroupInfos{})
}
func listVoteCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "listVote",
Aliases: []string{"lv"},
Short: "show vote list",
Run: listVote,
}
listVoteFlags(cmd)
return cmd
}
func listVoteFlags(cmd *cobra.Command) {
cmd.Flags().StringP("groupID", "g", "", "list vote belongs to specified group, list all if not set")
cmd.Flags().Uint32P("status", "t", 0, "vote status")
listCmdFlags(cmd)
}
func listVote(cmd *cobra.Command, args []string) {
groupID, _ := cmd.Flags().GetString("groupID")
status, _ := cmd.Flags().GetUint32("status")
listReq := getListReq(cmd)
req := &vty.ReqListVote{
GroupID: groupID,
ListReq: listReq,
Status: status,
}
sendQueryRPC(cmd, "ListVote", req, &vty.ReplyVoteList{})
}
func listMemberCMD() *cobra.Command {
cmd := &cobra.Command{
Use: "listMember",
Aliases: []string{"lm"},
Short: "show member list",
Run: listMember,
}
listCmdFlags(cmd)
return cmd
}
func listMember(cmd *cobra.Command, args []string) {
runListCMD(cmd, "ListMember", &vty.MemberInfos{})
}
func listCmdFlags(cmd *cobra.Command) {
cmd.Flags().StringP("startItem", "s", "", "list start item id, default nil value")
cmd.Flags().Uint32P("count", "c", 5, "list count, default 5")
cmd.Flags().Uint32P("direction", "d", 1, "list direction, default 1 (Ascending order)")
}
func runListCMD(cmd *cobra.Command, funcName string, reply types.Message) {
req := getListReq(cmd)
sendQueryRPC(cmd, funcName, req, reply)
}
func getListReq(cmd *cobra.Command) *vty.ReqListItem {
startID, _ := cmd.Flags().GetString("startItem")
count, _ := cmd.Flags().GetUint32("count")
direction, _ := cmd.Flags().GetUint32("direction")
req := &vty.ReqListItem{
StartItemID: startID,
Count: int32(count),
Direction: int32(direction),
}
return req
}
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/system/dapp"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
type action struct {
db dbm.KV
txHash []byte
fromAddr string
blockTime int64
height int64
index int
}
func newAction(v *vote, tx *types.Transaction, index int) *action {
return &action{db: v.GetStateDB(),
txHash: tx.Hash(),
fromAddr: tx.From(),
blockTime: v.GetBlockTime(),
height: v.GetHeight(),
index: index}
}
func (a *action) getGroupInfo(groupID string) (*vty.GroupInfo, error) {
info := &vty.GroupInfo{}
err := readStateDB(a.db, formatStateIDKey(groupID), info)
if err == types.ErrNotFound {
err = errGroupNotExist
}
return info, err
}
func (a *action) getVoteInfo(voteID string) (*vty.VoteInfo, error) {
info := &vty.VoteInfo{}
err := readStateDB(a.db, formatStateIDKey(voteID), info)
if err == types.ErrNotFound {
err = errVoteNotExist
}
return info, err
}
func (a *action) createGroup(create *vty.CreateGroup) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
group := &vty.GroupInfo{}
group.Name = create.Name
group.ID = formatGroupID(dapp.HeightIndexStr(a.height, int64(a.index)))
//添加创建者作为默认管理员
group.Admins = append(group.Admins, a.fromAddr)
for _, addr := range create.GetAdmins() {
if addr != a.fromAddr {
group.Admins = append(group.Admins, addr)
}
}
group.Members = create.Members
// set default vote weight
for _, member := range group.Members {
if member.VoteWeight < 1 {
member.VoteWeight = 1
}
}
group.MemberNum = uint32(len(group.Members))
group.Creator = a.fromAddr
group.Description = create.GetDescription()
groupValue := types.Encode(group)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(group.ID), Value: groupValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCreateGroupLog, Log: groupValue})
return receipt, nil
}
func (a *action) updateGroup(update *vty.UpdateGroup) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
group, err := a.getGroupInfo(update.GroupID)
if err != nil {
elog.Error("vote exec updateGroup", "txHash", a.txHash, "err", err)
return nil, errStateDBGet
}
addrMap := make(map[string]int)
for index, member := range group.Members {
addrMap[member.Addr] = index
}
// remove members
for _, addr := range update.GetRemoveMembers() {
if index, ok := addrMap[addr]; ok {
group.Members = append(group.Members[:index], group.Members[index+1:]...)
delete(addrMap, addr)
}
}
// add members
for _, member := range update.GetAddMembers() {
if _, ok := addrMap[member.Addr]; !ok {
group.Members = append(group.Members, member)
}
}
group.MemberNum = uint32(len(group.Members))
adminMap := make(map[string]int)
for index, addr := range group.Admins {
adminMap[addr] = index
}
// remove admins
for _, addr := range update.GetRemoveAdmins() {
if index, ok := adminMap[addr]; ok {
group.Admins = append(group.Admins[:index], group.Admins[index+1:]...)
delete(adminMap, addr)
}
}
// add admins
for _, addr := range update.GetAddAdmins() {
if _, ok := adminMap[addr]; !ok {
group.Admins = append(group.Admins, addr)
}
}
groupValue := types.Encode(group)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(group.ID), Value: groupValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyUpdateGroupLog, Log: groupValue})
return receipt, nil
}
func (a *action) createVote(create *vty.CreateVote) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
vote := &vty.VoteInfo{}
vote.ID = formatVoteID(dapp.HeightIndexStr(a.height, int64(a.index)))
vote.BeginTimestamp = create.BeginTimestamp
vote.EndTimestamp = create.EndTimestamp
vote.Name = create.Name
vote.GroupID = create.GroupID
vote.Description = create.Description
vote.Creator = a.fromAddr
vote.VoteOptions = make([]*vty.VoteOption, 0)
for _, option := range create.VoteOptions {
vote.VoteOptions = append(vote.VoteOptions, &vty.VoteOption{Option: option})
}
voteValue := types.Encode(vote)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(vote.ID), Value: voteValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCreateVoteLog, Log: voteValue})
return receipt, nil
}
func (a *action) commitVote(commit *vty.CommitVote) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
vote, err := a.getVoteInfo(commit.VoteID)
if err != nil {
elog.Error("vote exec commitVote", "txHash", a.txHash, "get vote err", err)
return nil, errStateDBGet
}
group, err := a.getGroupInfo(vote.GroupID)
if err != nil {
elog.Error("vote exec commitVote", "txHash", a.txHash, "get group err", err)
return nil, errStateDBGet
}
var voteWeight uint32
for _, member := range group.Members {
if member.Addr == a.fromAddr {
voteWeight = member.VoteWeight
}
}
vote.VoteOptions[commit.OptionIndex].Score += voteWeight
info := &vty.CommitInfo{Addr: a.fromAddr}
vote.CommitInfos = append(vote.CommitInfos, info)
voteValue := types.Encode(vote)
//提交的哈希和权重等信息不记录到statedb中
info.VoteWeight = voteWeight
info.TxHash = hex.EncodeToString(a.txHash)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(vote.ID), Value: voteValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCommitVoteLog, Log: types.Encode(info)})
return receipt, nil
}
func (a *action) closeVote(close *vty.CloseVote) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
vote, err := a.getVoteInfo(close.VoteID)
if err != nil {
elog.Error("vote exec commitVote", "txHash", a.txHash, "get vote err", err)
return nil, errStateDBGet
}
vote.Status = voteStatusClosed
voteValue := types.Encode(vote)
receipt.KV = append(receipt.KV, &types.KeyValue{Key: formatStateIDKey(vote.ID), Value: voteValue})
receipt.Logs = append(receipt.Logs, &types.ReceiptLog{Ty: vty.TyCloseVoteLog, Log: voteValue})
return receipt, nil
}
func (a *action) updateMember(update *vty.UpdateMember) (*types.Receipt, error) {
receipt := &types.Receipt{Ty: types.ExecOk}
return receipt, nil
}
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
// CheckTx 实现自定义检验交易接口,供框架调用
func (v *vote) CheckTx(tx *types.Transaction, index int) error {
// implement code
txHash := hex.EncodeToString(tx.Hash())
var action vty.VoteAction
err := types.Decode(tx.Payload, &action)
if err != nil {
elog.Error("vote CheckTx", "txHash", txHash, "Decode payload error", err)
return types.ErrActionNotSupport
}
if action.Ty == vty.TyCreateGroupAction {
err = v.checkCreateGroup(action.GetCreateGroup())
} else if action.Ty == vty.TyUpdateGroupAction {
err = v.checkUpdateGroup(action.GetUpdateGroup(), tx, index)
} else if action.Ty == vty.TyCreateVoteAction {
err = v.checkCreateVote(action.GetCreateVote(), tx, index)
} else if action.Ty == vty.TyCommitVoteAction {
err = v.checkCommitVote(action.GetCommitVote(), tx, index)
} else if action.Ty == vty.TyCloseVoteAction {
err = v.checkCloseVote(action.GetCloseVote(), tx, index)
} else if action.Ty == vty.TyUpdateMemberAction {
err = v.checkUpdateMember(action.GetUpdateMember())
} else {
err = types.ErrActionNotSupport
}
if err != nil {
elog.Error("vote CheckTx", "txHash", txHash, "actionName", tx.ActionName(), "err", err, "actionData", action)
}
return err
}
func checkMemberValidity(members []*vty.GroupMember) error {
filter := make(map[string]struct{}, len(members))
for _, member := range members {
if member.GetAddr() == "" {
return types.ErrInvalidAddress
}
if _, ok := filter[member.Addr]; ok {
return errDuplicateMember
}
filter[member.Addr] = struct{}{}
}
return nil
}
func (v *vote) checkCreateGroup(create *vty.CreateGroup) error {
if create.GetName() == "" {
return errEmptyName
}
//检测组成员是否有重复
if err := checkMemberValidity(create.GetMembers()); err != nil {
return err
}
//检测管理员是否有重复
if checkSliceItemDuplicate(create.GetAdmins()) {
return errDuplicateAdmin
}
return nil
}
func (v *vote) checkUpdateGroup(update *vty.UpdateGroup, tx *types.Transaction, index int) error {
action := newAction(v, tx, index)
groupInfo, err := action.getGroupInfo(update.GetGroupID())
if err != nil {
return err
}
if !checkSliceItemExist(action.fromAddr, groupInfo.GetAdmins()) {
return errAddrPermissionDenied
}
//防止将管理员全部删除
if len(update.RemoveAdmins) >= len(groupInfo.GetAdmins()) && len(update.AddAdmins) == 0 {
return errAddrPermissionDenied
}
addrs := make([]string, 0, len(update.RemoveMembers)+len(update.AddAdmins)+len(update.RemoveAdmins))
addrs = append(addrs, update.RemoveMembers...)
addrs = append(addrs, update.AddAdmins...)
addrs = append(addrs, update.RemoveAdmins...)
for _, member := range update.AddMembers {
if len(member.Addr) != addrLen {
return types.ErrInvalidAddress
}
}
for _, addr := range addrs {
if len(addr) != addrLen {
elog.Error("checkUpdateGroup", "invalid addr", addr)
return types.ErrInvalidAddress
}
}
//保证管理员地址合法性
for _, addr := range update.GetAddAdmins() {
if err := dapp.CheckAddress(v.GetAPI().GetConfig(), addr, v.GetHeight()); err != nil {
elog.Error("checkUpdateGroup", "addr", addr, "CheckAddress err", err)
return types.ErrInvalidAddress
}
}
return nil
}
func (v *vote) checkCreateVote(create *vty.CreateVote, tx *types.Transaction, index int) error {
if create.GetName() == "" {
return errEmptyName
}
action := newAction(v, tx, index)
groupInfo, err := action.getGroupInfo(create.GetGroupID())
if err != nil {
return err
}
if !checkSliceItemExist(action.fromAddr, groupInfo.GetAdmins()) {
return errAddrPermissionDenied
}
if create.GetEndTimestamp() <= v.GetBlockTime() || create.EndTimestamp <= create.BeginTimestamp {
return errInvalidVoteTime
}
if len(create.VoteOptions) < 2 {
return errInvalidVoteOption
}
return nil
}
func (v *vote) checkCommitVote(commit *vty.CommitVote, tx *types.Transaction, index int) error {
action := newAction(v, tx, index)
voteInfo, err := action.getVoteInfo(commit.GetVoteID())
if err != nil {
return err
}
if voteInfo.BeginTimestamp > action.blockTime {
return errVoteNotStarted
}
if voteInfo.EndTimestamp <= action.blockTime {
return errVoteAlreadyFinished
}
if voteInfo.Status == voteStatusClosed {
return errVoteAlreadyClosed
}
if commit.OptionIndex >= uint32(len(voteInfo.VoteOptions)) {
return errInvalidOptionIndex
}
groupInfo, err := action.getGroupInfo(voteInfo.GroupID)
if err != nil {
return err
}
// check if exist in group members
if !checkMemberExist(action.fromAddr, groupInfo.Members) {
return errAddrPermissionDenied
}
// check if already vote
for _, info := range voteInfo.GetCommitInfos() {
if action.fromAddr == info.Addr {
return errAddrAlreadyVoted
}
}
return nil
}
func (v *vote) checkCloseVote(close *vty.CloseVote, tx *types.Transaction, index int) error {
action := newAction(v, tx, index)
voteInfo, err := action.getVoteInfo(close.GetVoteID())
if err != nil {
return err
}
if voteInfo.Creator != action.fromAddr {
return errAddrPermissionDenied
}
if voteInfo.Status == voteStatusClosed {
return errVoteAlreadyClosed
}
return nil
}
func (v *vote) checkUpdateMember(update *vty.UpdateMember) error {
if update.GetName() == "" {
return errEmptyName
}
return nil
}
package executor
import (
"testing"
"github.com/33cn/chain33/common/address"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
func TestVote_CheckTx_CreateGroup(t *testing.T) {
tcArr := []*testcase{{
index: 1,
payload: &vty.CreateGroup{},
expectCheckErr: errEmptyName,
}, {
index: 2,
payload: &vty.CreateGroup{Name: "test", Members: []*vty.GroupMember{{}}},
expectCheckErr: types.ErrInvalidAddress,
}, {
index: 3,
payload: &vty.CreateGroup{
Name: "test",
Members: []*vty.GroupMember{{
Addr: testAddrs[0],
}, {
Addr: testAddrs[0],
}},
},
expectCheckErr: errDuplicateMember,
}, {
index: 4,
payload: &vty.CreateGroup{Name: "test", Admins: []string{testAddrs[0], testAddrs[0]}},
expectCheckErr: errDuplicateAdmin,
}, {
index: 5,
payload: &vty.CreateGroup{Name: "test"},
},
}
testExec(t, nil, testTypeCheckTx, tcArr, privKeys[0])
}
func TestVote_CheckTx_UpdateGroup(t *testing.T) {
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
execType: testTypeExecLocal,
}, {
index: 1,
payload: &vty.UpdateGroup{},
expectCheckErr: errGroupNotExist,
}, {
index: 2,
payload: &vty.UpdateGroup{GroupID: groupID},
priv: privKeys[1],
expectCheckErr: errAddrPermissionDenied,
}, {
index: 3,
payload: &vty.UpdateGroup{GroupID: groupID, RemoveAdmins: testAddrs[:]},
expectCheckErr: errAddrPermissionDenied,
}, {
index: 4,
payload: &vty.UpdateGroup{GroupID: groupID, AddMembers: []*vty.GroupMember{{Addr: "errAddr"}}},
expectCheckErr: types.ErrInvalidAddress,
}, {
index: 5,
payload: &vty.UpdateGroup{GroupID: groupID, AddAdmins: []string{"errAddr"}},
expectCheckErr: types.ErrInvalidAddress,
}, {
index: 6,
payload: &vty.UpdateGroup{GroupID: groupID, AddAdmins: []string{address.MultiSignAddress(privKeys[0].PubKey().Bytes())}},
expectCheckErr: types.ErrInvalidAddress,
}, {
index: 7,
payload: &vty.UpdateGroup{GroupID: groupID, AddAdmins: []string{testAddrs[1]}},
},
}
testExec(t, nil, testTypeCheckTx, tcArr, privKeys[0])
}
func TestVote_CheckTx_CreateVote(t *testing.T) {
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
execType: testTypeExecLocal,
}, {
index: 1,
payload: &vty.CreateVote{},
expectCheckErr: errEmptyName,
}, {
index: 2,
payload: &vty.CreateVote{Name: "vote"},
expectCheckErr: errGroupNotExist,
}, {
index: 3,
payload: &vty.CreateVote{Name: "vote", GroupID: groupID},
priv: privKeys[1],
expectCheckErr: errAddrPermissionDenied,
}, {
index: 4,
payload: &vty.CreateVote{Name: "vote", GroupID: groupID},
expectCheckErr: errInvalidVoteTime,
}, {
index: 5,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
BeginTimestamp: testBlockTime + 1,
EndTimestamp: testBlockTime + 1,
},
expectCheckErr: errInvalidVoteTime,
}, {
index: 6,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
EndTimestamp: testBlockTime + 1,
},
expectCheckErr: errInvalidVoteOption,
}, {
index: 7,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
EndTimestamp: testBlockTime + 1,
VoteOptions: []string{"A", "B"},
},
},
}
testExec(t, nil, testTypeCheckTx, tcArr, privKeys[0])
}
func TestVote_CheckTx_CommitVote(t *testing.T) {
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
vote2 := formatVoteID(dapp.HeightIndexStr(testHeight, 7))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test", Members: []*vty.GroupMember{{Addr: testAddrs[0]}}},
execType: testTypeExecLocal,
}, {
index: 1,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
EndTimestamp: testBlockTime + 1,
VoteOptions: []string{"A", "B"},
},
execType: testTypeExecLocal,
}, {
index: 2,
payload: &vty.CommitVote{},
expectCheckErr: errVoteNotExist,
}, {
index: 3,
payload: &vty.CommitVote{VoteID: voteID, OptionIndex: 10},
expectCheckErr: errInvalidOptionIndex,
}, {
index: 4,
payload: &vty.CommitVote{VoteID: voteID},
priv: privKeys[1],
expectCheckErr: errAddrPermissionDenied,
}, {
index: 5,
payload: &vty.CommitVote{VoteID: voteID},
execType: testTypeExecLocal,
}, {
index: 6,
payload: &vty.CommitVote{VoteID: voteID},
expectCheckErr: errAddrAlreadyVoted,
}, {
index: 7,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
BeginTimestamp: testBlockTime + 1,
EndTimestamp: testBlockTime + 2,
VoteOptions: []string{"A", "B"},
},
execType: testTypeExecLocal,
}, {
index: 8,
payload: &vty.CommitVote{VoteID: vote2},
expectCheckErr: errVoteNotStarted,
}}
testExec(t, nil, testTypeCheckTx, tcArr, privKeys[0])
}
func TestVote_CheckTx_CloseVote(t *testing.T) {
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test", Members: []*vty.GroupMember{{Addr: testAddrs[0]}}},
execType: testTypeExecLocal,
}, {
index: 1,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
EndTimestamp: testBlockTime + 1,
VoteOptions: []string{"A", "B"},
},
execType: testTypeExecLocal,
}, {
index: 2,
payload: &vty.CloseVote{},
expectCheckErr: errVoteNotExist,
}, {
index: 3,
payload: &vty.CloseVote{VoteID: voteID},
priv: privKeys[1],
expectCheckErr: errAddrPermissionDenied,
}, {
index: 4,
payload: &vty.CloseVote{VoteID: voteID},
execType: testTypeExecLocal,
}, {
index: 5,
payload: &vty.CloseVote{VoteID: voteID},
expectCheckErr: errVoteAlreadyClosed,
},
}
testExec(t, nil, testTypeCheckTx, tcArr, privKeys[0])
}
func TestVote_CheckTx_UpdateMember(t *testing.T) {
tcArr := []*testcase{{
index: 0,
payload: &vty.UpdateMember{},
expectCheckErr: errEmptyName,
}, {
index: 1,
payload: &vty.UpdateMember{Name: "test"},
},
}
testExec(t, nil, testTypeCheckTx, tcArr, privKeys[0])
}
package executor
import "errors"
var (
errEmptyName = errors.New("errEmptyName")
errDuplicateMember = errors.New("errDuplicateMember")
errDuplicateAdmin = errors.New("errDuplicateAdmin")
errInvalidVoteTime = errors.New("errInvalidVoteTime")
errInvalidVoteOption = errors.New("errInvalidVoteOption")
errVoteNotExist = errors.New("errVoteNotExist")
errGroupNotExist = errors.New("errGroupNotExist")
errStateDBGet = errors.New("errStateDBGet")
errInvalidVoteID = errors.New("errInvalidVoteID")
errInvalidGroupID = errors.New("errInvalidGroupID")
errInvalidOptionIndex = errors.New("errInvalidOptionIndex")
errAddrAlreadyVoted = errors.New("errAddrAlreadyVoted")
errVoteAlreadyFinished = errors.New("errVoteAlreadyFinished")
errVoteNotStarted = errors.New("errVoteNotStarted")
errVoteAlreadyClosed = errors.New("errVoteAlreadyClosed")
errAddrPermissionDenied = errors.New("errAddrPermissionDenied")
)
package executor
import (
"github.com/33cn/chain33/types"
votetypes "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 实现交易的链上执行接口
* 关键数据上链(statedb)并生成交易回执(log)
*/
func (v *vote) Exec_CreateGroup(payload *votetypes.CreateGroup, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.createGroup(payload)
}
func (v *vote) Exec_UpdateGroup(payload *votetypes.UpdateGroup, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.updateGroup(payload)
}
func (v *vote) Exec_CreateVote(payload *votetypes.CreateVote, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.createVote(payload)
}
func (v *vote) Exec_CommitVote(payload *votetypes.CommitVote, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.commitVote(payload)
}
func (v *vote) Exec_CloseVote(payload *votetypes.CloseVote, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.closeVote(payload)
}
func (v *vote) Exec_UpdateMember(payload *votetypes.UpdateMember, tx *types.Transaction, index int) (*types.Receipt, error) {
action := newAction(v, tx, index)
return action.updateMember(payload)
}
package executor
import (
"github.com/33cn/chain33/types"
)
/*
* 实现区块回退时本地执行的数据清除
*/
// ExecDelLocal localdb kv数据自动回滚接口
func (v *vote) ExecDelLocal(tx *types.Transaction, receipt *types.ReceiptData, index int) (*types.LocalDBSet, error) {
kvs, err := v.DelRollbackKV(tx, tx.Execer)
if err != nil {
return nil, err
}
dbSet := &types.LocalDBSet{}
dbSet.KV = append(dbSet.KV, kvs...)
return dbSet, nil
}
package executor
import (
"encoding/hex"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 实现交易相关数据本地执行,数据不上链
* 非关键数据,本地存储(localDB), 用于辅助查询,效率高
*/
func (v *vote) ExecLocal_CreateGroup(payload *vty.CreateGroup, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
groupInfo := decodeGroupInfo(receiptData.Logs[0].Log)
table := newGroupTable(v.GetLocalDB())
kvs, err := v.updateAndSaveTable(table.Add, table.Save, groupInfo, tx, vty.NameCreateGroupAction, "group")
if err != nil {
return nil, err
}
dbSet.KV = kvs
addAddrs := make([]string, 0)
addAddrs = append(addAddrs, groupInfo.Admins...)
for _, member := range groupInfo.Members {
if !checkSliceItemExist(member.Addr, groupInfo.Admins) {
addAddrs = append(addAddrs, member.Addr)
}
}
kvs, err = v.addGroupMember(groupInfo.GetID(), addAddrs)
if err != nil {
elog.Error("execLocal createGroup", "txHash", hex.EncodeToString(tx.Hash()), "addMemberErr", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_UpdateGroup(update *vty.UpdateGroup, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
groupInfo := decodeGroupInfo(receiptData.Logs[0].Log)
table := newGroupTable(v.GetLocalDB())
kvs, err := v.updateAndSaveTable(table.Replace, table.Save, groupInfo, tx, vty.NameUpdateGroupAction, "group")
if err != nil {
return nil, err
}
dbSet.KV = kvs
removeAddrs := make([]string, 0)
//仍然为管理员或群成员之一,不删除groupID索引
tempAddrs := append(update.RemoveAdmins, update.RemoveMembers...)
for _, addr := range tempAddrs {
if checkMemberExist(addr, groupInfo.Members) || checkSliceItemExist(addr, groupInfo.Admins) {
continue
}
removeAddrs = append(removeAddrs, addr)
}
kvs, err = v.removeGroupMember(groupInfo.GetID(), removeAddrs)
if err != nil {
elog.Error("execLocal UpdateGroup", "txHash", hex.EncodeToString(tx.Hash()), "removeMemberErr", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
addAddrs := make([]string, 0)
addAddrs = append(addAddrs, update.AddAdmins...)
for _, member := range update.AddMembers {
if !checkSliceItemExist(member.Addr, update.AddAdmins) {
addAddrs = append(addAddrs, member.Addr)
}
}
kvs, err = v.addGroupMember(groupInfo.GetID(), addAddrs)
if err != nil {
elog.Error("execLocal UpdateGroup", "txHash", hex.EncodeToString(tx.Hash()), "addMemberErr", err)
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_CreateVote(payload *vty.CreateVote, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
voteInfo := decodeVoteInfo(receiptData.Logs[0].Log)
table := newVoteTable(v.GetLocalDB())
kvs, err := v.updateAndSaveTable(table.Add, table.Save, voteInfo, tx, vty.NameCreateVoteAction, "vote")
if err != nil {
return nil, err
}
dbSet.KV = kvs
table = newGroupTable(v.GetLocalDB())
row, err := table.GetData([]byte(voteInfo.GroupID))
if err != nil {
elog.Error("execLocal createVote", "txHash", hex.EncodeToString(tx.Hash()), "voteTable get", err)
return nil, err
}
groupInfo, _ := row.Data.(*vty.GroupInfo)
groupInfo.VoteNum++
kvs, err = v.updateAndSaveTable(table.Replace, table.Save, groupInfo, tx, vty.NameCreateVoteAction, "group")
if err != nil {
return nil, err
}
dbSet.KV = append(dbSet.KV, kvs...)
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_CommitVote(payload *vty.CommitVote, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
//implement code, add customize kv to dbSet...
commitInfo := decodeCommitInfo(receiptData.Logs[0].Log)
table := newVoteTable(v.GetLocalDB())
row, err := table.GetData([]byte(payload.GetVoteID()))
if err != nil {
elog.Error("execLocal commitVote", "txHash", hex.EncodeToString(tx.Hash()), "table get", err)
return nil, err
}
voteInfo, _ := row.Data.(*vty.VoteInfo)
voteInfo.VoteOptions[payload.OptionIndex].Score += commitInfo.VoteWeight
voteInfo.CommitInfos = append(voteInfo.CommitInfos, commitInfo)
dbSet.KV, err = v.updateAndSaveTable(table.Replace, table.Save, voteInfo, tx, vty.NameCommitVoteAction, "vote")
if err != nil {
return nil, err
}
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_CloseVote(payload *vty.CloseVote, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
table := newVoteTable(v.GetLocalDB())
row, err := table.GetData([]byte(payload.GetVoteID()))
if err != nil {
elog.Error("execLocal closeVote", "txHash", hex.EncodeToString(tx.Hash()), "table get", err)
return nil, err
}
voteInfo, ok := row.Data.(*vty.VoteInfo)
if !ok {
elog.Error("execLocal closeVote", "txHash", hex.EncodeToString(tx.Hash()), "voteInfo type asset", err)
return nil, types.ErrTypeAsset
}
voteInfo.Status = voteStatusClosed
dbSet.KV, err = v.updateAndSaveTable(table.Replace, table.Save, voteInfo, tx, vty.NameCloseVoteAction, "vote")
if err != nil {
return nil, err
}
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
func (v *vote) ExecLocal_UpdateMember(payload *vty.UpdateMember, tx *types.Transaction, receiptData *types.ReceiptData, index int) (*types.LocalDBSet, error) {
dbSet := &types.LocalDBSet{}
table := newMemberTable(v.GetLocalDB())
row, err := table.GetData([]byte(tx.From()))
if err != nil {
elog.Error("execLocal updateMember", "txHash", hex.EncodeToString(tx.Hash()), "table get", err)
return nil, err
}
memberInfo, _ := row.Data.(*vty.MemberInfo)
memberInfo.Name = payload.GetName()
dbSet.KV, err = v.updateAndSaveTable(table.Replace, table.Save, memberInfo, tx, vty.NameUpdateMemberAction, "member")
if err != nil {
return nil, err
}
//auto gen for localdb auto rollback
return v.addAutoRollBack(tx, dbSet.KV), nil
}
//当区块回滚时,框架支持自动回滚localdb kv,需要对exec-local返回的kv进行封装
func (v *vote) addAutoRollBack(tx *types.Transaction, kv []*types.KeyValue) *types.LocalDBSet {
dbSet := &types.LocalDBSet{}
dbSet.KV = v.AddRollbackKV(tx, tx.Execer, kv)
return dbSet
}
type updateFunc func(message types.Message) error
type saveFunc func() ([]*types.KeyValue, error)
func (v *vote) updateAndSaveTable(update updateFunc, save saveFunc, data types.Message, tx *types.Transaction, actionName, tableName string) ([]*types.KeyValue, error) {
err := update(data)
if err != nil {
elog.Error("execLocal "+actionName, "txHash", hex.EncodeToString(tx.Hash()), tableName+" table update", err)
return nil, err
}
kvs, err := save()
if err != nil {
elog.Error("execLocal "+actionName, "txHash", hex.EncodeToString(tx.Hash()), tableName+" table save", err)
return nil, err
}
return kvs, nil
}
// 新增用户时,将对应的groupID信息添加到用户表中
func (v *vote) addGroupMember(groupID string, addrs []string) ([]*types.KeyValue, error) {
table := newMemberTable(v.GetLocalDB())
for _, addr := range addrs {
addrKey := []byte(addr)
row, err := table.GetData(addrKey)
if err == nil {
info, _ := row.Data.(*vty.MemberInfo)
if !checkSliceItemExist(groupID, info.GroupIDs) {
info.GroupIDs = append(info.GroupIDs, groupID)
err = table.Replace(info)
}
} else if err == types.ErrNotFound {
err = table.Add(&vty.MemberInfo{Addr: addr, GroupIDs: []string{groupID}})
}
// 这个错可能由GetData,Replace,Add返回
if err != nil {
elog.Error("execLocal addMember", "member table Add/Replace", err)
return nil, err
}
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal addMember", "member table save", err)
return nil, err
}
return kvs, nil
}
//删除用户,将对应的groupID信息删除
func (v *vote) removeGroupMember(groupID string, addrs []string) ([]*types.KeyValue, error) {
table := newMemberTable(v.GetLocalDB())
for _, addr := range addrs {
addrKey := []byte(addr)
row, err := table.GetData(addrKey)
if err == types.ErrNotFound {
continue
} else if err != nil {
elog.Error("execLocal removeMember", "member table getData", err)
return nil, err
}
info, _ := row.Data.(*vty.MemberInfo)
for index, id := range info.GroupIDs {
if id == groupID {
info.GroupIDs = append(info.GroupIDs[:index], info.GroupIDs[index+1:]...)
err = table.Replace(info)
if err != nil {
elog.Error("execLocal removeMember", "member table replace", err)
return nil, err
}
break
}
}
}
kvs, err := table.Save()
if err != nil {
elog.Error("execLocal addMember", "member table save", err)
return nil, err
}
return kvs, nil
}
package executor
import (
"testing"
"github.com/33cn/chain33/system/dapp"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/33cn/chain33/common/crypto"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
"github.com/stretchr/testify/require"
)
const (
testTypeCheckTx = iota + 1
testTypeExec
testTypeExecLocal
testTypeExecDelLocal
)
func testExec(t *testing.T, mock *testExecMock, testExecType int, tcArr []*testcase, priv crypto.PrivKey) {
if mock == nil {
mock = &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
}
exec := mock.exec
for i, tc := range tcArr {
signPriv := priv
if tc.priv != nil {
signPriv = tc.priv
}
tx, err := createTx(mock, tc.payload, signPriv)
require.NoErrorf(t, err, "createTxErr, testIndex=%d", tc.index)
if err != nil {
continue
}
err = exec.CheckTx(tx, i)
require.Equalf(t, tc.expectCheckErr, err, "checkTx err index %d", tc.index)
execType := testExecType
if tc.execType > 0 {
execType = tc.execType
}
if execType == testTypeCheckTx {
continue
}
recp, err := exec.Exec(tx, i)
recpData := &types.ReceiptData{
Ty: recp.GetTy(),
Logs: recp.GetLogs(),
}
if err == nil && len(recp.GetKV()) > 0 {
util.SaveKVList(mock.stateDB, recp.KV)
}
require.Equalf(t, tc.expectExecErr, err, "execTx err index %d", tc.index)
if execType == testTypeExec {
continue
}
kvSet, err := exec.ExecLocal(tx, recpData, i)
for _, kv := range kvSet.GetKV() {
err := mock.localDB.Set(kv.Key, kv.Value)
require.Nil(t, err)
}
require.Equalf(t, tc.expectExecLocalErr, err, "execLocalTx err index %d", tc.index)
if execType == testTypeExecLocal {
continue
}
kvSet, err = exec.ExecDelLocal(tx, recpData, i)
for _, kv := range kvSet.GetKV() {
err := mock.localDB.Set(kv.Key, kv.Value)
require.Nil(t, err)
}
require.Equalf(t, tc.expectExecDelErr, err, "execDelLocalTx err index %d", tc.index)
}
}
func TestVote_Exec(t *testing.T) {
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test", Members: []*vty.GroupMember{{Addr: testAddrs[0]}}},
}, {
index: 1,
payload: &vty.CreateVote{
Name: "vote",
GroupID: groupID,
EndTimestamp: testBlockTime + 1,
VoteOptions: []string{"A", "B"},
},
}, {
index: 2,
payload: &vty.UpdateGroup{GroupID: groupID, RemoveAdmins: testAddrs, AddAdmins: []string{testAddrs[1]}},
}, {
index: 3,
payload: &vty.CommitVote{VoteID: voteID},
}, {
index: 4,
payload: &vty.UpdateGroup{GroupID: groupID, AddAdmins: []string{testAddrs[0]}},
expectCheckErr: errAddrPermissionDenied,
execType: testTypeCheckTx,
}, {
index: 5,
payload: &vty.UpdateGroup{GroupID: groupID, RemoveAdmins: testAddrs, AddAdmins: []string{testAddrs[0]}},
priv: privKeys[1],
}, {
index: 6,
payload: &vty.UpdateGroup{GroupID: groupID, AddMembers: []*vty.GroupMember{{Addr: testAddrs[1]}}, RemoveMembers: testAddrs},
}, {
index: 7,
payload: &vty.CommitVote{VoteID: voteID},
expectCheckErr: errAddrPermissionDenied,
execType: testTypeCheckTx,
}, {
index: 8,
payload: &vty.CommitVote{VoteID: voteID},
priv: privKeys[1],
}, {
index: 9,
payload: &vty.CloseVote{VoteID: voteID},
}, {
index: 10,
payload: &vty.CloseVote{VoteID: voteID},
execType: testTypeCheckTx,
expectCheckErr: errVoteAlreadyClosed,
}, {
index: 11,
payload: &vty.UpdateMember{Name: "testName"},
},
}
testExec(t, nil, testTypeExec, tcArr, privKeys[0])
}
package executor
import (
"testing"
tab "github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/stretchr/testify/require"
)
type tableCase struct {
index int
key []byte
expectGetErr error
expectData types.Message
}
func testTableData(t *testing.T, table *tab.Table, tcArr []*tableCase, msg string) {
for _, tc := range tcArr {
row, err := table.GetData(tc.key)
require.Equalf(t, tc.expectGetErr, err, msg+",index=%d", tc.index)
if err != nil {
continue
}
require.Equalf(t, tc.expectData.String(), row.Data.String(),
msg+",index=%d", tc.index)
}
}
func TestVote_ExecLocal_CreateGroup(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
groupID2 := formatGroupID(dapp.HeightIndexStr(testHeight, 1))
members1 := []*vty.GroupMember{{Addr: testAddrs[1], VoteWeight: 1}}
members2 := []*vty.GroupMember{{Addr: testAddrs[2], VoteWeight: 1}}
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{
Name: "test",
Members: members1},
}, {
index: 1,
payload: &vty.CreateGroup{
Name: "test",
Members: members2},
},
}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
table := newMemberTable(mock.exec.GetLocalDB())
tcArr1 := []*tableCase{{
index: 0,
key: []byte(testAddrs[0]),
expectData: &vty.MemberInfo{Addr: testAddrs[0], GroupIDs: []string{groupID, groupID2}},
}, {
index: 1,
key: []byte(testAddrs[1]),
expectData: &vty.MemberInfo{Addr: testAddrs[1], GroupIDs: []string{groupID}},
}, {
index: 2,
key: []byte(testAddrs[2]),
expectData: &vty.MemberInfo{Addr: testAddrs[2], GroupIDs: []string{groupID2}},
}, {
index: 3,
key: []byte("addr"),
expectGetErr: types.ErrNotFound,
}}
testTableData(t, table, tcArr1, "check member groupIDs")
table = newGroupTable(mock.exec.GetLocalDB())
tcArr1 = []*tableCase{{
index: 0,
key: []byte(groupID),
expectData: &vty.GroupInfo{ID: groupID, Name: "test", MemberNum: 1,
Admins: []string{testAddrs[0]}, Creator: testAddrs[0], Members: members1},
}, {
index: 1,
key: []byte(groupID2),
expectData: &vty.GroupInfo{ID: groupID2, Name: "test", MemberNum: 1,
Admins: []string{testAddrs[0]}, Creator: testAddrs[0], Members: members2},
}}
testTableData(t, table, tcArr1, "check groupInfo")
}
func TestVote_ExecLocal_UpdateGroup(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
members := []*vty.GroupMember{{Addr: testAddrs[2], VoteWeight: 1}}
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.UpdateGroup{GroupID: groupID, RemoveAdmins: []string{testAddrs[0]}, AddAdmins: []string{testAddrs[1]}},
}, {
index: 2,
priv: privKeys[1],
payload: &vty.UpdateGroup{GroupID: groupID, RemoveMembers: testAddrs, AddMembers: members},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
table := newMemberTable(mock.exec.GetLocalDB())
tcArr1 := []*tableCase{{
index: 0,
key: []byte(testAddrs[0]),
expectData: &vty.MemberInfo{Addr: testAddrs[0]},
}, {
index: 1,
key: []byte(testAddrs[1]),
expectData: &vty.MemberInfo{Addr: testAddrs[1], GroupIDs: []string{groupID}},
}, {
index: 2,
key: []byte(testAddrs[2]),
expectData: &vty.MemberInfo{Addr: testAddrs[2], GroupIDs: []string{groupID}},
}}
testTableData(t, table, tcArr1, "check member groupIDs")
table = newGroupTable(mock.exec.GetLocalDB())
expectInfo := &vty.GroupInfo{ID: groupID, Name: "test", Admins: []string{testAddrs[1]},
Members: members, MemberNum: 1, Creator: testAddrs[0]}
testTableData(t, table, []*tableCase{{
index: 0,
key: []byte(groupID),
expectData: expectInfo,
}, {
index: 1,
key: []byte("testid"),
expectGetErr: types.ErrNotFound,
}}, "check group Info")
tx := util.CreateNoneTx(mock.cfg, privKeys[0])
group, err := newAction(mock.exec, tx, 0).getGroupInfo(groupID)
require.Nil(t, err)
require.Equal(t, group.String(), expectInfo.String())
}
func TestVote_ExecLocal_CreateVote(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
options := []*vty.VoteOption{{Option: "A"}, {Option: "B"}}
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
BeginTimestamp: testBlockTime, EndTimestamp: testBlockTime + 1},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
table := newVoteTable(mock.exec.GetLocalDB())
expectVoteInfo := &vty.VoteInfo{
Name: "test", VoteOptions: options, BeginTimestamp: testBlockTime, EndTimestamp: testBlockTime + 1,
GroupID: groupID, ID: voteID, Creator: testAddrs[0],
}
testTableData(t, table, []*tableCase{{
index: 0,
key: []byte(voteID),
expectData: expectVoteInfo,
}}, "check vote Info")
table = newGroupTable(mock.exec.GetLocalDB())
row, err := table.GetData([]byte(groupID))
require.Nil(t, err)
info, _ := row.Data.(*vty.GroupInfo)
require.Equal(t, uint32(1), info.VoteNum)
tx := util.CreateNoneTx(mock.cfg, privKeys[0])
group, err := newAction(mock.exec, tx, 0).getGroupInfo(groupID)
require.Nil(t, err)
group.VoteNum = info.VoteNum
require.Equal(t, group.String(), info.String())
}
func TestVote_ExecLocal_CloseVote(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
BeginTimestamp: testBlockTime, EndTimestamp: testBlockTime + 1},
}, {
index: 2,
payload: &vty.CloseVote{VoteID: voteID},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
table := newVoteTable(mock.exec.GetLocalDB())
row, err := table.GetData([]byte(voteID))
require.Nil(t, err)
info, _ := row.Data.(*vty.VoteInfo)
require.Equal(t, uint32(voteStatusClosed), info.Status)
tx := util.CreateNoneTx(mock.cfg, privKeys[0])
vote, err := newAction(mock.exec, tx, 0).getVoteInfo(voteID)
require.Nil(t, err)
require.Equal(t, vote.String(), info.String())
}
func TestVote_ExecLocal_CommitVote(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
members := []*vty.GroupMember{{Addr: testAddrs[0], VoteWeight: 1}}
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test", Members: members},
}, {
index: 1,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
BeginTimestamp: testBlockTime, EndTimestamp: testBlockTime + 1},
}, {
index: 2,
payload: &vty.CommitVote{VoteID: voteID},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
table := newVoteTable(mock.exec.GetLocalDB())
row, err := table.GetData([]byte(voteID))
require.Nil(t, err)
info, _ := row.Data.(*vty.VoteInfo)
require.Equal(t, testAddrs[0], info.CommitInfos[0].Addr)
require.Equal(t, uint32(1), info.VoteOptions[0].Score)
tx := util.CreateNoneTx(mock.cfg, privKeys[0])
vote, err := newAction(mock.exec, tx, 0).getVoteInfo(voteID)
require.Nil(t, err)
vote.CommitInfos[0].TxHash = info.CommitInfos[0].TxHash
vote.CommitInfos[0].VoteWeight = info.CommitInfos[0].VoteWeight
require.Equal(t, vote.String(), info.String())
}
func TestVote_ExecDelLocal(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
execType: testTypeExecLocal,
}, {
index: 1,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
BeginTimestamp: testBlockTime, EndTimestamp: testBlockTime + 1},
}}
testExec(t, mock, testTypeExecDelLocal, tcArr, privKeys[0])
table := newVoteTable(mock.exec.GetLocalDB())
_, err := table.GetData([]byte(voteID))
require.Equal(t, types.ErrDecode, err)
}
package executor
/*
* 用户合约存取kv数据时,key值前缀需要满足一定规范
* 即key = keyPrefix + userKey
* 需要字段前缀查询时,使用’-‘作为分割符号
*/
var (
//keyPrefixStateDB state db key必须前缀
keyPrefixStateDB = "mavl-vote-"
//keyPrefixLocalDB local db的key必须前缀
keyPrefixLocalDB = "LODB-vote-"
)
// groupID or voteID
func formatStateIDKey(id string) []byte {
return []byte(keyPrefixStateDB + id)
}
package executor
import (
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
func (v *vote) getGroup(groupID string) (*vty.GroupInfo, error) {
if len(groupID) != IDLen {
return nil, errInvalidGroupID
}
table := newGroupTable(v.GetLocalDB())
row, err := table.GetData([]byte(groupID))
if err != nil {
elog.Error("query getGroup", "groupID", groupID, "err", err)
return nil, err
}
info, ok := row.Data.(*vty.GroupInfo)
if !ok {
return nil, types.ErrTypeAsset
}
return info, nil
}
// Query_GroupInfo query group info
func (v *vote) Query_GetGroups(in *vty.ReqStrings) (types.Message, error) {
if len(in.GetItems()) == 0 {
return nil, types.ErrInvalidParam
}
infos := &vty.GroupInfos{GroupList: make([]*vty.GroupInfo, 0, len(in.GetItems()))}
for _, id := range in.GetItems() {
info, err := v.getGroup(id)
if err != nil {
return nil, err
}
infos.GroupList = append(infos.GroupList, info)
}
return infos, nil
}
func (v *vote) getVote(voteID string) (*vty.VoteInfo, error) {
if len(voteID) != IDLen {
return nil, errInvalidVoteID
}
table := newVoteTable(v.GetLocalDB())
row, err := table.GetData([]byte(voteID))
if err != nil {
elog.Error("query getVote", "id", voteID, "err", err)
return nil, err
}
info, ok := row.Data.(*vty.VoteInfo)
if !ok {
return nil, types.ErrTypeAsset
}
return info, nil
}
func (v *vote) Query_GetVotes(in *vty.ReqStrings) (types.Message, error) {
if len(in.GetItems()) == 0 {
return nil, types.ErrInvalidParam
}
voteList := make([]*vty.VoteInfo, 0, len(in.GetItems()))
for _, id := range in.GetItems() {
info, err := v.getVote(id)
if err != nil {
return nil, err
}
voteList = append(voteList, info)
}
reply := &vty.ReplyVoteList{CurrentTimestamp: types.Now().Unix()}
reply.VoteList = filterVoteWithStatus(voteList, 0, reply.CurrentTimestamp)
return reply, nil
}
func (v *vote) getMember(addr string) (*vty.MemberInfo, error) {
if len(addr) != addrLen {
return nil, types.ErrInvalidAddress
}
table := newMemberTable(v.GetLocalDB())
row, err := table.GetData([]byte(addr))
if err != nil {
elog.Error("query getMember", "addr", addr, "err", err)
return nil, err
}
info, ok := row.Data.(*vty.MemberInfo)
if !ok {
return nil, types.ErrTypeAsset
}
return info, nil
}
func (v *vote) Query_GetMembers(in *vty.ReqStrings) (types.Message, error) {
if len(in.GetItems()) == 0 {
return nil, types.ErrInvalidParam
}
infos := &vty.MemberInfos{MemberList: make([]*vty.MemberInfo, 0, len(in.GetItems()))}
for _, id := range in.GetItems() {
info, err := v.getMember(id)
if err != nil {
return nil, err
}
infos.MemberList = append(infos.MemberList, info)
}
return infos, nil
}
func (v *vote) Query_ListGroup(in *vty.ReqListItem) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
table := newGroupTable(v.GetLocalDB())
var primaryKey []byte
primaryKey = append(primaryKey, []byte(in.StartItemID)...)
list := &vty.GroupInfos{}
rows, err := table.ListIndex(groupTablePrimary, nil, primaryKey, in.Count, in.Direction)
// 已经没有数据,直接返回
if err == types.ErrNotFound {
return list, nil
}
if err != nil {
elog.Error("query listGroup", "err", err, "param", in)
return nil, err
}
list.GroupList = make([]*vty.GroupInfo, 0, len(rows))
for _, row := range rows {
info, ok := row.Data.(*vty.GroupInfo)
if !ok {
return nil, types.ErrTypeAsset
}
list.GroupList = append(list.GroupList, info)
}
return list, nil
}
func (v *vote) Query_ListVote(in *vty.ReqListVote) (types.Message, error) {
if in.GetListReq() == nil {
return nil, types.ErrInvalidParam
}
table := newVoteTable(v.GetLocalDB())
//指定了组ID,则查询对应组下的投票列表
groupID := in.GetGroupID()
indexName := voteTablePrimary
var prefix, primaryKey []byte
if len(groupID) > 0 {
indexName = groupTablePrimary
prefix = []byte(groupID)
}
primaryKey = append(primaryKey, []byte(in.GetListReq().GetStartItemID())...)
reply := &vty.ReplyVoteList{CurrentTimestamp: types.Now().Unix()}
listCount := in.ListReq.GetCount()
listMore:
rows, err := table.ListIndex(indexName, prefix, primaryKey, listCount, in.GetListReq().Direction)
// 已经没有数据,直接返回
if err == types.ErrNotFound {
return reply, nil
}
if err != nil {
elog.Error("query listVote", "err", err, "param", in)
return nil, err
}
list := make([]*vty.VoteInfo, 0, len(rows))
for _, row := range rows {
info, ok := row.Data.(*vty.VoteInfo)
if !ok {
return nil, types.ErrTypeAsset
}
list = append(list, info)
}
primaryKey = append(primaryKey[:0], []byte(list[len(list)-1].ID)...)
list = filterVoteWithStatus(list, in.Status, reply.CurrentTimestamp)
reply.VoteList = append(reply.VoteList, list...)
//经过筛选后,数量小于请求数量,则需要再次list, 需要满足len(rows)==listCount, 否则表示已经没有数据
if len(rows) == int(listCount) && int(listCount) > len(list) {
listCount -= int32(len(list))
goto listMore
}
return reply, nil
}
func (v *vote) Query_ListMember(in *vty.ReqListItem) (types.Message, error) {
if in == nil {
return nil, types.ErrInvalidParam
}
table := newMemberTable(v.GetLocalDB())
var primaryKey []byte
primaryKey = append(primaryKey, []byte(in.StartItemID)...)
list := &vty.MemberInfos{}
rows, err := table.ListIndex(memberTablePrimary, nil, primaryKey, in.Count, in.Direction)
// 已经没有数据,直接返回
if err == types.ErrNotFound {
return list, nil
}
if err != nil {
elog.Error("query listMember", "err", err, "param", in)
return nil, err
}
list.MemberList = make([]*vty.MemberInfo, 0, len(rows))
for _, row := range rows {
info, ok := row.Data.(*vty.MemberInfo)
if !ok {
return nil, types.ErrTypeAsset
}
list.MemberList = append(list.MemberList, info)
}
return list, nil
}
package executor
import (
"testing"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/stretchr/testify/require"
)
func TestVote_Query_GetGroups(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
groupID2 := formatGroupID(dapp.HeightIndexStr(testHeight, 1))
groupID3 := formatGroupID(dapp.HeightIndexStr(testHeight, 2))
groupIDs := []string{groupID, groupID2, groupID3, "testid"}
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.CreateGroup{Name: "test"},
},
}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
exec := mock.exec
funcName := "GetGroups"
_, err := exec.Query(funcName, nil)
require.Equal(t, types.ErrInvalidParam, err)
_, err = exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: groupIDs[3:]}))
require.Equal(t, errInvalidGroupID, err)
_, err = exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: groupIDs[:3]}))
require.Equal(t, types.ErrNotFound, err)
data, err := exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: groupIDs[:2]}))
require.Equal(t, nil, err)
groups := data.(*vty.GroupInfos)
require.Equal(t, 2, len(groups.GroupList))
require.Equal(t, groupID, groups.GroupList[0].ID)
require.Equal(t, groupID2, groups.GroupList[1].ID)
}
func TestVote_Query_GetVotes(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
voteID := formatVoteID(dapp.HeightIndexStr(testHeight, 1))
voteID2 := formatVoteID(dapp.HeightIndexStr(testHeight, 2))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
BeginTimestamp: testBlockTime, EndTimestamp: testBlockTime + 1},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
exec := mock.exec
funcName := "GetVotes"
_, err := exec.Query(funcName, nil)
require.Equal(t, types.ErrInvalidParam, err)
_, err = exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: []string{voteID2}}))
require.Equal(t, types.ErrNotFound, err)
_, err = exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: []string{"voteid"}}))
require.Equal(t, errInvalidVoteID, err)
data, err := exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: []string{voteID}}))
require.Equal(t, nil, err)
vote := data.(*vty.ReplyVoteList)
require.Equal(t, voteID, vote.VoteList[0].ID)
}
func TestVote_Query_GetMembers(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test", Members: []*vty.GroupMember{{Addr: testAddrs[0]}}},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
exec := mock.exec
funcName := "GetMembers"
_, err := exec.Query(funcName, nil)
require.Equal(t, types.ErrInvalidParam, err)
_, err = exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: []string{testAddrs[1]}}))
require.Equal(t, types.ErrNotFound, err)
_, err = exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: []string{"addr"}}))
require.Equal(t, types.ErrInvalidAddress, err)
data, err := exec.Query(funcName, types.Encode(&vty.ReqStrings{Items: []string{testAddrs[0]}}))
require.Equal(t, nil, err)
members := data.(*vty.MemberInfos)
require.Equal(t, testAddrs[0], members.MemberList[0].Addr)
require.Equal(t, []string{groupID}, members.MemberList[0].GroupIDs)
}
func TestVote_Query_ListGroup(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
groupID2 := formatGroupID(dapp.HeightIndexStr(testHeight, 1))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.CreateGroup{Name: "test"},
},
}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
exec := mock.exec
funcName := "ListGroup"
data, err := exec.Query(funcName, nil)
require.Equal(t, nil, err)
list := data.(*vty.GroupInfos)
require.Equal(t, 2, len(list.GroupList))
data, err = exec.Query(funcName, types.Encode(&vty.ReqListItem{Count: 1, Direction: 1}))
require.Equal(t, nil, err)
list = data.(*vty.GroupInfos)
require.Equal(t, 1, len(list.GroupList))
require.Equal(t, groupID, list.GroupList[0].ID)
data, err = exec.Query(funcName, types.Encode(&vty.ReqListItem{StartItemID: groupID2}))
require.Equal(t, nil, err)
list = data.(*vty.GroupInfos)
require.Equal(t, 1, len(list.GroupList))
require.Equal(t, groupID, list.GroupList[0].ID)
}
func TestVote_Query_ListVote(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
now := types.Now().Unix() + 1000
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test"},
}, {
index: 1,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
EndTimestamp: testBlockTime + 1},
}, {
index: 2,
payload: &vty.CreateVote{Name: "test", GroupID: groupID, VoteOptions: []string{"A", "B"},
BeginTimestamp: now, EndTimestamp: now + 1},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
exec := mock.exec
funcName := "ListVote"
_, err := exec.Query(funcName, nil)
require.Equal(t, types.ErrInvalidParam, err)
data, err := exec.Query(funcName, types.Encode(&vty.ReqListVote{GroupID: groupID, ListReq: &vty.ReqListItem{}}))
require.Nil(t, err)
list := data.(*vty.ReplyVoteList)
require.Equal(t, 2, len(list.VoteList))
data, err = exec.Query(funcName, types.Encode(&vty.ReqListVote{GroupID: groupID, Status: voteStatusPending, ListReq: &vty.ReqListItem{}}))
require.Nil(t, err)
list = data.(*vty.ReplyVoteList)
require.Equal(t, 1, len(list.VoteList))
require.Equal(t, uint32(voteStatusPending), list.VoteList[0].Status)
}
func TestVote_Query_ListMember(t *testing.T) {
mock := &testExecMock{}
mock.InitEnv()
defer mock.FreeEnv()
groupID := formatGroupID(dapp.HeightIndexStr(testHeight, 0))
tcArr := []*testcase{{
index: 0,
payload: &vty.CreateGroup{Name: "test", Members: []*vty.GroupMember{{Addr: testAddrs[0]}}},
}}
testExec(t, mock, testTypeExecLocal, tcArr, privKeys[0])
exec := mock.exec
funcName := "ListMember"
data, err := exec.Query(funcName, nil)
require.Equal(t, nil, err)
list := data.(*vty.MemberInfos)
require.Equal(t, 1, len(list.MemberList))
require.Equal(t, testAddrs[0], list.MemberList[0].Addr)
require.Equal(t, []string{groupID}, list.MemberList[0].GroupIDs)
data, err = exec.Query(funcName, types.Encode(&vty.ReqListItem{StartItemID: "addr"}))
require.Equal(t, nil, err)
list = data.(*vty.MemberInfos)
require.Equal(t, &vty.MemberInfos{}, list)
}
package executor
import (
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/db/table"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
var (
groupTablePrimary = "groupid"
voteTablePrimary = "voteid"
memberTablePrimary = "addr"
)
var groupTableOpt = &table.Option{
Prefix: keyPrefixLocalDB,
Name: "group",
Primary: groupTablePrimary,
}
var voteTableOpt = &table.Option{
Prefix: keyPrefixLocalDB,
Name: "vote",
Primary: voteTablePrimary,
Index: []string{groupTablePrimary},
}
var memberTableOpt = &table.Option{
Prefix: keyPrefixLocalDB,
Name: "member",
Primary: memberTablePrimary,
}
//新建表
func newTable(kvDB db.KV, rowMeta table.RowMeta, opt *table.Option) *table.Table {
table, err := table.NewTable(rowMeta, kvDB, opt)
if err != nil {
panic(err)
}
return table
}
func newGroupTable(kvDB db.KV) *table.Table {
return newTable(kvDB, &groupTableRow{}, groupTableOpt)
}
func newVoteTable(kvDB db.KV) *table.Table {
return newTable(kvDB, &voteTableRow{}, voteTableOpt)
}
func newMemberTable(kvDB db.KV) *table.Table {
return newTable(kvDB, &memberTableRow{}, memberTableOpt)
}
//groupTableRow table meta 结构
type groupTableRow struct {
*vty.GroupInfo
}
//CreateRow 新建数据行
func (r *groupTableRow) CreateRow() *table.Row {
return &table.Row{Data: &vty.GroupInfo{}}
}
//SetPayload 设置数据
func (r *groupTableRow) SetPayload(data types.Message) error {
if d, ok := data.(*vty.GroupInfo); ok {
r.GroupInfo = d
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (r *groupTableRow) Get(key string) ([]byte, error) {
if key == groupTablePrimary {
return []byte(r.GroupInfo.GetID()), nil
}
return nil, types.ErrNotFound
}
//voteTableRow table meta 结构
type voteTableRow struct {
*vty.VoteInfo
}
//CreateRow 新建数据行
func (r *voteTableRow) CreateRow() *table.Row {
return &table.Row{Data: &vty.VoteInfo{}}
}
//SetPayload 设置数据
func (r *voteTableRow) SetPayload(data types.Message) error {
if d, ok := data.(*vty.VoteInfo); ok {
r.VoteInfo = d
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (r *voteTableRow) Get(key string) ([]byte, error) {
if key == voteTablePrimary {
return []byte(r.VoteInfo.GetID()), nil
} else if key == groupTablePrimary {
return []byte(r.VoteInfo.GetGroupID()), nil
}
return nil, types.ErrNotFound
}
type memberTableRow struct {
*vty.MemberInfo
}
//CreateRow 新建数据行
func (r *memberTableRow) CreateRow() *table.Row {
return &table.Row{Data: &vty.MemberInfo{}}
}
//SetPayload 设置数据
func (r *memberTableRow) SetPayload(data types.Message) error {
if d, ok := data.(*vty.MemberInfo); ok {
r.MemberInfo = d
return nil
}
return types.ErrTypeAsset
}
//Get 按照indexName 查询 indexValue
func (r *memberTableRow) Get(key string) ([]byte, error) {
if key == memberTablePrimary {
return []byte(r.MemberInfo.GetAddr()), nil
}
return nil, types.ErrNotFound
}
package executor
import (
"github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/types"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
)
const (
voteStatusNormal = iota //非关闭常规状态
voteStatusPending //即将开始
voteStatusOngoing //正在进行
voteStatusFinished //已经结束
voteStatusClosed //已经关闭
)
const (
// IDLen length of groupID or voteID
IDLen = 19
addrLen = 34
)
func formatGroupID(id string) string {
return "g" + id
}
func formatVoteID(id string) string {
return "v" + id
}
func checkMemberExist(addr string, members []*vty.GroupMember) bool {
for _, item := range members {
if addr == item.Addr {
return true
}
}
return false
}
func checkSliceItemExist(target string, items []string) bool {
for _, item := range items {
if target == item {
return true
}
}
return false
}
func checkSliceItemDuplicate(items []string) bool {
filter := make(map[string]struct{}, len(items))
for _, item := range items {
if _, ok := filter[item]; ok {
return true
}
filter[item] = struct{}{}
}
return false
}
func readStateDB(stateDB db.KV, key []byte, result types.Message) error {
val, err := stateDB.Get(key)
if err != nil {
return err
}
return types.Decode(val, result)
}
func mustDecodeProto(data []byte, msg types.Message) {
if err := types.Decode(data, msg); err != nil {
panic(err.Error())
}
}
func decodeGroupInfo(data []byte) *vty.GroupInfo {
info := &vty.GroupInfo{}
mustDecodeProto(data, info)
return info
}
func decodeVoteInfo(data []byte) *vty.VoteInfo {
info := &vty.VoteInfo{}
mustDecodeProto(data, info)
return info
}
func decodeCommitInfo(data []byte) *vty.CommitInfo {
info := &vty.CommitInfo{}
mustDecodeProto(data, info)
return info
}
func filterVoteWithStatus(voteList []*vty.VoteInfo, status uint32, currentTime int64) []*vty.VoteInfo {
var filterList []*vty.VoteInfo
for _, voteInfo := range voteList {
if voteInfo.Status == voteStatusClosed {
} else if voteInfo.BeginTimestamp > currentTime {
voteInfo.Status = voteStatusPending
} else if voteInfo.EndTimestamp > currentTime {
voteInfo.Status = voteStatusOngoing
} else {
voteInfo.Status = voteStatusFinished
}
//remove vote info with other status
if status == voteInfo.Status {
filterList = append(filterList, voteInfo)
}
}
//设置了状态筛选,返回对应的筛选列表
if status > 0 {
return filterList
}
return voteList
}
package executor
import (
"errors"
"testing"
"github.com/33cn/chain33/client"
"github.com/33cn/chain33/common"
"github.com/33cn/chain33/common/crypto"
dbm "github.com/33cn/chain33/common/db"
"github.com/33cn/chain33/common/log"
"github.com/33cn/chain33/queue"
"github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
"github.com/33cn/chain33/util"
wcom "github.com/33cn/chain33/wallet/common"
vty "github.com/33cn/plugin/plugin/dapp/vote/types"
"github.com/stretchr/testify/require"
)
var (
testHeight = int64(100)
testBlockTime = int64(1539918074)
// 测试的私钥
testPrivateKeys = []string{
"0x8dea7332c7bb3e3b0ce542db41161fd021e3cfda9d7dabacf24f98f2dfd69558",
"0x920976ffe83b5a98f603b999681a0bc790d97e22ffc4e578a707c2234d55cc8a",
"0xb59f2b02781678356c231ad565f73699753a28fd3226f1082b513ebf6756c15c",
}
// 测试的地址
testAddrs = []string{
"1EDDghAtgBsamrNEtNmYdQzC1QEhLkr87t",
"13cS5G1BDN2YfGudsxRxr7X25yu6ZdgxMU",
"1JSRSwp16NvXiTjYBYK9iUQ9wqp3sCxz2p",
}
// 测试的隐私公钥对
testPubkeyPairs = []string{
"92fe6cfec2e19cd15f203f83b5d440ddb63d0cb71559f96dc81208d819fea85886b08f6e874fca15108d244b40f9086d8c03260d4b954a40dfb3cbe41ebc7389",
"6326126c968a93a546d8f67d623ad9729da0e3e4b47c328a273dfea6930ffdc87bcc365822b80b90c72d30e955e7870a7a9725e9a946b9e89aec6db9455557eb",
"44bf54abcbae297baf3dec4dd998b313eafb01166760f0c3a4b36509b33d3b50239de0a5f2f47c2fc98a98a382dcd95a2c5bf1f4910467418a3c2595b853338e",
}
privKeys = make([]crypto.PrivKey, len(testPrivateKeys))
testCfg = types.NewChain33Config(types.GetDefaultCfgstring())
)
func init() {
log.SetLogLevel("error")
Init(vty.VoteX, testCfg, nil)
for i, priv := range testPrivateKeys {
privKeys[i], _ = decodePrivKey(priv)
}
}
type testExecMock struct {
dbDir string
localDB dbm.KVDB
stateDB dbm.DB
exec *vote
policy wcom.WalletBizPolicy
cfg *types.Chain33Config
q queue.Queue
qapi client.QueueProtocolAPI
execType types.ExecutorType
}
type testcase struct {
payload types.Message
expectExecErr error
expectCheckErr error
expectExecLocalErr error
expectExecDelErr error
priv crypto.PrivKey
execType int
index int
}
// InitEnv init env
func (mock *testExecMock) InitEnv() {
mock.cfg = testCfg
util.ResetDatadir(mock.cfg.GetModuleConfig(), "$TEMP/")
mock.q = queue.New("channel")
mock.q.SetConfig(mock.cfg)
mock.qapi, _ = client.New(mock.q.Client(), nil)
mock.initExec()
}
func (mock *testExecMock) FreeEnv() {
util.CloseTestDB(mock.dbDir, mock.stateDB)
}
func (mock *testExecMock) initExec() {
mock.dbDir, mock.stateDB, mock.localDB = util.CreateTestDB()
exec := newVote()
exec.SetAPI(mock.qapi)
exec.SetStateDB(mock.stateDB)
exec.SetLocalDB(mock.localDB)
exec.SetEnv(testHeight, testBlockTime, 1539918074)
mock.exec = exec.(*vote)
mock.execType = types.LoadExecutorType(vty.VoteX)
}
func decodePrivKey(priv string) (crypto.PrivKey, error) {
c, err := crypto.New(crypto.GetName(types.SECP256K1))
if err != nil {
return nil, err
}
bytes, err := common.FromHex(priv[:])
if err != nil {
return nil, err
}
privKey, err := c.PrivKeyFromBytes(bytes)
if err != nil {
return nil, err
}
return privKey, nil
}
func createTx(mock *testExecMock, payload types.Message, privKey crypto.PrivKey) (*types.Transaction, error) {
action, _ := getActionName(payload)
tx, err := mock.execType.CreateTransaction(action, payload)
if err != nil {
return nil, errors.New("createTxErr:" + err.Error())
}
tx, err = types.FormatTx(mock.cfg, vty.VoteX, tx)
if err != nil {
return nil, errors.New("formatTxErr:" + err.Error())
}
tx.Sign(int32(types.SECP256K1), privKey)
return tx, nil
}
func getActionName(param types.Message) (string, error) {
if _, ok := param.(*vty.CreateGroup); ok {
return vty.NameCreateGroupAction, nil
} else if _, ok := param.(*vty.UpdateGroup); ok {
return vty.NameUpdateGroupAction, nil
} else if _, ok := param.(*vty.CreateVote); ok {
return vty.NameCreateVoteAction, nil
} else if _, ok := param.(*vty.CommitVote); ok {
return vty.NameCommitVoteAction, nil
} else if _, ok := param.(*vty.CloseVote); ok {
return vty.NameCloseVoteAction, nil
} else if _, ok := param.(*vty.UpdateMember); ok {
return vty.NameUpdateMemberAction, nil
} else {
return "", types.ErrActionNotSupport
}
}
func TestUtil(t *testing.T) {
heightIndex := dapp.HeightIndexStr(100, 0)
require.Equal(t, IDLen, len(formatGroupID(heightIndex)))
require.Equal(t, IDLen, len(formatVoteID(heightIndex)))
strs := []string{"a", "b", "c"}
require.True(t, checkSliceItemExist("a", strs))
require.False(t, checkSliceItemExist("d", strs))
require.False(t, checkSliceItemDuplicate(strs))
strs = append(strs, "c")
require.True(t, checkSliceItemDuplicate(strs))
members := make([]*vty.GroupMember, 0)
for _, addr := range testAddrs {
members = append(members, &vty.GroupMember{
Addr: addr,
})
}
require.True(t, checkMemberExist(testAddrs[0], members))
require.False(t, checkMemberExist("testaddr", members))
require.Equal(t, addrLen, len(testAddrs[0]))
}
func TestFilterVoteWithStatus(t *testing.T) {
currentTime := types.Now().Unix()
voteList := []*vty.VoteInfo{
{
BeginTimestamp: currentTime + 1,
},
{
EndTimestamp: currentTime + 1,
},
{
BeginTimestamp: currentTime,
EndTimestamp: currentTime,
},
{
Status: voteStatusClosed,
},
}
statusList := []uint32{voteStatusPending, voteStatusOngoing, voteStatusFinished, voteStatusClosed}
voteList = filterVoteWithStatus(voteList, 0, currentTime)
for i, info := range voteList {
require.Equal(t, statusList[i], info.Status)
}
for _, status := range statusList {
list := filterVoteWithStatus(voteList, status, currentTime)
require.Equal(t, 1, len(list))
require.Equal(t, status, list[0].Status)
}
}
package executor
import (
log "github.com/33cn/chain33/common/log/log15"
drivers "github.com/33cn/chain33/system/dapp"
"github.com/33cn/chain33/types"
votetypes "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 执行器相关定义
* 重载基类相关接口
*/
var (
//日志
elog = log.New("module", "vote.executor")
)
var driverName = votetypes.VoteX
// Init register dapp
func Init(name string, cfg *types.Chain33Config, sub []byte) {
drivers.Register(cfg, GetName(), newVote, cfg.GetDappFork(driverName, "Enable"))
InitExecType()
}
// InitExecType Init Exec Type
func InitExecType() {
ety := types.LoadExecutorType(driverName)
ety.InitFuncList(types.ListMethod(&vote{}))
}
type vote struct {
drivers.DriverBase
}
func newVote() drivers.Driver {
t := &vote{}
t.SetChild(t)
t.SetExecutorType(types.LoadExecutorType(driverName))
return t
}
// GetName get driver name
func GetName() string {
return newVote().GetName()
}
func (v *vote) GetDriverName() string {
return driverName
}
package types
import (
"github.com/33cn/chain33/pluginmgr"
"github.com/33cn/plugin/plugin/dapp/vote/commands"
"github.com/33cn/plugin/plugin/dapp/vote/executor"
"github.com/33cn/plugin/plugin/dapp/vote/rpc"
votetypes "github.com/33cn/plugin/plugin/dapp/vote/types"
)
/*
* 初始化dapp相关的组件
*/
func init() {
pluginmgr.Register(&pluginmgr.PluginBase{
Name: votetypes.VoteX,
ExecName: executor.GetName(),
Exec: executor.Init,
Cmd: commands.Cmd,
RPC: rpc.Init,
})
}
all:
bash ./create_protobuf.sh
#!/bin/bash
# proto生成命令,将pb.go文件生成到types/目录下, chain33_path支持引用chain33框架的proto文件
chain33_path=$(go list -f '{{.Dir}}' "github.com/33cn/chain33")
protoc --go_out=plugins=grpc:../types ./*.proto --proto_path=. --proto_path="${chain33_path}/types/proto/"
syntax = "proto3";
package types;
// vote 合约交易行为总类型
message VoteAction {
int32 ty = 1;
oneof value {
CreateGroup createGroup = 2; //创建投票组
UpdateGroup updateGroup = 3; //更新组成员
CreateVote createVote = 4; //创建一个投票
CommitVote commitVote = 5; //组员提交投票
CloseVote closeVote = 6; //关闭投票
UpdateMember updateMember = 7; //更新用户信息
}
}
message GroupMember {
string addr = 1; //用户地址
uint32 voteWeight = 2; //投票权重, 不填时默认为1
string nickName = 3; //群昵称
}
//创建投票组
message CreateGroup {
string name = 1; //投票组名称
repeated string admins = 2; //管理员地址列表,创建者默认为管理员
repeated GroupMember members = 3; //组员
string description = 4; //描述
}
//更新投票组
message UpdateGroup {
string groupID = 1; //投票组ID
repeated GroupMember addMembers = 2; //需要增加的组成员
repeated string removeMembers = 3; //删除组成员的地址列表
repeated string addAdmins = 4; //增加管理员
repeated string removeAdmins = 5; //删除管理员
}
// 投票组信息
message GroupInfo {
string ID = 1; //投票组ID
string name = 2; //投票组名称
uint32 memberNum = 3; //组员数量
string creator = 4; //创建者
repeated string admins = 5; //管理员列表
repeated GroupMember members = 6; //成员列表
string description = 7; //描述信息
uint32 voteNum = 8; //投票数量
}
message GroupInfos {
repeated GroupInfo groupList = 1; //投票组信息列表
}
//投票选项
message VoteOption {
string option = 1; //投票选项
uint32 score = 2; //投票得分
}
// 创建投票交易,请求结构
message CreateVote {
string name = 1; //投票名称
string groupID = 2; //投票关联组
repeated string voteOptions = 3; //投票选项列表
int64 beginTimestamp = 4; //投票开始时间戳
int64 endTimestamp = 5; //投票结束时间戳
string description = 6; //描述信息
}
// 创建提交投票交易,请求结构
message CommitVote {
string voteID = 1; //投票ID
uint32 optionIndex = 2; //投票选项数组下标,下标对应投票内容
}
message CommitInfo {
string addr = 1; //提交地址
string txHash = 2; //提交交易哈希
uint32 voteWeight = 3; //投票权重
}
message CloseVote {
string voteID = 1; // 投票ID
}
message UpdateMember {
string name = 1; //用户名称
}
//投票信息
message VoteInfo {
string ID = 1; //投票ID
string name = 2; //投票名称
string creator = 3; //创建者
string groupID = 4; //投票关联的投票组
repeated VoteOption voteOptions = 5; //投票的选项
int64 beginTimestamp = 6; //投票开始时间戳
int64 endTimestamp = 7; //投票结束时间戳
repeated CommitInfo commitInfos = 8; //已投票的提交信息
string description = 9; //描述信息
uint32 status = 10; //状态,1即将开始,2正在进行,3已经结束,4已关闭
}
message VoteInfos {
repeated VoteInfo voteList = 1; //投票信息列表
}
message MemberInfo {
string addr = 1; //地址
string name = 2; //用户名称
repeated string groupIDs = 3; //所属投票组的ID列表
}
message MemberInfos {
repeated MemberInfo memberList = 1; //投票组成员信息列表
}
message ReqStrings {
repeated string items = 1; //请求项数组
}
//列表请求结构
message ReqListItem {
string startItemID = 1; //列表开始的ID,如请求组列表即groupID,不包含在结果中
int32 count = 2; //请求列表项数量, 0表示请求所有
int32 direction = 3; // 0表示根据ID降序,1表示升序,目前ID和区块高度正相关
}
message ReqListVote {
string groupID = 1; //指定所属组ID
ReqListItem listReq = 2; //列表请求
uint32 status = 3; //指定投票状态
}
message ReplyVoteList {
repeated VoteInfo voteList = 1; //投票列表
int64 currentTimestamp = 2; //当前系统时间
}
## 投票合约
基于区块链,公开透明的投票系统
### 交易功能
以下功能需要创建交易,并在链上执行
#### 创建投票组(CreateGroup)
- 设定投票组名称,管理员和组成员
- 默认创建者为管理员
- 可设定组成员的投票权重,默认都为1
- 投票组ID在交易执行后自动生成
##### 交易请求
```proto
//创建投票组
message CreateGroup {
string name = 1; //投票组名称
repeated string admins = 2; //管理员地址列表, 创建者默认为管理员
repeated GroupMember members = 3; //组员
string description = 4; //描述
}
message GroupMember {
string addr = 1; //用户地址
uint32 voteWeight = 2; //投票权重, 不填时默认为1
string nickName = 3; //群昵称
}
```
##### 交易回执
```proto
// 投票组信息
message GroupInfo {
string ID = 1; //投票组ID
string name = 2; //投票组名称
uint32 memberNum = 3; //组员数量
string creator = 4; //创建者
repeated string admins = 5; //管理员列表
repeated GroupMember members = 6; //成员列表
string description = 7; //描述信息
uint32 voteNum = 8; //投票数量
}
```
##### 创建交易示例
- 创建交易通用json rpc接口,Chain33.CreateTransaction
- actionName: CreateGroup
```bash
curl -kd '{"method":"Chain33.CreateTransaction","params":[{"execer":"vote","actionName":"CreateGroup","payload":{"name":"group30","admins":[],"members":[{"addr":"1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5","voteWeight":0}],"description":""}}],"id":0}' http://localhost:8801
```
#### 更新投票组(UpdateGroup)
指定投票组ID,并由管理员添加或删除组成员、管理员
##### 交易请求
```proto
message UpdateGroup {
string groupID = 1; //投票组ID
repeated GroupMember addMembers = 2; //需要增加的组成员
repeated string removeMembers = 3; //删除组成员的地址列表
repeated string addAdmins = 4; //增加管理员
repeated string removeAdmins = 5; //删除管理员
}
message GroupMember {
string addr = 1; //用户地址
uint32 voteWeight = 2; //投票权重, 不填时默认为1
string nickName = 3; //群昵称
}
```
##### 交易回执
```proto
// 投票组信息
message GroupInfo {
string ID = 1; //投票组ID
string name = 2; //投票组名称
uint32 memberNum = 3; //组员数量
string creator = 4; //创建者
repeated string admins = 5; //管理员列表
repeated GroupMember members = 6; //成员列表
string description = 7; //描述信息
uint32 voteNum = 8; //投票数量
}
```
##### 创建交易示例
- 创建交易通用json rpc接口,Chain33.CreateTransaction
- actionName: UpdateGroup
```bash
curl -kd '{"method":"Chain33.CreateTransaction","params":[{"execer":"vote","actionName":"UpdateGroup","payload":{"groupID":"g000000000000700000","addMembers":[{"addr":"member1","voteWeight":0},{"addr":"member2","voteWeight":0}],"removeMembers":["member3"],"addAdmins":["admin1"],"removeAdmins":["admin2"]}}],"id":0}' http://localhost:8801
```
#### 创建投票(CreateVote)
- 由管理员发起
- 设定投票名称,投票选项,关联投票组ID
- 关联投票组,即只有这些投票组的成员进行投票
- 投票ID在交易执行后生成
##### 交易请求
```proto
// 创建投票交易,请求结构
message CreateVote {
string name = 1; //投票名称
string groupID = 2; //投票关联组
repeated string voteOptions = 3; //投票选项列表
int64 beginTimestamp = 4; //投票开始时间戳
int64 endTimestamp = 5; //投票结束时间戳
string description = 6; //描述信息
}
```
##### 交易回执
```proto
//投票信息
message VoteInfo {
string ID = 1; //投票ID
string name = 2; //投票名称
string creator = 3; //创建者
string groupID = 4; //投票关联的投票组
repeated VoteOption voteOptions = 5; //投票的选项
int64 beginTimestamp = 6; //投票开始时间戳
int64 endTimestamp = 7; //投票结束时间戳
repeated CommitInfo commitInfos = 8; //已投票的提交信息
string description = 9; //描述信息
uint32 status = 10; //状态,1即将开始,2正在进行,3已经结束,4已关闭
}
//投票选项
message VoteOption {
string option = 1; //投票选项
uint32 score = 2; //投票得分
}
```
##### 创建交易示例
- 创建交易通用json rpc接口,Chain33.CreateTransaction
- actionName: CreateVote
```bash
curl -kd '{"method":"Chain33.CreateTransaction","params":[{"execer":"vote","actionName":"CreateVote","payload":{"name":"vote1","groupID":"g000000000000600000","voteOptions":["A","B","C"],"beginTimestamp":"1611562096","endTimestamp":"1611648496","description":""}}],"id":0}' http://localhost:8801
```
#### 提交投票(CommitVote)
- 投票组成员发起投票交易
- 指定投票ID,投票选项
- 投票选项使用数组下标标识,而不是选项内容
##### 交易请求
```proto
// 创建提交投票交易,请求结构
message CommitVote {
string voteID = 1; //投票ID
uint32 optionIndex = 2; //投票选项数组下标,下标对应投票内容
}
```
##### 交易回执
```proto
//投票信息
message CommitInfo {
string addr = 1; //提交地址
string txHash = 2; //提交交易哈希
uint32 voteWeight = 3; //投票权重
}
```
##### 创建交易示例
- 创建交易通用json rpc接口,Chain33.CreateTransaction
- actionName: CommitVote
```bash
curl -kd '{"method":"Chain33.CreateTransaction","params":[{"execer":"vote","actionName":"CommitVote","payload":{"voteID":"v000000000001300000","optionIndex":0}}],"id":0}' http://localhost:8801
```
#### 关闭投票(CloseVote)
- 由管理员发起,将指定投票关闭
##### 交易请求
```proto
message CloseVote {
string voteID = 1; // 投票ID
}
```
##### 交易回执
```proto
message VoteInfo {
string ID = 1; //投票ID
string name = 2; //投票名称
string creator = 3; //创建者
string groupID = 4; //投票关联的投票组
repeated VoteOption voteOptions = 5; //投票的选项
int64 beginTimestamp = 6; //投票开始时间戳
int64 endTimestamp = 7; //投票结束时间戳
repeated CommitInfo commitInfos = 8; //已投票的提交信息
string description = 9; //描述信息
uint32 status = 10; //状态,1即将开始,2正在进行,3已经结束,4已关闭
}
```
##### 创建交易示例
- 创建交易通用json rpc接口,Chain33.CreateTransaction
- actionName: CloseVote
```bash
curl -kd '{"method":"Chain33.CreateTransaction","params":[{"execer":"vote","actionName":"CloseVote","payload":{"voteID":"v000000000001300000"}}],"id":0}' http://localhost:8801
```
#### 更新用户信息(UpdateMember)
- 目前仅支持用户更新名称信息
##### 交易请求
```proto
message UpdateMember {
string name = 1; //用户名称
}
```
##### 交易回执
```proto
message MemberInfo {
string addr = 1; //地址
string name = 2; //用户名称
repeated string groupIDs = 3; //所属投票组的ID列表
}
```
##### 创建交易示例
- 创建交易通用json rpc接口,Chain33.CreateTransaction
- actionName: UpdateMember
```bash
curl -kd '{"method":"Chain33.CreateTransaction","params":[{"execer":"vote","actionName":"UpdateMember","payload":{"name":"name1"}}],"id":0}' http://localhost:8801
```
### 查询功能
以下功能为本地查询,无需创建交易
#### 获取组信息(GetGroups)
根据投票组ID查询组信息,支持多个同时查询
##### 请求结构
```proto
message ReqStrings {
repeated string items = 1; //投票组ID列表
}
```
##### 响应结构
```proto
message GroupInfos {
repeated GroupInfo groupList = 1; //投票组信息列表
}
```
##### 示例
- 通用查询json rpc接口,Chain33.Query
- funcName: GetGroups
```bash
curl -ksd '{"method":"Chain33.Query","params":[{"execer":"vote","funcName":"GetGroups","payload":{"items":["g000000000001700000","g000000000001800000"]}}],"id":0}' http://localhost:8801
```
#### 获取投票信息(GetVotes)
根据投票ID查询投票信息,支持多个同时查询
##### 请求结构
```proto
message ReqStrings {
repeated string items = 1; //投票ID列表
}
```
##### 响应结构
```proto
message ReplyVoteList {
repeated VoteInfo voteList = 1; //投票列表
int64 currentTimestamp = 2; //当前系统时间
}
```
##### 示例
- 通用查询json rpc接口,Chain33.Query
- funcName: GetVotes
```bash
curl -kd '{"method":"Chain33.Query","params":[{"execer":"vote","funcName":"GetVotes","payload":{"items":["v000000000001300000","v000000000001400000"]}}],"id":0}' http://localhost:8801
```
#### 获取成员信息(GetMembers)
根据用户地址,获取用户信息,支持多个同时查询
##### 请求结构
```proto
message ReqStrings {
repeated string items = 1; //用户地址列表
}
```
##### 响应结构
```proto
message MemberInfos {
repeated MemberInfo memberList = 1; //投票组成员信息列表
}
message MemberInfo {
string addr = 1; //地址
string name = 2; //用户名称
repeated string groupIDs = 3; //所属投票组的ID列表
}
```
##### 示例
- 通用查询json rpc接口,Chain33.Query
- funcName: GetMembers
-
```bash
curl -kd '{"method":"Chain33.Query","params":[{"execer":"vote","funcName":"GetMembers","payload":{"items":["1BQXS6TxaYYG5mADaWij4AxhZZUTpw95a5"]}}],"id":0}' http://localhost:8801
```
#### 获取投票组列表(ListGroup)
全局投票组有序列表
##### 请求结构
```proto
//列表请求结构
message ReqListItem {
string startItemID = 1; //列表开始的ID,如请求组列表即groupID,不包含在结果中
int32 count = 2; //请求列表项数量, 0表示请求所有
int32 direction = 3; // 0表示根据ID降序,1表示升序,目前ID和区块高度正相关
}
```
##### 响应结构
```proto
message GroupInfos {
repeated GroupInfo groupList = 1; //投票组信息列表
}
```
##### 示例
- 通用查询json rpc接口,Chain33.Query
- funcName: ListGroup
```bash
curl -kd '{"method":"Chain33.Query","params":[{"execer":"vote","funcName":"ListGroup","payload":{"startItemID":"","count":2,"direction":0}}],"id":0}' http://localhost:8801
```
#### 获取投票列表(ListVote)
- 获取全局投票列表
- 可指定groupID,获取指定组的投票列表
- 可指定投票状态进行分页查找, status 0表示不做状态区分
##### 请求结构
```proto
//列表请求结构
message ReqListVote {
string groupID = 1; //指定所属组ID
ReqListItem listReq = 2; //列表请求
uint32 status = 3; //指定投票状态, 1即将开始,2正在进行,3已经结束,4已关闭
}
message ReqListItem {
string startItemID = 1; //列表开始的ID,如请求组列表即groupID,不包含在结果中
int32 count = 2; //请求列表项数量, 0表示请求所有
int32 direction = 3; // 0表示根据ID降序,1表示升序,目前ID和区块高度正相关
}
```
##### 响应结构
```proto
message ReplyVoteList {
repeated VoteInfo voteList = 1; //投票列表
int64 currentTimestamp = 2; //当前系统时间
}
```
##### 示例
- 通用查询json rpc接口,Chain33.Query
- funcName: ListVote
```bash
curl -kd '{"method":"Chain33.Query","params":[{"execer":"vote","funcName":"ListVote","payload":{"groupID":"","listReq":{"startItemID":"","count":2,"direction":0}}}],"id":0}' http://localhost:8801
```
#### 获取用户列表(ListMember)
全局用户有序列表
##### 请求结构
```proto
//列表请求结构
message ReqListItem {
string startItemID = 1; //列表开始的ID,如请求组列表即groupID,不包含在结果中
int32 count = 2; //请求列表项数量, 0表示请求所有
int32 direction = 3; // 0表示根据ID降序,1表示升序,目前ID和区块高度正相关
}
```
##### 响应结构
```proto
message MemberInfos {
repeated MemberInfo memberList = 1; //投票组成员信息列表
}
```
##### 示例
- 通用查询json rpc接口,Chain33.Query
- funcName: ListMember
```bash
curl -kd '{"method":"Chain33.Query","params":[{"execer":"vote","funcName":"ListMember","payload":{"startItemID":"","count":1,"direction":1}}],"id":0}' http://localhost:8801
```
#### 错误码表
发送交易和查询等接口可能返回的错误
|名称 |含义
|---|---|
errEmptyName| 名称为空
errInvalidMemberWeights | 非法投票权重
errDuplicateMember | 投票组成员重复
errDuplicateAdmin | 投票组管理员重复
errInvalidVoteTime | 非法投票时间
errInvalidVoteOption | 非法投票选项
errVoteNotExist | 投票不存在
errGroupNotExist | 投票组不存在
errStateDBGet | 状态数据获取错误
errInvalidVoteID | 非法投票ID
errInvalidGroupID | 非法投票组ID
errInvalidOptionIndex | 非法投票索引
errAddrAlreadyVoted | 已完成投票
errVoteAlreadyFinished | 投票已结束
errVoteNotStarted | 投票未开始
errVoteAlreadyClosed | 投票已关闭
errAddrPermissionDenied | 地址没有权限
#### 其他
[投票合约proto源文件](proto/vote.proto)
package rpc
/*
* 实现json rpc和grpc service接口
* json rpc用Jrpc结构作为接收实例
* grpc使用channelClient结构作为接收实例
*/
package rpc
import (
rpctypes "github.com/33cn/chain33/rpc/types"
)
/*
* rpc相关结构定义和初始化
*/
// 实现grpc的service接口
type channelClient struct {
rpctypes.ChannelClient
}
// Jrpc 实现json rpc调用实例
type Jrpc struct {
cli *channelClient
}
// Grpc grpc
type Grpc struct {
*channelClient
}
// Init init rpc
func Init(name string, s rpctypes.RPCServer) {
cli := &channelClient{}
grpc := &Grpc{channelClient: cli}
cli.Init(name, s, &Jrpc{cli: cli}, grpc)
}
package types
import (
"reflect"
log "github.com/33cn/chain33/common/log/log15"
"github.com/33cn/chain33/types"
)
/*
* 交易相关类型定义
* 交易action通常有对应的log结构,用于交易回执日志记录
* 每一种action和log需要用id数值和name名称加以区分
*/
// action类型id和name,这些常量可以自定义修改
const (
TyUnknowAction = iota + 100
TyCreateGroupAction
TyUpdateGroupAction
TyCreateVoteAction
TyCommitVoteAction
TyCloseVoteAction
TyUpdateMemberAction
NameCreateGroupAction = "CreateGroup"
NameUpdateGroupAction = "UpdateGroup"
NameCreateVoteAction = "CreateVote"
NameCommitVoteAction = "CommitVote"
NameCloseVoteAction = "CloseVote"
NameUpdateMemberAction = "UpdateMember"
)
// log类型id值
const (
TyUnknownLog = iota + 100
TyCreateGroupLog
TyUpdateGroupLog
TyCreateVoteLog
TyCommitVoteLog
TyCloseVoteLog
TyUpdateMemberLog
NameCreateGroupLog = "CreateGroupLog"
NameUpdateGroupLog = "UpdateGroupLog"
NameCreateVoteLog = "CreateVoteLog"
NameCommitVoteLog = "CommitVoteLog"
NameCloseVoteLog = "CloseVoteLog"
NameUpdateMemberLog = "UpdateMemberLog"
)
var (
//VoteX 执行器名称定义
VoteX = "vote"
//定义actionMap
actionMap = map[string]int32{
NameCreateGroupAction: TyCreateGroupAction,
NameUpdateGroupAction: TyUpdateGroupAction,
NameCreateVoteAction: TyCreateVoteAction,
NameCommitVoteAction: TyCommitVoteAction,
NameCloseVoteAction: TyCloseVoteAction,
NameUpdateMemberAction: TyUpdateMemberAction,
}
//定义log的id和具体log类型及名称,填入具体自定义log类型
logMap = map[int64]*types.LogInfo{
TyCreateGroupLog: {Ty: reflect.TypeOf(GroupInfo{}), Name: NameCreateGroupLog},
TyUpdateGroupLog: {Ty: reflect.TypeOf(GroupInfo{}), Name: NameUpdateGroupLog},
TyCreateVoteLog: {Ty: reflect.TypeOf(VoteInfo{}), Name: NameCreateVoteLog},
TyCommitVoteLog: {Ty: reflect.TypeOf(CommitInfo{}), Name: NameCommitVoteLog},
TyCloseVoteLog: {Ty: reflect.TypeOf(VoteInfo{}), Name: NameCloseVoteLog},
TyUpdateMemberLog: {Ty: reflect.TypeOf(MemberInfo{}), Name: NameUpdateMemberLog},
}
tlog = log.New("module", "vote.types")
)
// init defines a register function
func init() {
types.AllowUserExec = append(types.AllowUserExec, []byte(VoteX))
//注册合约启用高度
types.RegFork(VoteX, InitFork)
types.RegExec(VoteX, InitExecutor)
}
// InitFork defines register fork
func InitFork(cfg *types.Chain33Config) {
cfg.RegisterDappFork(VoteX, "Enable", 0)
}
// InitExecutor defines register executor
func InitExecutor(cfg *types.Chain33Config) {
types.RegistorExecutor(VoteX, NewType(cfg))
}
type voteType struct {
types.ExecTypeBase
}
func NewType(cfg *types.Chain33Config) *voteType {
c := &voteType{}
c.SetChild(c)
c.SetConfig(cfg)
return c
}
// GetPayload 获取合约action结构
func (v *voteType) GetPayload() types.Message {
return &VoteAction{}
}
// GeTypeMap 获取合约action的id和name信息
func (v *voteType) GetTypeMap() map[string]int32 {
return actionMap
}
// GetLogMap 获取合约log相关信息
func (v *voteType) GetLogMap() map[int64]*types.LogInfo {
return logMap
}
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: vote.proto
package types
import (
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// vote 合约交易行为总类型
type VoteAction struct {
Ty int32 `protobuf:"varint,1,opt,name=ty,proto3" json:"ty,omitempty"`
// Types that are valid to be assigned to Value:
// *VoteAction_CreateGroup
// *VoteAction_UpdateGroup
// *VoteAction_CreateVote
// *VoteAction_CommitVote
// *VoteAction_CloseVote
// *VoteAction_UpdateMember
Value isVoteAction_Value `protobuf_oneof:"value"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *VoteAction) Reset() { *m = VoteAction{} }
func (m *VoteAction) String() string { return proto.CompactTextString(m) }
func (*VoteAction) ProtoMessage() {}
func (*VoteAction) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{0}
}
func (m *VoteAction) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VoteAction.Unmarshal(m, b)
}
func (m *VoteAction) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_VoteAction.Marshal(b, m, deterministic)
}
func (m *VoteAction) XXX_Merge(src proto.Message) {
xxx_messageInfo_VoteAction.Merge(m, src)
}
func (m *VoteAction) XXX_Size() int {
return xxx_messageInfo_VoteAction.Size(m)
}
func (m *VoteAction) XXX_DiscardUnknown() {
xxx_messageInfo_VoteAction.DiscardUnknown(m)
}
var xxx_messageInfo_VoteAction proto.InternalMessageInfo
func (m *VoteAction) GetTy() int32 {
if m != nil {
return m.Ty
}
return 0
}
type isVoteAction_Value interface {
isVoteAction_Value()
}
type VoteAction_CreateGroup struct {
CreateGroup *CreateGroup `protobuf:"bytes,2,opt,name=createGroup,proto3,oneof"`
}
type VoteAction_UpdateGroup struct {
UpdateGroup *UpdateGroup `protobuf:"bytes,3,opt,name=updateGroup,proto3,oneof"`
}
type VoteAction_CreateVote struct {
CreateVote *CreateVote `protobuf:"bytes,4,opt,name=createVote,proto3,oneof"`
}
type VoteAction_CommitVote struct {
CommitVote *CommitVote `protobuf:"bytes,5,opt,name=commitVote,proto3,oneof"`
}
type VoteAction_CloseVote struct {
CloseVote *CloseVote `protobuf:"bytes,6,opt,name=closeVote,proto3,oneof"`
}
type VoteAction_UpdateMember struct {
UpdateMember *UpdateMember `protobuf:"bytes,7,opt,name=updateMember,proto3,oneof"`
}
func (*VoteAction_CreateGroup) isVoteAction_Value() {}
func (*VoteAction_UpdateGroup) isVoteAction_Value() {}
func (*VoteAction_CreateVote) isVoteAction_Value() {}
func (*VoteAction_CommitVote) isVoteAction_Value() {}
func (*VoteAction_CloseVote) isVoteAction_Value() {}
func (*VoteAction_UpdateMember) isVoteAction_Value() {}
func (m *VoteAction) GetValue() isVoteAction_Value {
if m != nil {
return m.Value
}
return nil
}
func (m *VoteAction) GetCreateGroup() *CreateGroup {
if x, ok := m.GetValue().(*VoteAction_CreateGroup); ok {
return x.CreateGroup
}
return nil
}
func (m *VoteAction) GetUpdateGroup() *UpdateGroup {
if x, ok := m.GetValue().(*VoteAction_UpdateGroup); ok {
return x.UpdateGroup
}
return nil
}
func (m *VoteAction) GetCreateVote() *CreateVote {
if x, ok := m.GetValue().(*VoteAction_CreateVote); ok {
return x.CreateVote
}
return nil
}
func (m *VoteAction) GetCommitVote() *CommitVote {
if x, ok := m.GetValue().(*VoteAction_CommitVote); ok {
return x.CommitVote
}
return nil
}
func (m *VoteAction) GetCloseVote() *CloseVote {
if x, ok := m.GetValue().(*VoteAction_CloseVote); ok {
return x.CloseVote
}
return nil
}
func (m *VoteAction) GetUpdateMember() *UpdateMember {
if x, ok := m.GetValue().(*VoteAction_UpdateMember); ok {
return x.UpdateMember
}
return nil
}
// XXX_OneofWrappers is for the internal use of the proto package.
func (*VoteAction) XXX_OneofWrappers() []interface{} {
return []interface{}{
(*VoteAction_CreateGroup)(nil),
(*VoteAction_UpdateGroup)(nil),
(*VoteAction_CreateVote)(nil),
(*VoteAction_CommitVote)(nil),
(*VoteAction_CloseVote)(nil),
(*VoteAction_UpdateMember)(nil),
}
}
type GroupMember struct {
Addr string `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
VoteWeight uint32 `protobuf:"varint,2,opt,name=voteWeight,proto3" json:"voteWeight,omitempty"`
NickName string `protobuf:"bytes,3,opt,name=nickName,proto3" json:"nickName,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GroupMember) Reset() { *m = GroupMember{} }
func (m *GroupMember) String() string { return proto.CompactTextString(m) }
func (*GroupMember) ProtoMessage() {}
func (*GroupMember) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{1}
}
func (m *GroupMember) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GroupMember.Unmarshal(m, b)
}
func (m *GroupMember) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GroupMember.Marshal(b, m, deterministic)
}
func (m *GroupMember) XXX_Merge(src proto.Message) {
xxx_messageInfo_GroupMember.Merge(m, src)
}
func (m *GroupMember) XXX_Size() int {
return xxx_messageInfo_GroupMember.Size(m)
}
func (m *GroupMember) XXX_DiscardUnknown() {
xxx_messageInfo_GroupMember.DiscardUnknown(m)
}
var xxx_messageInfo_GroupMember proto.InternalMessageInfo
func (m *GroupMember) GetAddr() string {
if m != nil {
return m.Addr
}
return ""
}
func (m *GroupMember) GetVoteWeight() uint32 {
if m != nil {
return m.VoteWeight
}
return 0
}
func (m *GroupMember) GetNickName() string {
if m != nil {
return m.NickName
}
return ""
}
//创建投票组
type CreateGroup struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Admins []string `protobuf:"bytes,2,rep,name=admins,proto3" json:"admins,omitempty"`
Members []*GroupMember `protobuf:"bytes,3,rep,name=members,proto3" json:"members,omitempty"`
Description string `protobuf:"bytes,4,opt,name=description,proto3" json:"description,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateGroup) Reset() { *m = CreateGroup{} }
func (m *CreateGroup) String() string { return proto.CompactTextString(m) }
func (*CreateGroup) ProtoMessage() {}
func (*CreateGroup) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{2}
}
func (m *CreateGroup) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateGroup.Unmarshal(m, b)
}
func (m *CreateGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateGroup.Marshal(b, m, deterministic)
}
func (m *CreateGroup) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateGroup.Merge(m, src)
}
func (m *CreateGroup) XXX_Size() int {
return xxx_messageInfo_CreateGroup.Size(m)
}
func (m *CreateGroup) XXX_DiscardUnknown() {
xxx_messageInfo_CreateGroup.DiscardUnknown(m)
}
var xxx_messageInfo_CreateGroup proto.InternalMessageInfo
func (m *CreateGroup) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *CreateGroup) GetAdmins() []string {
if m != nil {
return m.Admins
}
return nil
}
func (m *CreateGroup) GetMembers() []*GroupMember {
if m != nil {
return m.Members
}
return nil
}
func (m *CreateGroup) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
//更新投票组
type UpdateGroup struct {
GroupID string `protobuf:"bytes,1,opt,name=groupID,proto3" json:"groupID,omitempty"`
AddMembers []*GroupMember `protobuf:"bytes,2,rep,name=addMembers,proto3" json:"addMembers,omitempty"`
RemoveMembers []string `protobuf:"bytes,3,rep,name=removeMembers,proto3" json:"removeMembers,omitempty"`
AddAdmins []string `protobuf:"bytes,4,rep,name=addAdmins,proto3" json:"addAdmins,omitempty"`
RemoveAdmins []string `protobuf:"bytes,5,rep,name=removeAdmins,proto3" json:"removeAdmins,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UpdateGroup) Reset() { *m = UpdateGroup{} }
func (m *UpdateGroup) String() string { return proto.CompactTextString(m) }
func (*UpdateGroup) ProtoMessage() {}
func (*UpdateGroup) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{3}
}
func (m *UpdateGroup) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UpdateGroup.Unmarshal(m, b)
}
func (m *UpdateGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UpdateGroup.Marshal(b, m, deterministic)
}
func (m *UpdateGroup) XXX_Merge(src proto.Message) {
xxx_messageInfo_UpdateGroup.Merge(m, src)
}
func (m *UpdateGroup) XXX_Size() int {
return xxx_messageInfo_UpdateGroup.Size(m)
}
func (m *UpdateGroup) XXX_DiscardUnknown() {
xxx_messageInfo_UpdateGroup.DiscardUnknown(m)
}
var xxx_messageInfo_UpdateGroup proto.InternalMessageInfo
func (m *UpdateGroup) GetGroupID() string {
if m != nil {
return m.GroupID
}
return ""
}
func (m *UpdateGroup) GetAddMembers() []*GroupMember {
if m != nil {
return m.AddMembers
}
return nil
}
func (m *UpdateGroup) GetRemoveMembers() []string {
if m != nil {
return m.RemoveMembers
}
return nil
}
func (m *UpdateGroup) GetAddAdmins() []string {
if m != nil {
return m.AddAdmins
}
return nil
}
func (m *UpdateGroup) GetRemoveAdmins() []string {
if m != nil {
return m.RemoveAdmins
}
return nil
}
// 投票组信息
type GroupInfo struct {
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
MemberNum uint32 `protobuf:"varint,3,opt,name=memberNum,proto3" json:"memberNum,omitempty"`
Creator string `protobuf:"bytes,4,opt,name=creator,proto3" json:"creator,omitempty"`
Admins []string `protobuf:"bytes,5,rep,name=admins,proto3" json:"admins,omitempty"`
Members []*GroupMember `protobuf:"bytes,6,rep,name=members,proto3" json:"members,omitempty"`
Description string `protobuf:"bytes,7,opt,name=description,proto3" json:"description,omitempty"`
VoteNum uint32 `protobuf:"varint,8,opt,name=voteNum,proto3" json:"voteNum,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GroupInfo) Reset() { *m = GroupInfo{} }
func (m *GroupInfo) String() string { return proto.CompactTextString(m) }
func (*GroupInfo) ProtoMessage() {}
func (*GroupInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{4}
}
func (m *GroupInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GroupInfo.Unmarshal(m, b)
}
func (m *GroupInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GroupInfo.Marshal(b, m, deterministic)
}
func (m *GroupInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_GroupInfo.Merge(m, src)
}
func (m *GroupInfo) XXX_Size() int {
return xxx_messageInfo_GroupInfo.Size(m)
}
func (m *GroupInfo) XXX_DiscardUnknown() {
xxx_messageInfo_GroupInfo.DiscardUnknown(m)
}
var xxx_messageInfo_GroupInfo proto.InternalMessageInfo
func (m *GroupInfo) GetID() string {
if m != nil {
return m.ID
}
return ""
}
func (m *GroupInfo) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *GroupInfo) GetMemberNum() uint32 {
if m != nil {
return m.MemberNum
}
return 0
}
func (m *GroupInfo) GetCreator() string {
if m != nil {
return m.Creator
}
return ""
}
func (m *GroupInfo) GetAdmins() []string {
if m != nil {
return m.Admins
}
return nil
}
func (m *GroupInfo) GetMembers() []*GroupMember {
if m != nil {
return m.Members
}
return nil
}
func (m *GroupInfo) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *GroupInfo) GetVoteNum() uint32 {
if m != nil {
return m.VoteNum
}
return 0
}
type GroupInfos struct {
GroupList []*GroupInfo `protobuf:"bytes,1,rep,name=groupList,proto3" json:"groupList,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GroupInfos) Reset() { *m = GroupInfos{} }
func (m *GroupInfos) String() string { return proto.CompactTextString(m) }
func (*GroupInfos) ProtoMessage() {}
func (*GroupInfos) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{5}
}
func (m *GroupInfos) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GroupInfos.Unmarshal(m, b)
}
func (m *GroupInfos) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GroupInfos.Marshal(b, m, deterministic)
}
func (m *GroupInfos) XXX_Merge(src proto.Message) {
xxx_messageInfo_GroupInfos.Merge(m, src)
}
func (m *GroupInfos) XXX_Size() int {
return xxx_messageInfo_GroupInfos.Size(m)
}
func (m *GroupInfos) XXX_DiscardUnknown() {
xxx_messageInfo_GroupInfos.DiscardUnknown(m)
}
var xxx_messageInfo_GroupInfos proto.InternalMessageInfo
func (m *GroupInfos) GetGroupList() []*GroupInfo {
if m != nil {
return m.GroupList
}
return nil
}
//投票选项
type VoteOption struct {
Option string `protobuf:"bytes,1,opt,name=option,proto3" json:"option,omitempty"`
Score uint32 `protobuf:"varint,2,opt,name=score,proto3" json:"score,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *VoteOption) Reset() { *m = VoteOption{} }
func (m *VoteOption) String() string { return proto.CompactTextString(m) }
func (*VoteOption) ProtoMessage() {}
func (*VoteOption) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{6}
}
func (m *VoteOption) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VoteOption.Unmarshal(m, b)
}
func (m *VoteOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_VoteOption.Marshal(b, m, deterministic)
}
func (m *VoteOption) XXX_Merge(src proto.Message) {
xxx_messageInfo_VoteOption.Merge(m, src)
}
func (m *VoteOption) XXX_Size() int {
return xxx_messageInfo_VoteOption.Size(m)
}
func (m *VoteOption) XXX_DiscardUnknown() {
xxx_messageInfo_VoteOption.DiscardUnknown(m)
}
var xxx_messageInfo_VoteOption proto.InternalMessageInfo
func (m *VoteOption) GetOption() string {
if m != nil {
return m.Option
}
return ""
}
func (m *VoteOption) GetScore() uint32 {
if m != nil {
return m.Score
}
return 0
}
// 创建投票交易,请求结构
type CreateVote struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
GroupID string `protobuf:"bytes,2,opt,name=groupID,proto3" json:"groupID,omitempty"`
VoteOptions []string `protobuf:"bytes,3,rep,name=voteOptions,proto3" json:"voteOptions,omitempty"`
BeginTimestamp int64 `protobuf:"varint,4,opt,name=beginTimestamp,proto3" json:"beginTimestamp,omitempty"`
EndTimestamp int64 `protobuf:"varint,5,opt,name=endTimestamp,proto3" json:"endTimestamp,omitempty"`
Description string `protobuf:"bytes,6,opt,name=description,proto3" json:"description,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CreateVote) Reset() { *m = CreateVote{} }
func (m *CreateVote) String() string { return proto.CompactTextString(m) }
func (*CreateVote) ProtoMessage() {}
func (*CreateVote) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{7}
}
func (m *CreateVote) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CreateVote.Unmarshal(m, b)
}
func (m *CreateVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CreateVote.Marshal(b, m, deterministic)
}
func (m *CreateVote) XXX_Merge(src proto.Message) {
xxx_messageInfo_CreateVote.Merge(m, src)
}
func (m *CreateVote) XXX_Size() int {
return xxx_messageInfo_CreateVote.Size(m)
}
func (m *CreateVote) XXX_DiscardUnknown() {
xxx_messageInfo_CreateVote.DiscardUnknown(m)
}
var xxx_messageInfo_CreateVote proto.InternalMessageInfo
func (m *CreateVote) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *CreateVote) GetGroupID() string {
if m != nil {
return m.GroupID
}
return ""
}
func (m *CreateVote) GetVoteOptions() []string {
if m != nil {
return m.VoteOptions
}
return nil
}
func (m *CreateVote) GetBeginTimestamp() int64 {
if m != nil {
return m.BeginTimestamp
}
return 0
}
func (m *CreateVote) GetEndTimestamp() int64 {
if m != nil {
return m.EndTimestamp
}
return 0
}
func (m *CreateVote) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
// 创建提交投票交易,请求结构
type CommitVote struct {
VoteID string `protobuf:"bytes,1,opt,name=voteID,proto3" json:"voteID,omitempty"`
OptionIndex uint32 `protobuf:"varint,2,opt,name=optionIndex,proto3" json:"optionIndex,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CommitVote) Reset() { *m = CommitVote{} }
func (m *CommitVote) String() string { return proto.CompactTextString(m) }
func (*CommitVote) ProtoMessage() {}
func (*CommitVote) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{8}
}
func (m *CommitVote) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CommitVote.Unmarshal(m, b)
}
func (m *CommitVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CommitVote.Marshal(b, m, deterministic)
}
func (m *CommitVote) XXX_Merge(src proto.Message) {
xxx_messageInfo_CommitVote.Merge(m, src)
}
func (m *CommitVote) XXX_Size() int {
return xxx_messageInfo_CommitVote.Size(m)
}
func (m *CommitVote) XXX_DiscardUnknown() {
xxx_messageInfo_CommitVote.DiscardUnknown(m)
}
var xxx_messageInfo_CommitVote proto.InternalMessageInfo
func (m *CommitVote) GetVoteID() string {
if m != nil {
return m.VoteID
}
return ""
}
func (m *CommitVote) GetOptionIndex() uint32 {
if m != nil {
return m.OptionIndex
}
return 0
}
type CommitInfo struct {
Addr string `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
TxHash string `protobuf:"bytes,2,opt,name=txHash,proto3" json:"txHash,omitempty"`
VoteWeight uint32 `protobuf:"varint,3,opt,name=voteWeight,proto3" json:"voteWeight,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CommitInfo) Reset() { *m = CommitInfo{} }
func (m *CommitInfo) String() string { return proto.CompactTextString(m) }
func (*CommitInfo) ProtoMessage() {}
func (*CommitInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{9}
}
func (m *CommitInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CommitInfo.Unmarshal(m, b)
}
func (m *CommitInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CommitInfo.Marshal(b, m, deterministic)
}
func (m *CommitInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_CommitInfo.Merge(m, src)
}
func (m *CommitInfo) XXX_Size() int {
return xxx_messageInfo_CommitInfo.Size(m)
}
func (m *CommitInfo) XXX_DiscardUnknown() {
xxx_messageInfo_CommitInfo.DiscardUnknown(m)
}
var xxx_messageInfo_CommitInfo proto.InternalMessageInfo
func (m *CommitInfo) GetAddr() string {
if m != nil {
return m.Addr
}
return ""
}
func (m *CommitInfo) GetTxHash() string {
if m != nil {
return m.TxHash
}
return ""
}
func (m *CommitInfo) GetVoteWeight() uint32 {
if m != nil {
return m.VoteWeight
}
return 0
}
type CloseVote struct {
VoteID string `protobuf:"bytes,1,opt,name=voteID,proto3" json:"voteID,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *CloseVote) Reset() { *m = CloseVote{} }
func (m *CloseVote) String() string { return proto.CompactTextString(m) }
func (*CloseVote) ProtoMessage() {}
func (*CloseVote) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{10}
}
func (m *CloseVote) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseVote.Unmarshal(m, b)
}
func (m *CloseVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_CloseVote.Marshal(b, m, deterministic)
}
func (m *CloseVote) XXX_Merge(src proto.Message) {
xxx_messageInfo_CloseVote.Merge(m, src)
}
func (m *CloseVote) XXX_Size() int {
return xxx_messageInfo_CloseVote.Size(m)
}
func (m *CloseVote) XXX_DiscardUnknown() {
xxx_messageInfo_CloseVote.DiscardUnknown(m)
}
var xxx_messageInfo_CloseVote proto.InternalMessageInfo
func (m *CloseVote) GetVoteID() string {
if m != nil {
return m.VoteID
}
return ""
}
type UpdateMember struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *UpdateMember) Reset() { *m = UpdateMember{} }
func (m *UpdateMember) String() string { return proto.CompactTextString(m) }
func (*UpdateMember) ProtoMessage() {}
func (*UpdateMember) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{11}
}
func (m *UpdateMember) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UpdateMember.Unmarshal(m, b)
}
func (m *UpdateMember) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_UpdateMember.Marshal(b, m, deterministic)
}
func (m *UpdateMember) XXX_Merge(src proto.Message) {
xxx_messageInfo_UpdateMember.Merge(m, src)
}
func (m *UpdateMember) XXX_Size() int {
return xxx_messageInfo_UpdateMember.Size(m)
}
func (m *UpdateMember) XXX_DiscardUnknown() {
xxx_messageInfo_UpdateMember.DiscardUnknown(m)
}
var xxx_messageInfo_UpdateMember proto.InternalMessageInfo
func (m *UpdateMember) GetName() string {
if m != nil {
return m.Name
}
return ""
}
//投票信息
type VoteInfo struct {
ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
Creator string `protobuf:"bytes,3,opt,name=creator,proto3" json:"creator,omitempty"`
GroupID string `protobuf:"bytes,4,opt,name=groupID,proto3" json:"groupID,omitempty"`
VoteOptions []*VoteOption `protobuf:"bytes,5,rep,name=voteOptions,proto3" json:"voteOptions,omitempty"`
BeginTimestamp int64 `protobuf:"varint,6,opt,name=beginTimestamp,proto3" json:"beginTimestamp,omitempty"`
EndTimestamp int64 `protobuf:"varint,7,opt,name=endTimestamp,proto3" json:"endTimestamp,omitempty"`
CommitInfos []*CommitInfo `protobuf:"bytes,8,rep,name=commitInfos,proto3" json:"commitInfos,omitempty"`
Description string `protobuf:"bytes,9,opt,name=description,proto3" json:"description,omitempty"`
Status uint32 `protobuf:"varint,10,opt,name=status,proto3" json:"status,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *VoteInfo) Reset() { *m = VoteInfo{} }
func (m *VoteInfo) String() string { return proto.CompactTextString(m) }
func (*VoteInfo) ProtoMessage() {}
func (*VoteInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{12}
}
func (m *VoteInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VoteInfo.Unmarshal(m, b)
}
func (m *VoteInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_VoteInfo.Marshal(b, m, deterministic)
}
func (m *VoteInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_VoteInfo.Merge(m, src)
}
func (m *VoteInfo) XXX_Size() int {
return xxx_messageInfo_VoteInfo.Size(m)
}
func (m *VoteInfo) XXX_DiscardUnknown() {
xxx_messageInfo_VoteInfo.DiscardUnknown(m)
}
var xxx_messageInfo_VoteInfo proto.InternalMessageInfo
func (m *VoteInfo) GetID() string {
if m != nil {
return m.ID
}
return ""
}
func (m *VoteInfo) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *VoteInfo) GetCreator() string {
if m != nil {
return m.Creator
}
return ""
}
func (m *VoteInfo) GetGroupID() string {
if m != nil {
return m.GroupID
}
return ""
}
func (m *VoteInfo) GetVoteOptions() []*VoteOption {
if m != nil {
return m.VoteOptions
}
return nil
}
func (m *VoteInfo) GetBeginTimestamp() int64 {
if m != nil {
return m.BeginTimestamp
}
return 0
}
func (m *VoteInfo) GetEndTimestamp() int64 {
if m != nil {
return m.EndTimestamp
}
return 0
}
func (m *VoteInfo) GetCommitInfos() []*CommitInfo {
if m != nil {
return m.CommitInfos
}
return nil
}
func (m *VoteInfo) GetDescription() string {
if m != nil {
return m.Description
}
return ""
}
func (m *VoteInfo) GetStatus() uint32 {
if m != nil {
return m.Status
}
return 0
}
type VoteInfos struct {
VoteList []*VoteInfo `protobuf:"bytes,1,rep,name=voteList,proto3" json:"voteList,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *VoteInfos) Reset() { *m = VoteInfos{} }
func (m *VoteInfos) String() string { return proto.CompactTextString(m) }
func (*VoteInfos) ProtoMessage() {}
func (*VoteInfos) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{13}
}
func (m *VoteInfos) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VoteInfos.Unmarshal(m, b)
}
func (m *VoteInfos) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_VoteInfos.Marshal(b, m, deterministic)
}
func (m *VoteInfos) XXX_Merge(src proto.Message) {
xxx_messageInfo_VoteInfos.Merge(m, src)
}
func (m *VoteInfos) XXX_Size() int {
return xxx_messageInfo_VoteInfos.Size(m)
}
func (m *VoteInfos) XXX_DiscardUnknown() {
xxx_messageInfo_VoteInfos.DiscardUnknown(m)
}
var xxx_messageInfo_VoteInfos proto.InternalMessageInfo
func (m *VoteInfos) GetVoteList() []*VoteInfo {
if m != nil {
return m.VoteList
}
return nil
}
type MemberInfo struct {
Addr string `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
GroupIDs []string `protobuf:"bytes,3,rep,name=groupIDs,proto3" json:"groupIDs,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberInfo) Reset() { *m = MemberInfo{} }
func (m *MemberInfo) String() string { return proto.CompactTextString(m) }
func (*MemberInfo) ProtoMessage() {}
func (*MemberInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{14}
}
func (m *MemberInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MemberInfo.Unmarshal(m, b)
}
func (m *MemberInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MemberInfo.Marshal(b, m, deterministic)
}
func (m *MemberInfo) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberInfo.Merge(m, src)
}
func (m *MemberInfo) XXX_Size() int {
return xxx_messageInfo_MemberInfo.Size(m)
}
func (m *MemberInfo) XXX_DiscardUnknown() {
xxx_messageInfo_MemberInfo.DiscardUnknown(m)
}
var xxx_messageInfo_MemberInfo proto.InternalMessageInfo
func (m *MemberInfo) GetAddr() string {
if m != nil {
return m.Addr
}
return ""
}
func (m *MemberInfo) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *MemberInfo) GetGroupIDs() []string {
if m != nil {
return m.GroupIDs
}
return nil
}
type MemberInfos struct {
MemberList []*MemberInfo `protobuf:"bytes,1,rep,name=memberList,proto3" json:"memberList,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *MemberInfos) Reset() { *m = MemberInfos{} }
func (m *MemberInfos) String() string { return proto.CompactTextString(m) }
func (*MemberInfos) ProtoMessage() {}
func (*MemberInfos) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{15}
}
func (m *MemberInfos) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MemberInfos.Unmarshal(m, b)
}
func (m *MemberInfos) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_MemberInfos.Marshal(b, m, deterministic)
}
func (m *MemberInfos) XXX_Merge(src proto.Message) {
xxx_messageInfo_MemberInfos.Merge(m, src)
}
func (m *MemberInfos) XXX_Size() int {
return xxx_messageInfo_MemberInfos.Size(m)
}
func (m *MemberInfos) XXX_DiscardUnknown() {
xxx_messageInfo_MemberInfos.DiscardUnknown(m)
}
var xxx_messageInfo_MemberInfos proto.InternalMessageInfo
func (m *MemberInfos) GetMemberList() []*MemberInfo {
if m != nil {
return m.MemberList
}
return nil
}
type ReqStrings struct {
Items []string `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReqStrings) Reset() { *m = ReqStrings{} }
func (m *ReqStrings) String() string { return proto.CompactTextString(m) }
func (*ReqStrings) ProtoMessage() {}
func (*ReqStrings) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{16}
}
func (m *ReqStrings) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReqStrings.Unmarshal(m, b)
}
func (m *ReqStrings) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReqStrings.Marshal(b, m, deterministic)
}
func (m *ReqStrings) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReqStrings.Merge(m, src)
}
func (m *ReqStrings) XXX_Size() int {
return xxx_messageInfo_ReqStrings.Size(m)
}
func (m *ReqStrings) XXX_DiscardUnknown() {
xxx_messageInfo_ReqStrings.DiscardUnknown(m)
}
var xxx_messageInfo_ReqStrings proto.InternalMessageInfo
func (m *ReqStrings) GetItems() []string {
if m != nil {
return m.Items
}
return nil
}
//列表请求结构
type ReqListItem struct {
StartItemID string `protobuf:"bytes,1,opt,name=startItemID,proto3" json:"startItemID,omitempty"`
Count int32 `protobuf:"varint,2,opt,name=count,proto3" json:"count,omitempty"`
Direction int32 `protobuf:"varint,3,opt,name=direction,proto3" json:"direction,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReqListItem) Reset() { *m = ReqListItem{} }
func (m *ReqListItem) String() string { return proto.CompactTextString(m) }
func (*ReqListItem) ProtoMessage() {}
func (*ReqListItem) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{17}
}
func (m *ReqListItem) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReqListItem.Unmarshal(m, b)
}
func (m *ReqListItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReqListItem.Marshal(b, m, deterministic)
}
func (m *ReqListItem) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReqListItem.Merge(m, src)
}
func (m *ReqListItem) XXX_Size() int {
return xxx_messageInfo_ReqListItem.Size(m)
}
func (m *ReqListItem) XXX_DiscardUnknown() {
xxx_messageInfo_ReqListItem.DiscardUnknown(m)
}
var xxx_messageInfo_ReqListItem proto.InternalMessageInfo
func (m *ReqListItem) GetStartItemID() string {
if m != nil {
return m.StartItemID
}
return ""
}
func (m *ReqListItem) GetCount() int32 {
if m != nil {
return m.Count
}
return 0
}
func (m *ReqListItem) GetDirection() int32 {
if m != nil {
return m.Direction
}
return 0
}
type ReqListVote struct {
GroupID string `protobuf:"bytes,1,opt,name=groupID,proto3" json:"groupID,omitempty"`
ListReq *ReqListItem `protobuf:"bytes,2,opt,name=listReq,proto3" json:"listReq,omitempty"`
Status uint32 `protobuf:"varint,3,opt,name=status,proto3" json:"status,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReqListVote) Reset() { *m = ReqListVote{} }
func (m *ReqListVote) String() string { return proto.CompactTextString(m) }
func (*ReqListVote) ProtoMessage() {}
func (*ReqListVote) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{18}
}
func (m *ReqListVote) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReqListVote.Unmarshal(m, b)
}
func (m *ReqListVote) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReqListVote.Marshal(b, m, deterministic)
}
func (m *ReqListVote) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReqListVote.Merge(m, src)
}
func (m *ReqListVote) XXX_Size() int {
return xxx_messageInfo_ReqListVote.Size(m)
}
func (m *ReqListVote) XXX_DiscardUnknown() {
xxx_messageInfo_ReqListVote.DiscardUnknown(m)
}
var xxx_messageInfo_ReqListVote proto.InternalMessageInfo
func (m *ReqListVote) GetGroupID() string {
if m != nil {
return m.GroupID
}
return ""
}
func (m *ReqListVote) GetListReq() *ReqListItem {
if m != nil {
return m.ListReq
}
return nil
}
func (m *ReqListVote) GetStatus() uint32 {
if m != nil {
return m.Status
}
return 0
}
type ReplyVoteList struct {
VoteList []*VoteInfo `protobuf:"bytes,1,rep,name=voteList,proto3" json:"voteList,omitempty"`
CurrentTimestamp int64 `protobuf:"varint,2,opt,name=currentTimestamp,proto3" json:"currentTimestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *ReplyVoteList) Reset() { *m = ReplyVoteList{} }
func (m *ReplyVoteList) String() string { return proto.CompactTextString(m) }
func (*ReplyVoteList) ProtoMessage() {}
func (*ReplyVoteList) Descriptor() ([]byte, []int) {
return fileDescriptor_21d31c94b62a6ac7, []int{19}
}
func (m *ReplyVoteList) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ReplyVoteList.Unmarshal(m, b)
}
func (m *ReplyVoteList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_ReplyVoteList.Marshal(b, m, deterministic)
}
func (m *ReplyVoteList) XXX_Merge(src proto.Message) {
xxx_messageInfo_ReplyVoteList.Merge(m, src)
}
func (m *ReplyVoteList) XXX_Size() int {
return xxx_messageInfo_ReplyVoteList.Size(m)
}
func (m *ReplyVoteList) XXX_DiscardUnknown() {
xxx_messageInfo_ReplyVoteList.DiscardUnknown(m)
}
var xxx_messageInfo_ReplyVoteList proto.InternalMessageInfo
func (m *ReplyVoteList) GetVoteList() []*VoteInfo {
if m != nil {
return m.VoteList
}
return nil
}
func (m *ReplyVoteList) GetCurrentTimestamp() int64 {
if m != nil {
return m.CurrentTimestamp
}
return 0
}
func init() {
proto.RegisterType((*VoteAction)(nil), "types.VoteAction")
proto.RegisterType((*GroupMember)(nil), "types.GroupMember")
proto.RegisterType((*CreateGroup)(nil), "types.CreateGroup")
proto.RegisterType((*UpdateGroup)(nil), "types.UpdateGroup")
proto.RegisterType((*GroupInfo)(nil), "types.GroupInfo")
proto.RegisterType((*GroupInfos)(nil), "types.GroupInfos")
proto.RegisterType((*VoteOption)(nil), "types.VoteOption")
proto.RegisterType((*CreateVote)(nil), "types.CreateVote")
proto.RegisterType((*CommitVote)(nil), "types.CommitVote")
proto.RegisterType((*CommitInfo)(nil), "types.CommitInfo")
proto.RegisterType((*CloseVote)(nil), "types.CloseVote")
proto.RegisterType((*UpdateMember)(nil), "types.UpdateMember")
proto.RegisterType((*VoteInfo)(nil), "types.VoteInfo")
proto.RegisterType((*VoteInfos)(nil), "types.VoteInfos")
proto.RegisterType((*MemberInfo)(nil), "types.MemberInfo")
proto.RegisterType((*MemberInfos)(nil), "types.MemberInfos")
proto.RegisterType((*ReqStrings)(nil), "types.ReqStrings")
proto.RegisterType((*ReqListItem)(nil), "types.ReqListItem")
proto.RegisterType((*ReqListVote)(nil), "types.ReqListVote")
proto.RegisterType((*ReplyVoteList)(nil), "types.ReplyVoteList")
}
func init() {
proto.RegisterFile("vote.proto", fileDescriptor_21d31c94b62a6ac7)
}
var fileDescriptor_21d31c94b62a6ac7 = []byte{
// 912 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x56, 0xdd, 0x8e, 0xe3, 0x34,
0x14, 0x6e, 0x92, 0x26, 0x6d, 0x4e, 0xa6, 0xcb, 0x60, 0x50, 0x15, 0xad, 0x56, 0xa8, 0x32, 0x08,
0x8d, 0x60, 0x55, 0xc1, 0x56, 0x42, 0x80, 0xb8, 0x60, 0x61, 0x05, 0xad, 0xc4, 0x0c, 0xc8, 0xc0,
0xc0, 0x0d, 0x17, 0x99, 0xc4, 0x74, 0x22, 0x9a, 0xa4, 0x13, 0xbb, 0xd5, 0xf4, 0x11, 0x78, 0x2e,
0xee, 0xb8, 0xe4, 0x2d, 0x90, 0x78, 0x08, 0xe4, 0xe3, 0xfc, 0x38, 0xfd, 0xd1, 0xce, 0xdc, 0xf5,
0x1c, 0x7f, 0xc7, 0x3e, 0xfe, 0xbe, 0xcf, 0xa7, 0x01, 0xd8, 0x16, 0x92, 0x4f, 0xd7, 0x65, 0x21,
0x0b, 0xe2, 0xca, 0xdd, 0x9a, 0x0b, 0xfa, 0x9f, 0x0d, 0x70, 0x5d, 0x48, 0xfe, 0x32, 0x96, 0x69,
0x91, 0x93, 0x27, 0x60, 0xcb, 0x5d, 0x68, 0x4d, 0xac, 0x0b, 0x97, 0xd9, 0x72, 0x47, 0x3e, 0x81,
0x20, 0x2e, 0x79, 0x24, 0xf9, 0xb7, 0x65, 0xb1, 0x59, 0x87, 0xf6, 0xc4, 0xba, 0x08, 0x5e, 0x90,
0x29, 0xd6, 0x4e, 0xbf, 0x6e, 0x57, 0xe6, 0x3d, 0x66, 0x02, 0x55, 0xdd, 0x66, 0x9d, 0x34, 0x75,
0x4e, 0xa7, 0xee, 0xe7, 0x76, 0x45, 0xd5, 0x19, 0x40, 0x32, 0x03, 0xd0, 0xdb, 0xa8, 0x9e, 0xc2,
0x3e, 0x96, 0xbd, 0xd9, 0x39, 0x4e, 0x2d, 0xcc, 0x7b, 0xcc, 0x80, 0x61, 0x51, 0x91, 0x65, 0xa9,
0xc4, 0x22, 0xb7, 0x5b, 0xd4, 0x2c, 0x60, 0x51, 0x13, 0x91, 0x8f, 0xc0, 0x8f, 0x57, 0x85, 0xd0,
0x07, 0x79, 0x58, 0x73, 0x5e, 0xd7, 0xd4, 0xf9, 0x79, 0x8f, 0xb5, 0x20, 0xf2, 0x19, 0x9c, 0xe9,
0x56, 0x2f, 0x79, 0x76, 0xc3, 0xcb, 0x70, 0x80, 0x45, 0x6f, 0x75, 0x2e, 0xa5, 0x97, 0xe6, 0x3d,
0xd6, 0x81, 0x7e, 0x35, 0x00, 0x77, 0x1b, 0xad, 0x36, 0x9c, 0xfe, 0x06, 0x01, 0x5e, 0x54, 0xe7,
0x09, 0x81, 0x7e, 0x94, 0x24, 0x25, 0x12, 0xee, 0x33, 0xfc, 0x4d, 0xde, 0xd1, 0x32, 0xfd, 0xc2,
0xd3, 0xe5, 0xad, 0x44, 0xc6, 0x47, 0xcc, 0xc8, 0x90, 0xa7, 0x30, 0xcc, 0xd3, 0xf8, 0x8f, 0xab,
0x28, 0xe3, 0xc8, 0xab, 0xcf, 0x9a, 0x98, 0xfe, 0x69, 0x41, 0x60, 0xa8, 0xa2, 0xf6, 0xcf, 0x15,
0xae, 0xda, 0x5f, 0xfd, 0x26, 0x63, 0xf0, 0xa2, 0x24, 0x4b, 0x73, 0x11, 0xda, 0x13, 0xe7, 0xc2,
0x67, 0x55, 0x44, 0x9e, 0xc3, 0x20, 0xc3, 0xae, 0x44, 0xe8, 0x4c, 0x1c, 0x43, 0x2e, 0xa3, 0x61,
0x56, 0x43, 0xc8, 0x04, 0x82, 0x84, 0x8b, 0xb8, 0x4c, 0xd7, 0xca, 0x37, 0xa8, 0x94, 0xcf, 0xcc,
0x14, 0xfd, 0xcb, 0x82, 0xc0, 0x50, 0x9a, 0x84, 0x30, 0x58, 0xaa, 0x1f, 0x8b, 0x57, 0x55, 0x3b,
0x75, 0x48, 0x5e, 0x00, 0x44, 0x49, 0x72, 0x59, 0x1d, 0x6e, 0x9f, 0x3c, 0xdc, 0x40, 0x91, 0xf7,
0x60, 0x54, 0xf2, 0xac, 0xd8, 0xf2, 0x4b, 0xa3, 0x67, 0x9f, 0x75, 0x93, 0xe4, 0x19, 0xf8, 0x51,
0x92, 0xbc, 0xd4, 0xd7, 0xed, 0x23, 0xa2, 0x4d, 0x10, 0x0a, 0x67, 0x1a, 0x5e, 0x01, 0x5c, 0x04,
0x74, 0x72, 0xf4, 0x5f, 0x0b, 0x7c, 0xec, 0x61, 0x91, 0xff, 0x5e, 0xa8, 0xe7, 0xd1, 0xb4, 0x6f,
0x2f, 0x5e, 0x35, 0xfc, 0xda, 0x06, 0xbf, 0xcf, 0xc0, 0xd7, 0x24, 0x5d, 0x6d, 0x32, 0x14, 0x68,
0xc4, 0xda, 0x84, 0x62, 0x01, 0x9d, 0x5b, 0x94, 0x15, 0x67, 0x75, 0x68, 0xe8, 0xe2, 0x9e, 0xd2,
0xc5, 0x7b, 0xb4, 0x2e, 0x83, 0x03, 0x5d, 0x54, 0x07, 0xca, 0x4d, 0xaa, 0xbb, 0x21, 0x76, 0x57,
0x87, 0xf4, 0x0b, 0x80, 0xe6, 0xaa, 0x82, 0x4c, 0xc1, 0x47, 0x81, 0xbe, 0x4b, 0x85, 0x0c, 0x2d,
0x3c, 0xf9, 0xdc, 0x3c, 0x59, 0xa1, 0x58, 0x0b, 0xa1, 0x9f, 0xeb, 0x41, 0xf2, 0xbd, 0x3e, 0x65,
0x0c, 0x5e, 0xa1, 0x5b, 0xd0, 0x6c, 0x55, 0x11, 0x79, 0x1b, 0x5c, 0x11, 0x17, 0x25, 0xaf, 0x8c,
0xad, 0x03, 0xfa, 0xb7, 0x05, 0xd0, 0x3e, 0xef, 0xa3, 0xb6, 0x35, 0xec, 0x63, 0x77, 0xed, 0x33,
0x81, 0x60, 0xdb, 0x1c, 0x5c, 0x1b, 0xc1, 0x4c, 0x91, 0xf7, 0xe1, 0xc9, 0x0d, 0x5f, 0xa6, 0xf9,
0x4f, 0x69, 0xc6, 0x85, 0x8c, 0xb2, 0x35, 0x72, 0xef, 0xb0, 0xbd, 0xac, 0x32, 0x04, 0xcf, 0x93,
0x16, 0xe5, 0x22, 0xaa, 0x93, 0xdb, 0x27, 0xd8, 0x3b, 0x34, 0xfe, 0x37, 0x00, 0xed, 0xd4, 0x51,
0x44, 0xa8, 0x56, 0x1a, 0xdb, 0x54, 0x91, 0xda, 0x47, 0x53, 0xb2, 0xc8, 0x13, 0x7e, 0x5f, 0xd1,
0x61, 0xa6, 0xe8, 0xaf, 0xf5, 0x3e, 0x68, 0xbd, 0x63, 0xa3, 0x62, 0x0c, 0x9e, 0xbc, 0x9f, 0x47,
0xe2, 0xb6, 0xa2, 0xa4, 0x8a, 0xf6, 0x46, 0x88, 0xb3, 0x3f, 0x42, 0xe8, 0xbb, 0xe0, 0x37, 0x33,
0xee, 0x54, 0x83, 0x94, 0xc2, 0x99, 0x39, 0xd3, 0x8e, 0x89, 0x42, 0xff, 0xb1, 0x61, 0xa8, 0x36,
0x79, 0xf0, 0xe3, 0x30, 0xec, 0xef, 0x74, 0xed, 0x6f, 0xe8, 0xdb, 0xef, 0xea, 0x3b, 0xeb, 0xea,
0xeb, 0xa2, 0x15, 0xeb, 0xf9, 0xde, 0x5a, 0xee, 0x75, 0x92, 0x7b, 0x0f, 0x92, 0x7c, 0x70, 0x44,
0xf2, 0x19, 0x04, 0x71, 0x23, 0x84, 0x08, 0x87, 0x9d, 0x06, 0x5a, 0x89, 0x98, 0x89, 0xda, 0xf7,
0x89, 0x7f, 0xf8, 0x10, 0xc7, 0xe0, 0x09, 0x19, 0xc9, 0x8d, 0x08, 0x01, 0x15, 0xaa, 0x22, 0xfa,
0x29, 0xf8, 0x35, 0xa7, 0x82, 0x7c, 0x08, 0x43, 0x75, 0x2d, 0xe3, 0x11, 0xbe, 0x61, 0xdc, 0x1c,
0x8f, 0x6d, 0x00, 0xf4, 0x07, 0x00, 0x2d, 0xd6, 0x49, 0xc7, 0x1c, 0xd3, 0xe4, 0x29, 0x0c, 0x2b,
0xaa, 0xeb, 0xc7, 0xd3, 0xc4, 0xf4, 0x4b, 0x08, 0xda, 0x1d, 0x05, 0xf9, 0x18, 0x40, 0x0f, 0x1a,
0xa3, 0x9f, 0x9a, 0x88, 0x16, 0xc7, 0x0c, 0x10, 0xa5, 0x00, 0x8c, 0xdf, 0xfd, 0x28, 0xcb, 0x34,
0x5f, 0x0a, 0xf5, 0xfc, 0x53, 0xc9, 0x33, 0x81, 0xb5, 0x3e, 0xd3, 0x01, 0x8d, 0x21, 0x60, 0xfc,
0x4e, 0xc1, 0x17, 0x92, 0x67, 0x8a, 0x3a, 0x21, 0xa3, 0x12, 0x83, 0xc6, 0x51, 0x66, 0x4a, 0x6d,
0x13, 0x17, 0x9b, 0x5c, 0xff, 0x3d, 0xba, 0x4c, 0x07, 0x6a, 0xf2, 0x26, 0x69, 0xc9, 0xf1, 0x4b,
0x06, 0xed, 0xe5, 0xb2, 0x36, 0x41, 0xb3, 0xe6, 0x10, 0xb4, 0xfd, 0xe9, 0xbf, 0xa3, 0xe7, 0x30,
0x58, 0xa5, 0x42, 0x32, 0x7e, 0xb7, 0xf7, 0xbd, 0x63, 0xf4, 0xc8, 0x6a, 0x88, 0xa1, 0xa2, 0xd3,
0x51, 0xf1, 0x16, 0x46, 0x8c, 0xaf, 0x57, 0xbb, 0xeb, 0x4a, 0x9c, 0x47, 0x29, 0x49, 0x3e, 0x80,
0xf3, 0x78, 0x53, 0x96, 0x3c, 0x97, 0xad, 0x35, 0x6d, 0xb4, 0xe6, 0x41, 0xfe, 0xc6, 0xc3, 0x0f,
0xba, 0xd9, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x1d, 0x74, 0x0c, 0x4b, 0xde, 0x09, 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