diff --git a/api/grpc/chain.go b/api/grpc/chain.go index 5d52865..3ed58ff 100644 --- a/api/grpc/chain.go +++ b/api/grpc/chain.go @@ -18,7 +18,12 @@ func GetChainStatus(cbs *ChainBrokerService) (*pb.Response, error) { } func GetValidators(cbs *ChainBrokerService) (*pb.Response, error) { - addresses := cbs.genesis.Addresses + admins := cbs.genesis.Admins + addresses := make([]string, 0) + for _, admin := range admins { + addresses = append(addresses, admin.Address) + } + v, err := json.Marshal(addresses) if err != nil { return nil, err diff --git a/config/bitxhub.toml b/config/bitxhub.toml index 6d94725..863e25f 100755 --- a/config/bitxhub.toml +++ b/config/bitxhub.toml @@ -61,9 +61,20 @@ solo = false type = "serial" # opensource version only supports serial type, commercial version supports serial and parallel types [genesis] -addresses = [ - "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", - "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", - "0x97c8B516D19edBf575D72a172Af7F418BE498C37", - "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" -] + [[genesis.admins]] + address = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013" + weight = 1 + [[genesis.admins]] + address = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34" + weight = 1 + [[genesis.admins]] + address = "0x97c8B516D19edBf575D72a172Af7F418BE498C37" + weight = 1 + [[genesis.admins]] + address = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" + weight = 1 + [genesis.strategy] + AppchainMgr = "SimpleMajority" + RuleMgr = "SimpleMajority" + NodeMgr = "SimpleMajority" + ServiceMgr = "SimpleMajority" \ No newline at end of file diff --git a/go.mod b/go.mod index bb2df2a..3be61c9 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,18 @@ require ( github.com/Rican7/retry v0.1.0 github.com/aristanetworks/goarista v0.0.0-20200310212843-2da4c1f5881b // indirect github.com/cbergoon/merkletree v0.2.0 + github.com/cheynewallace/tabby v1.1.1 github.com/common-nighthawk/go-figure v0.0.0-20190529165535-67e0ed34491a github.com/coreos/etcd v3.3.18+incompatible github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f // indirect github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/ethereum/go-ethereum v1.9.18 + github.com/fatih/color v1.7.0 github.com/gobuffalo/envy v1.9.0 // indirect github.com/gobuffalo/packd v1.0.0 github.com/gobuffalo/packr v1.30.1 github.com/gogo/protobuf v1.3.2 - github.com/golang/mock v1.4.4 + github.com/golang/mock v1.5.0 github.com/google/btree v1.0.0 github.com/grpc-ecosystem/go-grpc-middleware v1.2.2 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 @@ -27,13 +29,13 @@ require ( github.com/lestrrat-go/strftime v1.0.3 // indirect github.com/libp2p/go-libp2p-core v0.5.6 github.com/magiconair/properties v1.8.4 - github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210126064930-8245c5b45956 + github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210318102029-494ee3060b0c github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359 - github.com/meshplus/bitxhub-model v1.1.2-0.20210120083349-c7a006b03fcb + github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1 github.com/meshplus/go-libp2p-cert v0.0.0-20210125063330-7c25fd5b7a49 github.com/meshplus/go-lightp2p v0.0.0-20210120082108-df5a536a6192 github.com/mitchellh/go-homedir v1.1.0 - github.com/multiformats/go-multiaddr v0.2.2 + github.com/multiformats/go-multiaddr v0.3.0 github.com/orcaman/concurrent-map v0.0.0-20190826125027-8c72a8bb44f6 github.com/pelletier/go-toml v1.2.0 github.com/pkg/errors v0.9.1 @@ -48,7 +50,7 @@ require ( github.com/stretchr/testify v1.6.0 github.com/sykesm/zap-logfmt v0.0.4 // indirect github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d - github.com/tidwall/gjson v1.3.5 + github.com/tidwall/gjson v1.6.8 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 github.com/urfave/cli v1.22.1 github.com/wasmerio/go-ext-wasm v0.3.1 @@ -57,7 +59,6 @@ require ( go.uber.org/atomic v1.7.0 go.uber.org/multierr v1.6.0 // indirect go.uber.org/zap v1.16.0 // indirect - golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 // indirect google.golang.org/genproto v0.0.0-20201119123407-9b1e624d6bc4 // indirect google.golang.org/grpc v1.33.2 ) diff --git a/go.sum b/go.sum index 9fc1935..53c3fc3 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,8 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cheynewallace/tabby v1.1.1 h1:JvUR8waht4Y0S3JF17G6Vhyt+FRhnqVCkk8l4YrOU54= +github.com/cheynewallace/tabby v1.1.1/go.mod h1:Pba/6cUL8uYqvOc9RkyvFbHGrQ9wShyrn6/S/1OYVys= github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/common-nighthawk/go-figure v0.0.0-20190529165535-67e0ed34491a h1:kTv7wPomOuRf17BKQKO5Y6GrKsYC52XHrjf26H6FdQU= @@ -208,8 +210,8 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4 h1:l75CXGRSwbaYNpl/Z2X1XIIAMSCquvXgpVZDhwEIJsc= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0 h1:jlYHihg//f7RRwuPfptm04yp4s7O6Kw8EZiVYIGcH0g= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= @@ -313,6 +315,8 @@ github.com/ipfs/go-cid v0.0.3/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUP github.com/ipfs/go-cid v0.0.4/go.mod h1:4LLaPOQwmk5z9LBgQnpkivrx8BJjUyGwTXCd5Xfj6+M= github.com/ipfs/go-cid v0.0.5 h1:o0Ix8e/ql7Zb5UVUJEUfjsWCIY8t48++9lR8qi6oiJU= github.com/ipfs/go-cid v0.0.5/go.mod h1:plgt+Y5MnOey4vO4UlUazGqdbEXuFYitED67FexhXog= +github.com/ipfs/go-cid v0.0.7 h1:ysQJVJA3fNDF1qigJbsSQOdjhVLsOEoPdh0+R97k3jY= +github.com/ipfs/go-cid v0.0.7/go.mod h1:6Ux9z5e+HpkQdckYoX1PG/6xqKspzlEIR5SDmgqgC/I= github.com/ipfs/go-datastore v0.0.1/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.0/go.mod h1:d4KVXhMt913cLBEI/PXAy6ko+W7e9AhyAKBGh803qeE= github.com/ipfs/go-datastore v0.1.1/go.mod h1:w38XXW9kVFNp57Zj5knbKWM2T+KOZCGDRVNdgPHtbHw= @@ -592,6 +596,8 @@ github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZ github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= github.com/libp2p/go-yamux v1.3.6 h1:O5qcBXRcfqecvQ/My9NqDNHB3/5t58yuJYqthcKhhgE= github.com/libp2p/go-yamux v1.3.6/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow= +github.com/looplab/fsm v0.2.0 h1:M8hf5EF4AYLcT1FNKVUX8nu7D0xfp291iGeuigSxfrw= +github.com/looplab/fsm v0.2.0/go.mod h1:p+IElwgCnAByqr2DWMuNbPjgMwqcHvTRZZn3dvKEke0= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -617,8 +623,8 @@ github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/ github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210126064930-8245c5b45956 h1:f1CXhJSLo/JuTXLjv5PRtvLvzq7mxzD58Aet27k5QlU= -github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210126064930-8245c5b45956/go.mod h1:MHf0waxqnW4Qwfpq66jqvJP+FritN5OTs/8wlQcNlJY= +github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210318102029-494ee3060b0c h1:78bm/wsSv0IORdGw6xdoeFVsILn/hwG6vk4eAg3rgLE= +github.com/meshplus/bitxhub-core v0.1.0-rc1.0.20210318102029-494ee3060b0c/go.mod h1:G19Wrz1u66UmwaES/iLM19jmlv3APAZ5qfYOlNnIIZw= github.com/meshplus/bitxhub-kit v1.1.1 h1:vkPO88oA3+Kpc0N8lIgfj/U52KBuI+633hPbMYt1xm8= github.com/meshplus/bitxhub-kit v1.1.1/go.mod h1:r4l4iqn0RPJreb/OmoYKfjCjQJrXpZX++6Qc31VG/1k= github.com/meshplus/bitxhub-kit v1.1.2-0.20201021105954-468d0a9d7957/go.mod h1:r4l4iqn0RPJreb/OmoYKfjCjQJrXpZX++6Qc31VG/1k= @@ -628,8 +634,8 @@ github.com/meshplus/bitxhub-kit v1.1.2-0.20201203072410-8a0383a6870d/go.mod h1:K github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359 h1:GdgS14bnCF4b/a5zhQi2wlu92pHc9cfl6A1HcbO7zmE= github.com/meshplus/bitxhub-kit v1.1.2-0.20210112075018-319e668d6359/go.mod h1:KR7ZlXhII9n0Bu8viaZTScvXCYn0MCQnYlsTvHPp0XA= github.com/meshplus/bitxhub-model v1.1.2-0.20201021152621-0b3c17c54b23/go.mod h1:4qWBZx5wv7WZzUqiuBsbkQqQ2Ju8aOFpsoNpBBNy8Us= -github.com/meshplus/bitxhub-model v1.1.2-0.20210120083349-c7a006b03fcb h1:PxGQL22OVxozkvEgVvxHol9WDqCZlhyvnd0Bu0HBX1Y= -github.com/meshplus/bitxhub-model v1.1.2-0.20210120083349-c7a006b03fcb/go.mod h1:x3H+TL24wcByzHegenLfs+5PQkQGNsk8eCm31QJMa+Q= +github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1 h1:ziae0L0cbCMKp66OYzjZuU1WtNoB2TgfFhNIVWOTod4= +github.com/meshplus/bitxhub-model v1.1.2-0.20210309053945-afaea82e9fe1/go.mod h1:x3H+TL24wcByzHegenLfs+5PQkQGNsk8eCm31QJMa+Q= github.com/meshplus/go-libp2p-cert v0.0.0-20210120021632-1578cf63e06a h1:eg1BDjSOsz3cdH49kPE8c2XnIFlLTPEMJLqpofV/OEY= github.com/meshplus/go-libp2p-cert v0.0.0-20210120021632-1578cf63e06a/go.mod h1:rS4AYMqKypLn2IPEnHICP//V2v16SZo4CWUbwMdihl0= github.com/meshplus/go-libp2p-cert v0.0.0-20210125063330-7c25fd5b7a49 h1:F8dpLJZW6FxqinAQcZKTkoymZgxnqlNvTebNqWVMEYI= @@ -671,6 +677,8 @@ github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc= github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= github.com/multiformats/go-multiaddr v0.0.1/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.2/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= github.com/multiformats/go-multiaddr v0.0.4/go.mod h1:xKVEak1K9cS1VdmPZW3LSIb6lgmoS58qz/pzqmAxV44= @@ -680,6 +688,8 @@ github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y9 github.com/multiformats/go-multiaddr v0.2.1/go.mod h1:s/Apk6IyxfvMjDafnhJgJ3/46z7tZ04iMk5wP4QMGGE= github.com/multiformats/go-multiaddr v0.2.2 h1:XZLDTszBIJe6m0zF6ITBrEcZR73OPUhCBBS9rYAuUzI= github.com/multiformats/go-multiaddr v0.2.2/go.mod h1:NtfXiOtHvghW9KojvtySjH5y0u0xW5UouOmQQrn6a3Y= +github.com/multiformats/go-multiaddr v0.3.0 h1:z1Old9IYcUyMEtSbvwCOJ1jcrmJdU0LYH8aFBvZKzcQ= +github.com/multiformats/go-multiaddr v0.3.0/go.mod h1:dF9kph9wfJ+3VLAaeBqo9Of8x4fJxp6ggJGteB8HQTI= github.com/multiformats/go-multiaddr-dns v0.0.1/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.0.2/go.mod h1:9kWcqw/Pj6FwxAwW38n/9403szc57zJPs45fmnznu3Q= github.com/multiformats/go-multiaddr-dns v0.2.0 h1:YWJoIDwLePniH7OU5hBnDZV6SWuvJqJ0YtN6pLeH9zA= @@ -698,6 +708,8 @@ github.com/multiformats/go-multiaddr-net v0.1.5/go.mod h1:ilNnaM9HbmVFqsb/qcNysj github.com/multiformats/go-multibase v0.0.1/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= github.com/multiformats/go-multibase v0.0.2 h1:2pAgScmS1g9XjH7EtAfNhTuyrWYEWcxy0G5Wo85hWDA= github.com/multiformats/go-multibase v0.0.2/go.mod h1:bja2MqRZ3ggyXtZSEDKpl0uO/gviWFaSteVbWT51qgs= +github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= +github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= @@ -705,6 +717,8 @@ github.com/multiformats/go-multihash v0.0.9/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa github.com/multiformats/go-multihash v0.0.10/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc= github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= +github.com/multiformats/go-multihash v0.0.14 h1:QoBceQYQQtNUuf6s7wHxnE2c8bhbMqhfGzNI032se/I= +github.com/multiformats/go-multihash v0.0.14/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc= github.com/multiformats/go-multistream v0.1.0/go.mod h1:fJTiDfXJVmItycydCnNx4+wSzZ5NwG2FEVAI30fiovg= github.com/multiformats/go-multistream v0.1.1 h1:JlAdpIFhBhGRLxe9W6Om0w++Gd6KMWoFPZL/dEnm9nI= github.com/multiformats/go-multistream v0.1.1/go.mod h1:KmHZ40hzVxiaiwlj3MEbYgK9JFk2/9UktWZAF54Du38= @@ -712,6 +726,8 @@ github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXS github.com/multiformats/go-varint v0.0.2/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg= github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= @@ -884,12 +900,12 @@ github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ= github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= -github.com/tidwall/gjson v1.3.5 h1:2oW9FBNu8qt9jy5URgrzsVx/T/KSn3qn/smJQ0crlDQ= -github.com/tidwall/gjson v1.3.5/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= -github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc= -github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E= -github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tidwall/gjson v1.6.8 h1:CTmXMClGYPAmln7652e69B7OLXfTi5ABcPPwjIWUv7w= +github.com/tidwall/gjson v1.6.8/go.mod h1:zeFuBCIqD4sN/gmqBzZ4j7Jd6UcA2Fc56x7QFsv+8fI= +github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= +github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU= +github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= @@ -1080,9 +1096,8 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/internal/executor/contracts/appchain_manager.go b/internal/executor/contracts/appchain_manager.go index 5d6fc24..ed23496 100644 --- a/internal/executor/contracts/appchain_manager.go +++ b/internal/executor/contracts/appchain_manager.go @@ -1,6 +1,7 @@ package contracts import ( + "encoding/json" "fmt" "strconv" @@ -15,15 +16,69 @@ type AppchainManager struct { appchainMgr.AppchainManager } +type RegisterResult struct { + ChainID string `json:"chain_id"` + ProposalID string `json:"proposal_id"` +} + +func (am *AppchainManager) Manager(des string, proposalResult string, extra []byte) *boltvm.Response { + am.AppchainManager.Persister = am.Stub + chain := &appchainMgr.Appchain{} + if err := json.Unmarshal(extra, chain); err != nil { + return boltvm.Error("unmarshal json error:" + err.Error()) + } + + ok, err := am.AppchainManager.ChangeStatus(chain.ID, proposalResult) + if !ok { + return boltvm.Error(string(err)) + } + + if proposalResult == string(APPOVED) { + switch des { + case appchainMgr.EventRegister: + return am.CrossInvoke(constant.InterchainContractAddr.String(), "Register", pb.String(chain.ID)) + case appchainMgr.EventUpdate: + return responseWrapper(am.AppchainManager.UpdateAppchain(chain.ID, chain.Validators, chain.ConsensusType, chain.ChainType, chain.Name, chain.Desc, chain.Version, chain.PublicKey)) + } + } + + return boltvm.Success(nil) +} + // Register appchain managers registers appchain info caller is the appchain // manager address return appchain id and error func (am *AppchainManager) Register(validators string, consensusType int32, chainType, name, desc, version, pubkey string) *boltvm.Response { am.AppchainManager.Persister = am.Stub - res := am.CrossInvoke(constant.InterchainContractAddr.String(), "Register") + ok, idData := am.AppchainManager.Register(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey) + if ok { + return boltvm.Error("appchain has registered, chain id: " + string(idData)) + } + + ok, data := am.AppchainManager.GetAppchain(string(idData)) + if !ok { + return boltvm.Error("get appchain error: " + string(data)) + } + + res := am.CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal", + pb.String(am.Caller()), + pb.String(appchainMgr.EventRegister), + pb.String(string(AppchainMgr)), + pb.Bytes(data), + ) + if !res.Ok { return res } - return responseWrapper(am.AppchainManager.Register(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey)) + + res1 := RegisterResult{ + ChainID: am.Caller(), + ProposalID: string(res.Result), + } + resData, err := json.Marshal(res1) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(resData) } // UpdateAppchain updates approved appchain @@ -32,16 +87,10 @@ func (am *AppchainManager) UpdateAppchain(validators string, consensusType int32 return responseWrapper(am.AppchainManager.UpdateAppchain(am.Caller(), validators, consensusType, chainType, name, desc, version, pubkey)) } -//FetchAuditRecords fetches audit records by appchain id -func (am *AppchainManager) FetchAuditRecords(id string) *boltvm.Response { - am.AppchainManager.Persister = am.Stub - return responseWrapper(am.AppchainManager.FetchAuditRecords(id)) -} - // CountApprovedAppchains counts all approved appchains -func (am *AppchainManager) CountApprovedAppchains() *boltvm.Response { +func (am *AppchainManager) CountAvailableAppchains() *boltvm.Response { am.AppchainManager.Persister = am.Stub - return responseWrapper(am.AppchainManager.CountApprovedAppchains()) + return responseWrapper(am.AppchainManager.CountAvailableAppchains()) } // CountAppchains counts all appchains including approved, rejected or registered @@ -74,15 +123,6 @@ func (am *AppchainManager) GetPubKeyByChainID(id string) *boltvm.Response { return responseWrapper(am.AppchainManager.GetPubKeyByChainID(id)) } -// Audit bitxhub manager audit appchain register info -func (am *AppchainManager) Audit(proposer string, isApproved int32, desc string) *boltvm.Response { - am.AppchainManager.Persister = am.Stub - if res := am.IsAdmin(); !res.Ok { - return res - } - return responseWrapper(am.AppchainManager.Audit(proposer, isApproved, desc)) -} - func (am *AppchainManager) DeleteAppchain(cid string) *boltvm.Response { am.AppchainManager.Persister = am.Stub if res := am.IsAdmin(); !res.Ok { diff --git a/internal/executor/contracts/contracts_test.go b/internal/executor/contracts/contracts_test.go index 5f61c36..f37512c 100644 --- a/internal/executor/contracts/contracts_test.go +++ b/internal/executor/contracts/contracts_test.go @@ -4,23 +4,24 @@ import ( "crypto/sha256" "encoding/json" "fmt" + "strconv" "testing" "time" - "github.com/meshplus/bitxhub-kit/crypto" - "github.com/meshplus/bitxhub-kit/crypto/asym" - "github.com/stretchr/testify/require" - "github.com/golang/mock/gomock" appchainMgr "github.com/meshplus/bitxhub-core/appchain-mgr" "github.com/meshplus/bitxhub-core/boltvm" "github.com/meshplus/bitxhub-core/boltvm/mock_stub" "github.com/meshplus/bitxhub-core/validator" + "github.com/meshplus/bitxhub-kit/crypto" + "github.com/meshplus/bitxhub-kit/crypto/asym" "github.com/meshplus/bitxhub-kit/log" "github.com/meshplus/bitxhub-kit/types" "github.com/meshplus/bitxhub-model/constant" "github.com/meshplus/bitxhub-model/pb" + "github.com/meshplus/bitxhub/internal/repo" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var caller = "0x3f9d18f7c3a6e5e4c0b877fe3e688ab08840b997" @@ -74,7 +75,7 @@ func TestAppchainManager_Appchains(t *testing.T) { addr := types.NewAddress([]byte{byte(i)}).String() chain := &appchainMgr.Appchain{ - Status: appchainMgr.APPROVED, + Status: appchainMgr.AppchainAvailable, ID: addr, Name: "appchain" + addr, Validators: "", @@ -93,9 +94,7 @@ func TestAppchainManager_Appchains(t *testing.T) { } logger := log.NewWithModule("contracts") - registerResponse := &boltvm.Response{ - Ok: true, - } + am := &AppchainManager{ Stub: mockStub, } @@ -104,7 +103,8 @@ func TestAppchainManager_Appchains(t *testing.T) { mockStub.EXPECT().Logger().Return(logger).AnyTimes() // test for register - mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register").Return(registerResponse) + mockStub.EXPECT().Get(gomock.Any()).Return(true, chainsData[0]).AnyTimes() + mockStub.EXPECT().CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)) mockStub.EXPECT().Has(AppchainKey(caller)).Return(false).MaxTimes(3) am.Register(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType, chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey) @@ -133,31 +133,102 @@ func TestAppchainManager_Appchains(t *testing.T) { assert.Equal(t, "2", string(res.Result)) // test GetAppchain - mockStub.EXPECT().Get(AppchainKey(caller)).Return(true, chainsData[0]) - res = am.GetAppchain(caller) assert.Equal(t, true, res.Ok) assert.Equal(t, chainsData[0], res.Result) } -func TestAudit(t *testing.T) { - am, mockStub, _, _ := prepare(t) +func TestAppchainManager_Register(t *testing.T) { + am, mockStub, chains, chainsData := prepare(t) + logger := log.NewWithModule("contracts") - // test for DeleteAppchain + mockStub.EXPECT().Caller().Return(caller).AnyTimes() + mockStub.EXPECT().Get(gomock.Any()).Return(true, chainsData[0]).AnyTimes() + mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do( + func(key string, ret interface{}) bool { + chain := ret.(*appchainMgr.Appchain) + chain.ID = chains[0].ID + return true + }).Return(true).AnyTimes() mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes() mockStub.EXPECT().Logger().Return(logger).AnyTimes() + mockStub.EXPECT().CrossInvoke(constant.GovernanceContractAddr.String(), "SubmitProposal", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)) + mockStub.EXPECT().Has(AppchainKey(caller)).Return(false).Times(1) + mockStub.EXPECT().Has(AppchainKey(caller)).Return(true).AnyTimes() + res := am.Register(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType, + chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey) + assert.True(t, res.Ok) - // test for audit - approveRes := &boltvm.Response{ - Ok: true, - Result: []byte("true"), + // test for repeated register + am.Register(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType, + chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey) + assert.True(t, res.Ok) +} + +func TestAppchainManager_Manager(t *testing.T) { + mockCtl := gomock.NewController(t) + mockStub := mock_stub.NewMockStub(mockCtl) + am := &AppchainManager{ + Stub: mockStub, } - mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", gomock.Any()).Return(approveRes) - mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(true).AnyTimes() - res := am.Audit(caller, appchainMgr.APPROVED, "approve test chain") - assert.Equal(t, true, res.Ok) + chain := &appchainMgr.Appchain{ + Status: appchainMgr.AppchainUpdating, + ID: "addr", + Name: "appchain A", + Validators: "", + ConsensusType: int32(1), + ChainType: "fabric", + Desc: "", + Version: "", + PublicKey: "11111", + } + data, err := json.Marshal(chain) + assert.Nil(t, err) + + chain1 := &appchainMgr.Appchain{ + Status: appchainMgr.AppchainUpdating, + ID: "addr1", + Name: "appchain A", + Validators: "", + ConsensusType: int32(1), + ChainType: "fabric", + Desc: "", + Version: "", + PublicKey: "11111", + } + data1, err := json.Marshal(chain1) + assert.Nil(t, err) + + mockStub.EXPECT().Get(AppchainKey("addr")).Return(true, data).AnyTimes() + mockStub.EXPECT().Get(AppchainKey("addr1")).Return(false, nil).AnyTimes() + mockStub.EXPECT().Has(AppchainKey("addr")).Return(true).AnyTimes() + mockStub.EXPECT().Has(AppchainKey("addr1")).Return(false).AnyTimes() + mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes() + mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do( + func(key string, ret interface{}) bool { + chain := ret.(*appchainMgr.Appchain) + chain.Status = appchainMgr.AppchainAvailable + assert.Equal(t, key, AppchainKey("addr")) + return true + }) + + res := am.Manager(appchainMgr.EventUpdate, string(APPOVED), data1) + assert.False(t, res.Ok) + res = am.Manager(appchainMgr.EventUpdate, string(REJECTED), data1) + assert.False(t, res.Ok) + res = am.Manager(appchainMgr.EventUpdate, string(APPOVED), data) + assert.True(t, res.Ok) + res = am.Manager(appchainMgr.EventUpdate, string(REJECTED), data) + assert.True(t, res.Ok) + + mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register", gomock.Any()).Return(boltvm.Error("")).Times(1) + mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "Register", gomock.Any()).Return(boltvm.Success(nil)).AnyTimes() + res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data) + assert.False(t, res.Ok) + res = am.Manager(appchainMgr.EventRegister, string(APPOVED), data) + assert.True(t, res.Ok) } func TestUpdateChain(t *testing.T) { @@ -165,26 +236,20 @@ func TestUpdateChain(t *testing.T) { logger := log.NewWithModule("contracts") // test for DeleteAppchain mockStub.EXPECT().Caller().Return(caller).AnyTimes() - mockStub.EXPECT().Has(AppchainKey(caller)).Return(true) + mockStub.EXPECT().Has(AppchainKey(caller)).Return(true).AnyTimes() mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).Return().AnyTimes() mockStub.EXPECT().Logger().Return(logger).AnyTimes() - // test UpdateAppchain without register - mockStub.EXPECT().GetObject(AppchainKey(caller), gomock.Any()).Return(true) - res := am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType, - chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey) - - assert.Equal(t, false, res.Ok) + // TODO: test UpdateAppchain without register (false) // test UpdateAppchain with register - mockStub.EXPECT().Has(AppchainKey(caller)).Return(true) mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do( func(key string, ret interface{}) bool { chain := ret.(*appchainMgr.Appchain) - chain.Status = appchainMgr.APPROVED + chain.Status = appchainMgr.AppchainAvailable chain.PublicKey = chains[0].PublicKey assert.Equal(t, key, AppchainKey(caller)) return true }) - res = am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType, + res := am.UpdateAppchain(chains[0].Validators, chains[0].ConsensusType, chains[0].ChainType, chains[0].Name, chains[0].Desc, chains[0].Version, chains[0].PublicKey) assert.Equal(t, true, res.Ok) } @@ -199,7 +264,7 @@ func TestCountApprovedAppchains(t *testing.T) { mockStub.EXPECT().Logger().Return(logger).AnyTimes() // test for CountApprovedAppchains mockStub.EXPECT().Query(appchainMgr.PREFIX).Return(true, chainsData) - res := am.CountApprovedAppchains() + res := am.CountAvailableAppchains() assert.Equal(t, true, res.Ok) assert.Equal(t, "2", string(res.Result)) } @@ -219,7 +284,6 @@ func TestDeleteAppchain(t *testing.T) { mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "IsAdmin", gomock.Any()).Return(approveRes) mockStub.EXPECT().CrossInvoke(constant.InterchainContractAddr.String(), "DeleteInterchain", gomock.Any()).Return(approveRes) - mockStub.EXPECT().GetObject(AppchainKey(caller), gomock.Any()).Return(true) mockStub.EXPECT().Delete(AppchainKey(caller)).Return() res := am.DeleteAppchain(caller) @@ -233,7 +297,7 @@ func TestGetPubKeyByChainID(t *testing.T) { mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Do( func(key string, ret interface{}) bool { chain := ret.(*appchainMgr.Appchain) - chain.Status = appchainMgr.APPROVED + chain.Status = appchainMgr.AppchainAvailable chain.PublicKey = chains[0].PublicKey assert.Equal(t, key, AppchainKey(caller)) fmt.Printf("chain is %v", chain) @@ -247,6 +311,9 @@ func TestGetPubKeyByChainID(t *testing.T) { func prepare(t *testing.T) (*AppchainManager, *mock_stub.MockStub, []*appchainMgr.Appchain, [][]byte) { mockCtl := gomock.NewController(t) mockStub := mock_stub.NewMockStub(mockCtl) + am := &AppchainManager{ + Stub: mockStub, + } var chains []*appchainMgr.Appchain var chainsData [][]byte @@ -254,7 +321,7 @@ func prepare(t *testing.T) (*AppchainManager, *mock_stub.MockStub, []*appchainMg addr := types.NewAddress([]byte{byte(i)}).String() chain := &appchainMgr.Appchain{ - Status: appchainMgr.APPROVED, + Status: appchainMgr.AppchainAvailable, ID: addr, Name: "appchain" + addr, Validators: "", @@ -272,9 +339,6 @@ func prepare(t *testing.T) (*AppchainManager, *mock_stub.MockStub, []*appchainMg chains = append(chains, chain) } - am := &AppchainManager{ - Stub: mockStub, - } return am, mockStub, chains, chainsData } @@ -283,7 +347,7 @@ func TestInterchainManager_Register(t *testing.T) { mockStub := mock_stub.NewMockStub(mockCtl) addr := types.NewAddress([]byte{0}).String() - mockStub.EXPECT().Caller().Return(addr).AnyTimes() + //mockStub.EXPECT().Caller().Return(addr).AnyTimes() mockStub.EXPECT().Set(gomock.Any(), gomock.Any()).AnyTimes() o1 := mockStub.EXPECT().Get(appchainMgr.PREFIX+addr).Return(false, nil) @@ -313,7 +377,7 @@ func TestInterchainManager_Register(t *testing.T) { im := &InterchainManager{mockStub} - res := im.Register() + res := im.Register(addr) assert.Equal(t, true, res.Ok) ic := &pb.Interchain{} @@ -324,11 +388,11 @@ func TestInterchainManager_Register(t *testing.T) { assert.Equal(t, 0, len(ic.ReceiptCounter)) assert.Equal(t, 0, len(ic.SourceReceiptCounter)) - res = im.Register() + res = im.Register(addr) assert.Equal(t, true, res.Ok) assert.Equal(t, data0, res.Result) - res = im.Register() + res = im.Register(addr) assert.Equal(t, true, res.Ok) assert.Equal(t, data1, res.Result) } @@ -585,19 +649,17 @@ func TestInterchainManager_HandleIBTPs(t *testing.T) { mockStub.EXPECT().Set(gomock.Any(), gomock.Any()).AnyTimes() mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes() - f1 := mockStub.EXPECT().Get(appchainMgr.PREFIX+caller).Return(false, nil) data0, err := interchain.Marshal() assert.Nil(t, err) - f2 := mockStub.EXPECT().Get(appchainMgr.PREFIX+caller).Return(true, data0).AnyTimes() + mockStub.EXPECT().Get(appchainMgr.PREFIX+caller).Return(true, data0).AnyTimes() mockStub.EXPECT().Get(appchainMgr.PREFIX+to).Return(true, data0).AnyTimes() mockStub.EXPECT().CrossInvoke(gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes() mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes() mockStub.EXPECT().GetTxIndex().Return(uint64(1)).AnyTimes() mockStub.EXPECT().PostInterchainEvent(gomock.Any()).AnyTimes() mockStub.EXPECT().GetTxHash().Return(&types.Hash{}).AnyTimes() - gomock.InOrder(f1, f2) im := &InterchainManager{mockStub} @@ -649,7 +711,7 @@ func TestInterchainManager_HandleUnionIBTP(t *testing.T) { assert.Nil(t, err) relayChain := &appchainMgr.Appchain{ - Status: appchainMgr.APPROVED, + Status: appchainMgr.AppchainAvailable, ID: from, Name: "appchain" + from, Validators: "", @@ -802,10 +864,15 @@ func TestRole_GetRole(t *testing.T) { mockCtl := gomock.NewController(t) mockStub := mock_stub.NewMockStub(mockCtl) - addrs := []string{types.NewAddress([]byte{0}).String(), types.NewAddress([]byte{1}).String()} + admins := []*repo.Admin{ + &repo.Admin{ + Address: "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", + Weight: 1, + }, + } - mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, addrs).AnyTimes() - mockStub.EXPECT().Caller().Return(types.NewAddress([]byte{0}).String()) + mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, admins).AnyTimes() + mockStub.EXPECT().Caller().Return(admins[0].Address) im := &Role{mockStub} @@ -831,13 +898,18 @@ func TestRole_IsAdmin(t *testing.T) { mockCtl := gomock.NewController(t) mockStub := mock_stub.NewMockStub(mockCtl) - addrs := []string{types.NewAddress([]byte{0}).String(), types.NewAddress([]byte{1}).String()} + admins := []*repo.Admin{ + &repo.Admin{ + Address: "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", + Weight: 1, + }, + } - mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, addrs).AnyTimes() + mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, admins).AnyTimes() im := &Role{mockStub} - res := im.IsAdmin(addrs[0]) + res := im.IsAdmin(admins[0].Address) assert.True(t, res.Ok) assert.Equal(t, "true", string(res.Result)) @@ -850,21 +922,30 @@ func TestRole_GetAdminRoles(t *testing.T) { mockCtl := gomock.NewController(t) mockStub := mock_stub.NewMockStub(mockCtl) - addrs := []string{types.NewAddress([]byte{0}).String(), types.NewAddress([]byte{1}).String()} + admins := []*repo.Admin{ + &repo.Admin{ + Address: "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", + Weight: 1, + }, + &repo.Admin{ + Address: "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", + Weight: 1, + }, + } - mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, addrs).AnyTimes() + mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, admins).AnyTimes() im := &Role{mockStub} res := im.GetAdminRoles() assert.True(t, res.Ok) - var admins []string - err := json.Unmarshal(res.Result, &admins) + var as []*repo.Admin + err := json.Unmarshal(res.Result, &as) assert.Nil(t, err) - assert.Equal(t, len(addrs), len(admins)) - for i, addr := range addrs { - assert.Equal(t, addr, admins[i]) + assert.Equal(t, len(admins), len(as)) + for i, admin := range admins { + assert.Equal(t, admin.Address, as[i].Address) } } @@ -873,7 +954,7 @@ func TestRole_SetAdminRoles(t *testing.T) { mockStub := mock_stub.NewMockStub(mockCtl) addrs := []string{types.NewAddress([]byte{0}).String(), types.NewAddress([]byte{1}).String()} - mockStub.EXPECT().SetObject(adminRolesKey, addrs).AnyTimes() + mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes() im := &Role{mockStub} @@ -884,6 +965,31 @@ func TestRole_SetAdminRoles(t *testing.T) { assert.True(t, res.Ok) } +func TestRole_GetRoleWeight(t *testing.T) { + mockCtl := gomock.NewController(t) + mockStub := mock_stub.NewMockStub(mockCtl) + + admins := []*repo.Admin{ + &repo.Admin{ + Address: "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", + Weight: 1, + }, + } + + mockStub.EXPECT().GetObject(adminRolesKey, gomock.Any()).SetArg(1, admins).AnyTimes() + + im := &Role{mockStub} + + res := im.GetRoleWeight(admins[0].Address) + assert.True(t, res.Ok) + w, err := strconv.Atoi(string(res.Result)) + assert.Nil(t, err) + assert.Equal(t, admins[0].Weight, uint64(w)) + + res = im.GetRoleWeight("") + assert.False(t, res.Ok) +} + func TestRuleManager_RegisterRule(t *testing.T) { mockCtl := gomock.NewController(t) mockStub := mock_stub.NewMockStub(mockCtl) @@ -1420,3 +1526,467 @@ func TestInterRelayBroker_InvokeInterRelayContract(t *testing.T) { res = interRelayBroker.GetOutMessage("123", 1) require.True(t, res.Ok) } + +func TestGovernance_SubmitProposal(t *testing.T) { + mockCtl := gomock.NewController(t) + mockStub := mock_stub.NewMockStub(mockCtl) + + g := Governance{mockStub} + + idExistent := "idExistent-1" + addrApproved := "addrApproved" + addrAganisted := "addrAganisted" + approveBallot := Ballot{ + VoterAddr: addrApproved, + Approve: BallotApprove, + Num: 1, + Reason: "", + } + againstBallot := Ballot{ + VoterAddr: addrAganisted, + Approve: BallotReject, + Num: 1, + Reason: "", + } + proposalExistent := &Proposal{ + Id: idExistent, + Des: "des", + Typ: AppchainMgr, + Status: PROPOSED, + BallotMap: map[string]Ballot{addrApproved: approveBallot, addrAganisted: againstBallot}, + ApproveNum: 1, + AgainstNum: 1, + } + pData, err := json.Marshal(proposalExistent) + assert.Nil(t, err) + pDatas := make([][]byte, 0) + pDatas = append(pDatas, pData) + + admins := []*repo.Admin{ + &repo.Admin{ + Address: "addr1", + Weight: 1, + }, + &repo.Admin{ + Address: "addr2", + Weight: 1, + }, + &repo.Admin{ + Address: "addr3", + Weight: 1, + }, + &repo.Admin{ + Address: "addr4", + Weight: 1, + }, + } + adminsData, err := json.Marshal(admins) + assert.Nil(t, err) + adminsErrorData := make([]byte, 0) + + mockStub.EXPECT().Query(gomock.Any()).Return(true, pDatas).AnyTimes() + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Error("")).Times(1) + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Success(adminsErrorData)).Times(2) + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Success(adminsData)).AnyTimes() + mockStub.EXPECT().GetObject(gomock.Any(), gomock.Any()).Return(false).AnyTimes() + mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes() + + res := g.SubmitProposal("", "des", string(AppchainMgr), []byte{}) + assert.False(t, res.Ok, string(res.Result)) + // GetAdminRoles error + res = g.SubmitProposal(idExistent, "des", string(AppchainMgr), []byte{}) + assert.False(t, res.Ok, string(res.Result)) + // GetAdminRoles unmarshal error + res = g.SubmitProposal(idExistent, "des", string(AppchainMgr), []byte{}) + assert.False(t, res.Ok, string(res.Result)) + res = g.SubmitProposal(idExistent, "des", "", []byte{}) + assert.False(t, res.Ok, string(res.Result)) + res = g.SubmitProposal(idExistent, "des", string(AppchainMgr), []byte{}) + assert.True(t, res.Ok, string(res.Result)) + +} +func TestGovernance_Proposal(t *testing.T) { + mockCtl := gomock.NewController(t) + mockStub := mock_stub.NewMockStub(mockCtl) + + g := Governance{mockStub} + + idExistent := "idExistent-1" + idNonexistent := "idNonexistent-2" + idClosed := "idClosed-3" + idNotReachThreshold := "idNotReachThreshold-4" + idSuperMajorityApprove := "idSuperMajorityApprove-5" + idSuperMajorityAgainst := "idSuperMajorityAgainst-6" + idUnupportedType := "idUnsupportedType-7" + addrApproved := "addrApproved" + addrAganisted := "addrAganisted" + addrNotVoted := "addrNotVoted" + addrNotVoted1 := "addrNotVoted1" + addrNotVoted2 := "addrNotVoted2" + addrNotVoted3 := "addrNotVoted3" + addrNotVoted4 := "addrNotVoted4" + addrNotVoted5 := "addrNotVoted5" + addrNotVoted6 := "addrNotVoted6" + + approveBallot := Ballot{ + VoterAddr: addrApproved, + Approve: BallotApprove, + Num: 1, + Reason: "", + } + againstBallot := Ballot{ + VoterAddr: addrAganisted, + Approve: BallotReject, + Num: 1, + Reason: "", + } + proposalExistent := Proposal{ + Id: idExistent, + Des: "des", + Typ: AppchainMgr, + Status: PROPOSED, + BallotMap: map[string]Ballot{addrApproved: approveBallot, addrAganisted: againstBallot}, + ApproveNum: 1, + AgainstNum: 1, + ElectorateNum: 4, + ThresholdNum: 3, + } + + pData, err := json.Marshal(proposalExistent) + assert.Nil(t, err) + pDatas := make([][]byte, 0) + pDatas = append(pDatas, pData) + + admins := []*repo.Admin{ + &repo.Admin{ + Address: "addr1", + Weight: 1, + }, + &repo.Admin{ + Address: "addr2", + Weight: 1, + }, + &repo.Admin{ + Address: "addr3", + Weight: 1, + }, + &repo.Admin{ + Address: "addr4", + Weight: 1, + }, + } + adminsData, err := json.Marshal(admins) + assert.Nil(t, err) + + mockStub.EXPECT().Has(ProposalKey(idExistent)).Return(true).AnyTimes() + mockStub.EXPECT().Has(ProposalKey(idNonexistent)).Return(false).AnyTimes() + mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes() + mockStub.EXPECT().AddObject(gomock.Any(), gomock.Any()).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idExistent), gomock.Any()).SetArg(1, proposalExistent).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idClosed), gomock.Any()).Do( + func(id string, ret interface{}) bool { + pro := ret.(*Proposal) + pro.Id = idClosed + pro.Des = proposalExistent.Des + pro.Typ = proposalExistent.Typ + pro.Status = APPOVED + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idNonexistent), gomock.Any()).Return(false).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idNotReachThreshold), gomock.Any()).Do( + func(key string, ret interface{}) bool { + pro := ret.(*Proposal) + pro.Id = idNotReachThreshold + pro.Des = proposalExistent.Des + pro.Typ = RuleMgr + pro.Status = proposalExistent.Status + pro.BallotMap = proposalExistent.BallotMap + pro.ApproveNum = proposalExistent.ApproveNum + pro.AgainstNum = proposalExistent.AgainstNum + pro.ElectorateNum = 4 + pro.ThresholdNum = 4 + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idSuperMajorityApprove), gomock.Any()).Do( + func(id string, ret interface{}) bool { + pro := ret.(*Proposal) + pro.Id = idSuperMajorityApprove + pro.Des = proposalExistent.Des + pro.Typ = NodeMgr + pro.Status = proposalExistent.Status + pro.BallotMap = proposalExistent.BallotMap + pro.ApproveNum = proposalExistent.ApproveNum + pro.AgainstNum = proposalExistent.AgainstNum + pro.ElectorateNum = proposalExistent.ElectorateNum + pro.ThresholdNum = proposalExistent.ThresholdNum + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idSuperMajorityAgainst), gomock.Any()).Do( + func(id string, ret interface{}) bool { + pro := ret.(*Proposal) + pro.Id = idSuperMajorityAgainst + pro.Des = proposalExistent.Des + pro.Typ = NodeMgr + pro.Status = proposalExistent.Status + pro.BallotMap = proposalExistent.BallotMap + pro.ApproveNum = proposalExistent.ApproveNum + pro.AgainstNum = proposalExistent.AgainstNum + pro.ElectorateNum = proposalExistent.ElectorateNum + pro.ThresholdNum = proposalExistent.ThresholdNum + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(ProposalKey(idUnupportedType), gomock.Any()).Do( + func(id string, ret interface{}) bool { + pro := ret.(*Proposal) + pro.Id = idUnupportedType + pro.Des = proposalExistent.Des + pro.Typ = ServiceMgr + pro.Status = proposalExistent.Status + pro.BallotMap = proposalExistent.BallotMap + pro.ApproveNum = proposalExistent.ApproveNum + pro.AgainstNum = proposalExistent.AgainstNum + pro.ElectorateNum = proposalExistent.ElectorateNum + pro.ThresholdNum = proposalExistent.ThresholdNum + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(string(AppchainMgr), gomock.Any()).Return(false).AnyTimes() + mockStub.EXPECT().GetObject(string(RuleMgr), gomock.Any()).Do( + func(key string, ret interface{}) bool { + proStrategy := ret.(*ProposalStrategy) + proStrategy.Typ = SimpleMajority + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(string(NodeMgr), gomock.Any()).Do( + func(key string, ret interface{}) bool { + proStrategy := ret.(*ProposalStrategy) + proStrategy.Typ = SuperMajorityApprove + return true + }).Return(true).Times(1) + mockStub.EXPECT().GetObject(string(NodeMgr), gomock.Any()).Do( + func(key string, ret interface{}) bool { + proStrategy := ret.(*ProposalStrategy) + proStrategy.Typ = SuperMajorityAgainst + return true + }).Return(true).AnyTimes() + mockStub.EXPECT().GetObject(string(ServiceMgr), gomock.Any()).Return(false).AnyTimes() + mockStub.EXPECT().Query(gomock.Any()).Return(true, pDatas).AnyTimes() + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetRoleWeight", gomock.Any()).Return(boltvm.Error("get role weight")).Times(1) + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetRoleWeight", gomock.Any()).Return(boltvm.Success([]byte(""))).Times(1) + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetRoleWeight", gomock.Any()).Return(boltvm.Success([]byte(strconv.Itoa(1)))).AnyTimes() + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Error("")).Times(1) + mockStub.EXPECT().CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles").Return(boltvm.Success(adminsData)).AnyTimes() + mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "Manager", gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Error("")).Times(1) + mockStub.EXPECT().CrossInvoke(constant.AppchainMgrContractAddr.String(), "Manager", gomock.Any(), gomock.Any(), gomock.Any()).Return(boltvm.Success(nil)).AnyTimes() + + res := g.ModifyProposal(idExistent, "des", string(AppchainMgr), []byte{}) + assert.False(t, res.Ok, string(res.Result)) + res = g.ModifyProposal(idExistent, "des", string(AppchainMgr), []byte{}) + assert.True(t, res.Ok, string(res.Result)) + res = g.ModifyProposal(idNonexistent, "des", string(AppchainMgr), []byte{}) + assert.False(t, res.Ok, string(res.Result)) + res = g.ModifyProposal("", "des", string(AppchainMgr), []byte{}) + assert.False(t, res.Ok, string(res.Result)) + res = g.ModifyProposal(idExistent, "des", "", []byte{}) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetProposal(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetProposal(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetProposalsByFrom("idExistent") + assert.True(t, res.Ok, string(res.Result)) + + res = g.GetProposalsByTyp("") + assert.False(t, res.Ok, string(res.Result)) + res = g.GetProposalsByTyp(string(AppchainMgr)) + assert.True(t, res.Ok, string(res.Result)) + + res = g.GetProposalsByStatus("") + assert.False(t, res.Ok, string(res.Result)) + res = g.GetProposalsByStatus(string((PROPOSED))) + assert.True(t, res.Ok, string(res.Result)) + + res = g.GetDes(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetDes(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetTyp(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetTyp(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetStatus(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetStatus(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetApprove(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetApprove(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetAgainst(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetAgainst(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetVotedNum(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetVotedNum(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetVoted(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetVoted(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetApproveNum(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetApproveNum(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetAgainstNum(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetAgainstNum(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetElectorateNum(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetElectorateNum(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetThresholdNum(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetThresholdNum(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + + var v = &Ballot{} + res = g.GetBallot(addrApproved, idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + res = g.GetBallot(addrNotVoted, idExistent) + assert.False(t, res.Ok, string(res.Result)) + res = g.GetBallot(addrApproved, idExistent) + assert.True(t, res.Ok, string(res.Result)) + err = json.Unmarshal(res.Result, v) + assert.Nil(t, err) + assert.Equal(t, BallotApprove, v.Approve) + assert.Equal(t, uint64(1), v.Num) + res = g.GetBallot(addrAganisted, idExistent) + assert.True(t, res.Ok, string(res.Result)) + err = json.Unmarshal(res.Result, v) + assert.Nil(t, err) + assert.Equal(t, BallotReject, v.Approve) + assert.Equal(t, uint64(1), v.Num) + + res = g.GetUnvote(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + res = g.GetUnvote(idExistent) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetUnvoteNum(idNonexistent) + assert.False(t, res.Ok, string(res.Result)) + res = g.GetUnvoteNum(idExistent) + assert.True(t, res.Ok, string(res.Result)) + mockStub.EXPECT().Caller().Return(addrApproved).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted).Times(1) + mockStub.EXPECT().Caller().Return(addrApproved).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted1).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted2).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted3).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted4).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted5).Times(1) + mockStub.EXPECT().Caller().Return(addrNotVoted6).Times(1) + + // nonexistent error + res = g.Vote(idNonexistent, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + // closed error + res = g.Vote(idClosed, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + // has voted error + res = g.Vote(idExistent, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + + // get weight error + res = g.Vote(idExistent, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + // get weight parse int error + res = g.Vote(idExistent, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + + // not reach threshold (approve:1) + res = g.Vote(idNotReachThreshold, BallotApprove, "") + assert.True(t, res.Ok, string(res.Result)) + // SuperMajorityApprove (reject:1) + res = g.Vote(idSuperMajorityApprove, BallotReject, "") + assert.False(t, res.Ok, string(res.Result)) + // SuperMajorityAgainst (approve:2) + res = g.Vote(idSuperMajorityAgainst, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + // UnupportedType (reject:2) + res = g.Vote(idUnupportedType, BallotReject, "") + assert.False(t, res.Ok, string(res.Result)) + // Manager error (approve:3) + res = g.Vote(idExistent, BallotApprove, "") + assert.False(t, res.Ok, string(res.Result)) + // reject (reject:3) + res = g.Vote(idExistent, BallotReject, "") + assert.True(t, res.Ok, string(res.Result)) + // approve (approve:4) + res = g.Vote(idExistent, BallotReject, "") + assert.True(t, res.Ok, string(res.Result)) +} + +func TestGovernance_ProposalStrategy(t *testing.T) { + mockCtl := gomock.NewController(t) + mockStub := mock_stub.NewMockStub(mockCtl) + + g := Governance{mockStub} + ps := &ProposalStrategy{ + Typ: SimpleMajority, + ParticipateThreshold: 0.5, + } + psData, err := json.Marshal(ps) + assert.Nil(t, err) + + psError := &ProposalStrategy{ + Typ: SimpleMajority, + ParticipateThreshold: 1.5, + } + psErrorData, err := json.Marshal(psError) + assert.Nil(t, err) + + mockStub.EXPECT().SetObject(gomock.Any(), gomock.Any()).AnyTimes() + mockStub.EXPECT().GetObject(string(RuleMgr), gomock.Any()).Return(false).AnyTimes() + mockStub.EXPECT().GetObject(string(AppchainMgr), gomock.Any()).Return(true).AnyTimes() + + res := g.NewProposalStrategy(string(SimpleMajority), 0.5, []byte{}) + assert.True(t, res.Ok, string(res.Result)) + res = g.NewProposalStrategy("", 0.5, []byte{}) + assert.False(t, res.Ok, string(res.Result)) + + res = g.SetProposalStrategy(string(AppchainMgr), psData) + assert.True(t, res.Ok, string(res.Result)) + res = g.SetProposalStrategy("", psData) + assert.False(t, res.Ok, string(res.Result)) + res = g.SetProposalStrategy(string(AppchainMgr), psErrorData) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetProposalStrategy(string(AppchainMgr)) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetProposalStrategy("") + assert.False(t, res.Ok, string(res.Result)) + res = g.GetProposalStrategy(string(RuleMgr)) + assert.False(t, res.Ok, string(res.Result)) + + res = g.GetProposalStrategyType(string(AppchainMgr)) + assert.True(t, res.Ok, string(res.Result)) + res = g.GetProposalStrategyType("") + assert.False(t, res.Ok, string(res.Result)) + res = g.GetProposalStrategyType(string(RuleMgr)) + assert.False(t, res.Ok, string(res.Result)) +} diff --git a/internal/executor/contracts/governance.go b/internal/executor/contracts/governance.go new file mode 100644 index 0000000..7388e9d --- /dev/null +++ b/internal/executor/contracts/governance.go @@ -0,0 +1,729 @@ +package contracts + +import ( + "encoding/json" + "fmt" + "math" + "strconv" + "strings" + + "github.com/meshplus/bitxhub-core/boltvm" + "github.com/meshplus/bitxhub-model/constant" + "github.com/meshplus/bitxhub-model/pb" + "github.com/meshplus/bitxhub/internal/repo" +) + +type Governance struct { + boltvm.Stub +} + +type ProposalType string +type ProposalStatus string + +const ( + PROPOSAL_PREFIX = "proposal-" + + AppchainMgr ProposalType = "AppchainMgr" + RuleMgr ProposalType = "RuleMgr" + NodeMgr ProposalType = "NodeMgr" + ServiceMgr ProposalType = "ServiceMgr" + + PROPOSED ProposalStatus = "proposed" + APPOVED ProposalStatus = "approve" + REJECTED ProposalStatus = "reject" + + BallotApprove = "approve" + BallotReject = "reject" +) + +type Ballot struct { + VoterAddr string `json:"voter_addr"` + Approve string `json:"approve"` + Num uint64 `json:"num"` + Reason string `json:"reason"` +} + +func (g *Governance) GetBallot(voterAddr, proposalId string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(proposalId), p) { + return boltvm.Error("proposal does not exist") + } + + ballot, ok := p.BallotMap[voterAddr] + if !ok { + return boltvm.Error("administrator of the address has not voted") + } + + bData, err := json.Marshal(ballot) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(bData) +} + +type Proposal struct { + Id string `json:"id"` + Des string `json:"des"` + Typ ProposalType `json:"typ"` + Status ProposalStatus `json:"status"` + // ballot information: voter address -> ballot + BallotMap map[string]Ballot `json:"ballot_map"` + ApproveNum uint64 `json:"approve_num"` + AgainstNum uint64 `json:"against_num"` + ElectorateNum uint64 `json:"electorate_num"` + ThresholdNum uint64 `json:"threshold_num"` + Extra []byte `json:"extra"` +} + +func (g *Governance) SubmitProposal(from, des string, typ string, extra []byte) *boltvm.Response { + ret, err := g.getProposalsByFrom(from) + if err != nil { + return boltvm.Error(err.Error()) + } + + en, err := g.getElectorateNum() + if err != nil { + return boltvm.Error(err.Error()) + } + + tn, err := g.getThresholdNum(en, ProposalType(typ)) + if err != nil { + return boltvm.Error(err.Error()) + } + + p := &Proposal{ + Id: from + "-" + strconv.Itoa(len(ret)), + Des: des, + Typ: ProposalType(typ), + Status: PROPOSED, + BallotMap: make(map[string]Ballot, 0), + ApproveNum: 0, + AgainstNum: 0, + ElectorateNum: en, + ThresholdNum: tn, + Extra: extra, + } + if err := checkProposalInfo(p); err != nil { + return boltvm.Error(err.Error()) + } + + g.AddObject(ProposalKey(p.Id), *p) + + return boltvm.Success([]byte(p.Id)) +} + +func (g *Governance) getElectorateNum() (uint64, error) { + res := g.CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles") + if !res.Ok { + return 0, fmt.Errorf(string(res.Result)) + } + + var admins []*repo.Admin + if err := json.Unmarshal(res.Result, &admins); err != nil { + return 0, fmt.Errorf(err.Error()) + } + + electorateNum := uint64(0) + for _, admin := range admins { + electorateNum = electorateNum + admin.Weight + } + return electorateNum, nil +} + +func (g *Governance) getThresholdNum(electorateNum uint64, proposalTyp ProposalType) (uint64, error) { + if err := checkProposalType(proposalTyp); err != nil { + return 0, fmt.Errorf(err.Error()) + } + ps := ProposalStrategy{} + if !g.GetObject(string(proposalTyp), &ps) { + // SimpleMajority is used by default + ps.Typ = SimpleMajority + ps.ParticipateThreshold = 0.75 + g.AddObject(string(proposalTyp), ps) + } + + return uint64(math.Ceil(float64(electorateNum) * ps.ParticipateThreshold)), nil +} + +// ModifyProposal modify a proposal +func (g *Governance) ModifyProposal(id, des string, typ string, extra []byte) *boltvm.Response { + en, err := g.getElectorateNum() + if err != nil { + return boltvm.Error(err.Error()) + } + + tn, err := g.getThresholdNum(en, ProposalType(typ)) + if err != nil { + return boltvm.Error(err.Error()) + } + + p := &Proposal{ + Id: id, + Des: des, + Typ: ProposalType(typ), + Status: PROPOSED, + BallotMap: make(map[string]Ballot, 0), + ApproveNum: 0, + AgainstNum: 0, + ElectorateNum: en, + ThresholdNum: tn, + Extra: extra, + } + + if err := checkProposalInfo(p); err != nil { + return boltvm.Error(err.Error()) + } + + if !g.Has(ProposalKey(p.Id)) { + return boltvm.Error(fmt.Sprintf("proposal does not exists")) + } + + g.SetObject(ProposalKey(p.Id), *p) + return boltvm.Success(nil) +} + +// GetProposal query proposal by id +func (g *Governance) GetProposal(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + pData, err := json.Marshal(p) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(pData) +} + +// Query proposals by proposal type, returning a list of proposal for that type +func (g *Governance) GetProposalsByFrom(from string) *boltvm.Response { + ret, err := g.getProposalsByFrom(from) + if err != nil { + return boltvm.Error(err.Error()) + } + + retData, err := json.Marshal(ret) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(retData) +} + +func (g *Governance) getProposalsByFrom(from string) ([]Proposal, error) { + ok, datas := g.Query(PROPOSAL_PREFIX) + if !ok { + return make([]Proposal, 0), nil + } + + ret := make([]Proposal, 0) + for _, d := range datas { + p := Proposal{} + if err := json.Unmarshal(d, &p); err != nil { + return nil, err + } + + if from == p.Id[0:strings.Index(p.Id, "-")] { + ret = append(ret, p) + } + } + + return ret, nil +} + +// Query proposals by proposal type, returning a list of proposal for that type +func (g *Governance) GetProposalsByTyp(typ string) *boltvm.Response { + if err := checkProposalType(ProposalType(typ)); err != nil { + return boltvm.Error(err.Error()) + } + + ret := make([]Proposal, 0) + + ok, datas := g.Query(PROPOSAL_PREFIX) + if ok { + for _, d := range datas { + p := Proposal{} + if err := json.Unmarshal(d, &p); err != nil { + return boltvm.Error(err.Error()) + } + + if ProposalType(typ) == p.Typ { + ret = append(ret, p) + } + } + } + + retData, err := json.Marshal(ret) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(retData) +} + +// Query proposals based on proposal status, returning a list of proposal for that status +func (g *Governance) GetProposalsByStatus(status string) *boltvm.Response { + if err := checkProposalStauts(ProposalStatus(status)); err != nil { + return boltvm.Error(err.Error()) + } + + ret := make([]Proposal, 0) + + ok, datas := g.Query(PROPOSAL_PREFIX) + if ok { + for _, d := range datas { + p := Proposal{} + if err := json.Unmarshal(d, &p); err != nil { + return boltvm.Error(err.Error()) + } + + if ProposalStatus(status) == p.Status { + ret = append(ret, p) + } + } + } + + retData, err := json.Marshal(ret) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(retData) +} + +// Get proposal description information +func (g *Governance) GetDes(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(p.Des)) +} + +// Get Proposal Type +func (g *Governance) GetTyp(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(p.Typ)) +} + +// Get proposal status +func (g *Governance) GetStatus(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(p.Status)) +} + +// Get affirmative vote information +func (g *Governance) GetApprove(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + approveMap := map[string]Ballot{} + for k, v := range p.BallotMap { + if v.Approve == BallotApprove { + approveMap[k] = v + } + } + + retData, err := json.Marshal(approveMap) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(retData) +} + +// Get negative vote information +func (g *Governance) GetAgainst(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + againstMap := map[string]Ballot{} + for k, v := range p.BallotMap { + if v.Approve == BallotReject { + againstMap[k] = v + } + } + + retData, err := json.Marshal(againstMap) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(retData) +} + +// Get the total number of affirmative votes +func (g *Governance) GetApproveNum(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(strconv.Itoa(int(p.ApproveNum)))) +} + +// Get the total number of negative votes +func (g *Governance) GetAgainstNum(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(strconv.Itoa(int(p.AgainstNum)))) +} + +// Get the number of total votes, include all votes cast and all votes not cast +func (g *Governance) GetElectorateNum(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(strconv.Itoa(int(p.ElectorateNum)))) +} + +// Get the minimum number of votes required for the current voting strategy +func (g *Governance) GetThresholdNum(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(strconv.Itoa(int(p.ThresholdNum)))) +} + +// Get the number of people who have voted +func (g *Governance) GetVotedNum(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + return boltvm.Success([]byte(strconv.Itoa(len(p.BallotMap)))) +} + +// Get voted information +func (g *Governance) GetVoted(id string) *boltvm.Response { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + retData, err := json.Marshal(p.BallotMap) + if err != nil { + return boltvm.Error(err.Error()) + } + + return boltvm.Success(retData) +} + +// Get Unvoted information +func (g *Governance) GetUnvote(id string) *boltvm.Response { + ret, err := g.getUnvote(id) + if err != nil { + return boltvm.Error(err.Error()) + } + + retData, err := json.Marshal(ret) + if err != nil { + return boltvm.Error(err.Error()) + } + + return boltvm.Success(retData) +} + +func (g *Governance) getUnvote(id string) ([]*repo.Admin, error) { + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return nil, fmt.Errorf("proposal does not exist") + } + + res := g.CrossInvoke(constant.RoleContractAddr.String(), "GetAdminRoles") + if !res.Ok { + return nil, fmt.Errorf("get admin roles error: " + string(res.Result)) + } + var admins []*repo.Admin + if err := json.Unmarshal(res.Result, &admins); err != nil { + return nil, fmt.Errorf("get admin roles error: " + err.Error()) + } + + ret := make([]*repo.Admin, 0) + for _, admin := range admins { + if _, ok := p.BallotMap[admin.Address]; !ok { + ret = append(ret, admin) + } + } + + return ret, nil +} + +// Get Unvoted information +func (g *Governance) GetUnvoteNum(id string) *boltvm.Response { + ret, err := g.getUnvote(id) + if err != nil { + return boltvm.Error(err.Error()) + } + + return boltvm.Success([]byte(strconv.Itoa(len(ret)))) +} + +// Add someone's voting information (each person can only vote once) +func (g *Governance) Vote(id, approve string, reason string) *boltvm.Response { + addr := g.Caller() + // 1. Determine if the proposal exists + p := &Proposal{} + if !g.GetObject(ProposalKey(id), p) { + return boltvm.Error("proposal does not exist") + } + + // 2. Set vote + if err := g.setVote(p, addr, approve, reason); err != nil { + return boltvm.Error("get vote error: " + err.Error()) + } + + // 3. Count votes + // If the threshold for participation is reached, the result of the vote can be judged. + // If the policy determines that the current vote has closed, the proposal state is modified. + ok, err := g.countVote(p) + if err != nil { + return boltvm.Error("count vote error: " + err.Error()) + } + if !ok { + // the round of the voting is not over, wait the next vote + return boltvm.Success(nil) + } + + // 4. Handle result + switch p.Typ { + case RuleMgr, NodeMgr, ServiceMgr: + return boltvm.Error("waiting for subsequent implementation") + default: // APPCHAIN_MGR + res := g.CrossInvoke(constant.AppchainMgrContractAddr.String(), "Manager", pb.String(p.Des), pb.String(string(p.Status)), pb.Bytes(p.Extra)) + if !res.Ok { + return boltvm.Error("cross invoke Manager error:" + string(res.Result)) + } + return boltvm.Success(nil) + } +} + +// Set vote of an administrator +func (g *Governance) setVote(p *Proposal, addr string, approve string, reason string) error { + // Determine if the proposal has been approved or rejected + if p.Status != PROPOSED { + return fmt.Errorf("the vote on the proposal has been closed") + } + + // Determine if the administrator has voted + if _, ok := p.BallotMap[addr]; ok { + return fmt.Errorf("administrator of the address has voted") + } + + res := g.CrossInvoke(constant.RoleContractAddr.String(), "GetRoleWeight", pb.String(addr)) + if !res.Ok { + return fmt.Errorf(string(res.Result)) + } + num, err := strconv.Atoi(string(res.Result)) + if err != nil { + return fmt.Errorf(err.Error()) + } + + // Record Voting Information + ballot := Ballot{ + VoterAddr: addr, + Approve: approve, + Num: uint64(num), + Reason: reason, + } + p.BallotMap[addr] = ballot + switch approve { + case BallotApprove: + p.ApproveNum = p.ApproveNum + uint64(num) + case BallotReject: + p.AgainstNum = p.AgainstNum + uint64(num) + } + + g.SetObject(ProposalKey(p.Id), *p) + return nil +} + +// Count votes to see if this round is over. +// If the vote is over change the status of the proposal. +func (g *Governance) countVote(p *Proposal) (bool, error) { + // Get proposal strategy + ps := ProposalStrategy{} + if !g.GetObject(string(p.Typ), &ps) { + // SimpleMajority is used by default + ps.Typ = SimpleMajority + ps.ParticipateThreshold = 0.75 + g.SetObject(string(p.Typ), ps) + } + + // Determine whether the participation threshold for the strategy has been met + if p.ApproveNum+p.AgainstNum < p.ThresholdNum { + return false, nil + } + + // Votes are counted according to strategy + switch ps.Typ { + case SuperMajorityApprove: + // TODO: SUPER_MAJORITY_APPROVE + return false, fmt.Errorf("this policy is not supported currently") + case SuperMajorityAgainst: + // TODO: SUPER_MAJORITY_AGAINST + return false, fmt.Errorf("this policy is not supported currently") + default: // SIMPLE_MAJORITY + if p.ApproveNum > p.AgainstNum { + p.Status = APPOVED + } else { + p.Status = REJECTED + } + g.SetObject(ProposalKey(p.Id), *p) + return true, nil + } +} + +// Proposal strategy =============================================================== + +type ProposalStrategyType string + +const ( + SuperMajorityApprove ProposalStrategyType = "SuperMajorityApprove" + SuperMajorityAgainst ProposalStrategyType = "SuperMajorityAgainst" + SimpleMajority ProposalStrategyType = "SimpleMajority" +) + +type ProposalStrategy struct { + Typ ProposalStrategyType `json:"typ"` + // The minimum participation threshold. + // Only when the number of voting participants reaches this proportion, + // the proposal will take effect. That is, the proposal can be judged + // according to the voting situation. + ParticipateThreshold float64 `json:"participate_threshold"` + Extra []byte `json:"extra"` +} + +func (g *Governance) NewProposalStrategy(typ string, participateThreshold float64, extra []byte) *boltvm.Response { + ps := &ProposalStrategy{ + Typ: ProposalStrategyType(typ), + ParticipateThreshold: participateThreshold, + Extra: extra, + } + if err := checkStrategyInfo(ps); err != nil { + return boltvm.Error(err.Error()) + } + + pData, err := json.Marshal(ps) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(pData) +} + +// set proposal strategy for a proposal type +func (g *Governance) SetProposalStrategy(pt string, psData []byte) *boltvm.Response { + ps := &ProposalStrategy{} + if err := json.Unmarshal(psData, ps); err != nil { + return boltvm.Error(err.Error()) + } + + if err := checkProposalType(ProposalType(pt)); err != nil { + return boltvm.Error(err.Error()) + } + + if err := checkStrategyInfo(ps); err != nil { + return boltvm.Error(err.Error()) + } + + g.SetObject(string(pt), *ps) + return boltvm.Success(nil) +} + +func (g *Governance) GetProposalStrategy(pt string) *boltvm.Response { + if err := checkProposalType(ProposalType(pt)); err != nil { + return boltvm.Error(err.Error()) + } + + ps := &ProposalStrategy{} + if !g.GetObject(string(pt), ps) { + return boltvm.Error("strategy does not exists") + } + + pData, err := json.Marshal(ps) + if err != nil { + return boltvm.Error(err.Error()) + } + return boltvm.Success(pData) +} + +func (g *Governance) GetProposalStrategyType(pt string) *boltvm.Response { + if err := checkProposalType(ProposalType(pt)); err != nil { + return boltvm.Error(err.Error()) + } + + ps := &ProposalStrategy{} + if !g.GetObject(string(pt), ps) { + return boltvm.Error("strategy does not exists") + } + + return boltvm.Success([]byte(ps.Typ)) +} + +// Key ==================================================================== +func ProposalKey(id string) string { + return fmt.Sprintf("%s-%s", PROPOSAL_PREFIX, id) +} + +// Check info ============================================================= +func checkProposalInfo(p *Proposal) error { + if checkProposalType(p.Typ) != nil || strings.Index(p.Id, "-") == -1 || p.Id[0:strings.Index(p.Id, "-")] == "" { + return fmt.Errorf("illegal proposal info") + } + + return nil +} + +func checkProposalType(pt ProposalType) error { + if pt != AppchainMgr && + pt != RuleMgr && + pt != NodeMgr && + pt != ServiceMgr { + return fmt.Errorf("illegal proposal type") + } + return nil +} + +func checkProposalStauts(ps ProposalStatus) error { + if ps != PROPOSED && + ps != APPOVED && + ps != REJECTED { + return fmt.Errorf("illegal proposal status") + } + return nil +} + +func checkStrategyInfo(ps *ProposalStrategy) error { + if checkStrategyType(ps.Typ) != nil || + ps.ParticipateThreshold < 0 || + ps.ParticipateThreshold > 1 { + return fmt.Errorf("illegal proposal strategy info") + } + return nil +} + +func checkStrategyType(pst ProposalStrategyType) error { + if pst != SuperMajorityApprove && + pst != SuperMajorityAgainst && + pst != SimpleMajority { + return fmt.Errorf("illegal proposal strategy type") + } + return nil +} diff --git a/internal/executor/contracts/interchain.go b/internal/executor/contracts/interchain.go index dc87fc3..52c60d2 100755 --- a/internal/executor/contracts/interchain.go +++ b/internal/executor/contracts/interchain.go @@ -23,16 +23,16 @@ type BxhValidators struct { Addresses []string `json:"addresses"` } -func (x *InterchainManager) Register() *boltvm.Response { - interchain, ok := x.getInterchain(x.Caller()) +func (x *InterchainManager) Register(chainId string) *boltvm.Response { + interchain, ok := x.getInterchain(chainId) if !ok { interchain = &pb.Interchain{ - ID: x.Caller(), + ID: chainId, InterchainCounter: make(map[string]uint64), ReceiptCounter: make(map[string]uint64), SourceReceiptCounter: make(map[string]uint64), } - x.setInterchain(x.Caller(), interchain) + x.setInterchain(chainId, interchain) } body, err := interchain.Marshal() if err != nil { diff --git a/internal/executor/contracts/role.go b/internal/executor/contracts/role.go index 63ed76d..05f3d1d 100644 --- a/internal/executor/contracts/role.go +++ b/internal/executor/contracts/role.go @@ -6,6 +6,7 @@ import ( "github.com/meshplus/bitxhub-core/boltvm" "github.com/meshplus/bitxhub-model/constant" + "github.com/meshplus/bitxhub/internal/repo" ) const ( @@ -17,11 +18,11 @@ type Role struct { } func (r *Role) GetRole() *boltvm.Response { - var addrs []string - r.GetObject(adminRolesKey, &addrs) + var admins []*repo.Admin + r.GetObject(adminRolesKey, &admins) - for _, addr := range addrs { - if addr == r.Caller() { + for _, admin := range admins { + if admin.Address == r.Caller() { return boltvm.Success([]byte("admin")) } } @@ -35,11 +36,11 @@ func (r *Role) GetRole() *boltvm.Response { } func (r *Role) IsAdmin(address string) *boltvm.Response { - var addrs []string - r.GetObject(adminRolesKey, &addrs) + var admins []*repo.Admin + r.GetObject(adminRolesKey, &admins) - for _, addr := range addrs { - if addr == address { + for _, admin := range admins { + if admin.Address == address { return boltvm.Success([]byte(strconv.FormatBool(true))) } } @@ -48,10 +49,10 @@ func (r *Role) IsAdmin(address string) *boltvm.Response { } func (r *Role) GetAdminRoles() *boltvm.Response { - var addrs []string - r.GetObject(adminRolesKey, &addrs) + var admins []*repo.Admin + r.GetObject(adminRolesKey, &admins) - ret, err := json.Marshal(addrs) + ret, err := json.Marshal(admins) if err != nil { return boltvm.Error(err.Error()) } @@ -65,6 +66,27 @@ func (r *Role) SetAdminRoles(addrs string) *boltvm.Response { return boltvm.Error(err.Error()) } - r.SetObject(adminRolesKey, as) + admins := make([]*repo.Admin, 0) + for _, addr := range as { + admins = append(admins, &repo.Admin{ + Address: addr, + Weight: 1, + }) + } + + r.SetObject(adminRolesKey, admins) return boltvm.Success(nil) } + +func (r *Role) GetRoleWeight(address string) *boltvm.Response { + var admins []*repo.Admin + r.GetObject(adminRolesKey, &admins) + + for _, admin := range admins { + if admin.Address == address { + return boltvm.Success([]byte(strconv.Itoa(int(admin.Weight)))) + } + } + + return boltvm.Error("account at the address does not exist:" + address) +} diff --git a/internal/executor/executor.go b/internal/executor/executor.go index b95cdf2..c833e53 100755 --- a/internal/executor/executor.go +++ b/internal/executor/executor.go @@ -268,6 +268,12 @@ func registerBoltContracts() map[string]agency.Contract { Address: constant.InterRelayBrokerContractAddr.Address().String(), Contract: &contracts.InterRelayBroker{}, }, + { + Enabled: true, + Name: "governance service", + Address: constant.GovernanceContractAddr.Address().String(), + Contract: &contracts.Governance{}, + }, } ContractsInfo := agency.GetRegisteredContractInfo() diff --git a/internal/ledger/genesis/genesis.go b/internal/ledger/genesis/genesis.go index ab119e9..aecd1bc 100644 --- a/internal/ledger/genesis/genesis.go +++ b/internal/ledger/genesis/genesis.go @@ -5,6 +5,7 @@ import ( "github.com/meshplus/bitxhub-kit/bytesutil" "github.com/meshplus/bitxhub-kit/types" + "github.com/meshplus/bitxhub-model/constant" "github.com/meshplus/bitxhub-model/pb" "github.com/meshplus/bitxhub/internal/ledger" "github.com/meshplus/bitxhub/internal/repo" @@ -16,17 +17,21 @@ var ( // Initialize initialize block func Initialize(genesis *repo.Genesis, lg ledger.Ledger) error { - for _, addr := range genesis.Addresses { - lg.SetBalance(types.NewAddressByStr(addr), 100000000) - } - - body, err := json.Marshal(genesis.Addresses) + body, err := json.Marshal(genesis.Admins) if err != nil { return err } lg.SetState(roleAddr, []byte("admin-roles"), body) + for _, admin := range genesis.Admins { + lg.SetBalance(types.NewAddressByStr(admin.Address), 100000000) + } + + for k, v := range genesis.Strategy { + lg.SetState(constant.GovernanceContractAddr.Address(), []byte(k), []byte(v)) + } + accounts, journal := lg.FlushDirtyDataAndComputeJournal() block := &pb.Block{ BlockHeader: &pb.BlockHeader{ diff --git a/internal/repo/config.go b/internal/repo/config.go index f15b2a2..f66608f 100755 --- a/internal/repo/config.go +++ b/internal/repo/config.go @@ -104,7 +104,13 @@ type LogModule struct { } type Genesis struct { - Addresses []string `json:"addresses" toml:"addresses"` + Admins []*Admin `json:"admins" toml:"admins"` + Strategy map[string]string `json:"strategy" toml:"strategy"` +} + +type Admin struct { + Address string `json:"address" toml:"address"` + Weight uint64 `json:"weight" toml:"weight"` } type Cert struct { diff --git a/internal/repo/network.go b/internal/repo/network.go index d63bdc3..918012c 100644 --- a/internal/repo/network.go +++ b/internal/repo/network.go @@ -90,8 +90,8 @@ func (config *NetworkConfig) GetVpInfos() map[uint64]*pb.VpInfo { // GetVpGenesisAccount gets genesis address from network config func (config *NetworkConfig) GetVpGenesisAccount() map[uint64]types.Address { m := make(map[uint64]types.Address) - for i, address := range config.Genesis.Addresses { - m[uint64(i)+1] = *types.NewAddressByStr(address) + for i, admin := range config.Genesis.Admins { + m[uint64(i)+1] = *types.NewAddressByStr(admin.Address) } return m } diff --git a/internal/repo/network_test.go b/internal/repo/network_test.go index c59151b..410bc16 100644 --- a/internal/repo/network_test.go +++ b/internal/repo/network_test.go @@ -9,10 +9,26 @@ import ( func TestNetworkConfig(t *testing.T) { path := "./testdata" - cfg, err := loadNetworkConfig(path, Genesis{Addresses: []string{"0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", - "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", - "0x97c8B516D19edBf575D72a172Af7F418BE498C37", - "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8"}}) + cfg, err := loadNetworkConfig(path, Genesis{ + Admins: []*Admin{ + &Admin{ + Address: "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", + Weight: 1, + }, + &Admin{ + Address: "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", + Weight: 1, + }, + &Admin{ + Address: "0x97c8B516D19edBf575D72a172Af7F418BE498C37", + Weight: 1, + }, + &Admin{ + Address: "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8", + Weight: 1, + }, + }, + }) require.Nil(t, err) peers, err := cfg.GetNetworkPeers() diff --git a/pkg/proof/proof_test.go b/pkg/proof/proof_test.go index bcecf92..b885042 100644 --- a/pkg/proof/proof_test.go +++ b/pkg/proof/proof_test.go @@ -6,12 +6,11 @@ import ( "testing" "time" - "github.com/meshplus/bitxhub-kit/crypto" - "github.com/meshplus/bitxhub-kit/crypto/asym" - "github.com/golang/mock/gomock" appchainMgr "github.com/meshplus/bitxhub-core/appchain-mgr" "github.com/meshplus/bitxhub-core/validator/mock_validator" + "github.com/meshplus/bitxhub-kit/crypto" + "github.com/meshplus/bitxhub-kit/crypto/asym" "github.com/meshplus/bitxhub-kit/log" "github.com/meshplus/bitxhub-kit/types" "github.com/meshplus/bitxhub-model/constant" @@ -123,11 +122,10 @@ func TestVerifyPool_CheckProof(t *testing.T) { func TestVerifyPool_CheckProof2(t *testing.T) { mockCtl := gomock.NewController(t) - mockLedger := mock_ledger.NewMockLedger(mockCtl) mockEngine := mock_validator.NewMockEngine(mockCtl) chain := &appchainMgr.Appchain{ - Status: appchainMgr.APPROVED, + Status: appchainMgr.AppchainAvailable, ID: from, Name: "appchain" + from, Validators: "", @@ -154,10 +152,6 @@ func TestVerifyPool_CheckProof2(t *testing.T) { addrsData, err := json.Marshal(bv) require.Nil(t, err) - chainData, err := json.Marshal(chain) - require.Nil(t, err) - - mockLedger.EXPECT().GetState(constant.AppchainMgrContractAddr.Address(), gomock.Any()).Return(true, chainData) mockEngine.EXPECT().Validate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() ibtp := getIBTP(t, 1, pb.IBTP_RECEIPT_SUCCESS, nil) @@ -194,12 +188,6 @@ func TestVerifyPool_CheckProof3(t *testing.T) { mockLedger := mock_ledger.NewMockLedger(mockCtl) mockEngine := mock_validator.NewMockEngine(mockCtl) - rl := &contracts.Rule{ - Address: contract, - } - rlData, err := json.Marshal(rl) - require.Nil(t, err) - mockLedger.EXPECT().GetState(constant.AppchainMgrContractAddr.Address(), gomock.Any()).Return(true, []byte("123")) mockEngine.EXPECT().Validate(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(true, nil).AnyTimes() @@ -219,7 +207,6 @@ func TestVerifyPool_CheckProof3(t *testing.T) { Extra: proof, } - mockLedger.EXPECT().GetState(constant.RuleManagerContractAddr.Address(), gomock.Any()).Return(true, rlData) txWithIBTP.TransactionHash = txWithIBTP.Hash() ok, err := vp.CheckProof(txWithIBTP) require.NotNil(t, err) diff --git a/pkg/vm/boltvm/boltvm_test.go b/pkg/vm/boltvm/boltvm_test.go index 1c2fc5c..adda50d 100644 --- a/pkg/vm/boltvm/boltvm_test.go +++ b/pkg/vm/boltvm/boltvm_test.go @@ -8,6 +8,7 @@ import ( "github.com/golang/mock/gomock" "github.com/meshplus/bitxhub-core/agency" + appchain_mgr "github.com/meshplus/bitxhub-core/appchain-mgr" "github.com/meshplus/bitxhub-core/validator/mock_validator" "github.com/meshplus/bitxhub-kit/log" "github.com/meshplus/bitxhub-kit/types" @@ -69,6 +70,12 @@ func GetBoltContracts() map[string]agency.Contract { Address: constant.AssetExchangeContractAddr.Address().String(), Contract: &contracts.AssetExchange{}, }, + { + Enabled: true, + Name: "governance service", + Address: constant.GovernanceContractAddr.Address().String(), + Contract: &contracts.Governance{}, + }, } ContractsInfo := agency.GetRegisteredContractInfo() @@ -85,7 +92,7 @@ func GetBoltContracts() map[string]agency.Contract { func TestRegister(t *testing.T) { registers := GetBoltContracts() - require.Equal(t, len(registers), 7) + require.Equal(t, len(registers), 8) contract, err := GetBoltContract(constant.StoreContractAddr.Address().String(), registers) require.Nil(t, err) @@ -115,7 +122,23 @@ func TestBoltVM_Run(t *testing.T) { data := make([][]byte, 0) data = append(data, []byte("1")) - mockLedger.EXPECT().QueryByPrefix(gomock.Any(), gomock.Any()).Return(true, data).AnyTimes() + proposalData, err := json.Marshal(&contracts.Proposal{ + Id: from + "-0", + }) + require.Nil(t, err) + proposals := make([][]byte, 0) + proposals = append(proposals, proposalData) + mockLedger.EXPECT().QueryByPrefix(gomock.Any(), contracts.PROPOSAL_PREFIX).Return(true, proposals).AnyTimes() + mockLedger.EXPECT().QueryByPrefix(gomock.Any(), appchain_mgr.PREFIX).Return(true, data).AnyTimes() + mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) { + switch addr.String() { + case constant.AppchainMgrContractAddr.String(): + return false, nil + case constant.InterchainContractAddr.String(): + return false, nil + } + return false, nil + }).Times(1) mockLedger.EXPECT().GetState(gomock.Any(), gomock.Any()).DoAndReturn(func(addr *types.Address, key []byte) (bool, []byte) { switch addr.String() { case constant.AppchainMgrContractAddr.String(): diff --git a/tester/case002_appchain_test.go b/tester/case002_appchain_test.go index f6d11e2..e54b723 100755 --- a/tester/case002_appchain_test.go +++ b/tester/case002_appchain_test.go @@ -4,8 +4,10 @@ import ( "encoding/json" "path/filepath" "strconv" - "testing" + "github.com/meshplus/bitxhub/internal/executor/contracts" + + appchainMgr "github.com/meshplus/bitxhub-core/appchain-mgr" "github.com/meshplus/bitxhub-kit/crypto" "github.com/meshplus/bitxhub-kit/crypto/asym" "github.com/meshplus/bitxhub-kit/types" @@ -18,22 +20,10 @@ import ( type RegisterAppchain struct { suite.Suite - api api.CoreAPI - privKey crypto.PrivateKey - from *types.Address -} - -type Appchain struct { - ID string `json:"id"` - Name string `json:"name"` - Validators string `json:"validators"` - ConsensusType int32 `json:"consensus_type"` - // 0 => registered, 1 => approved, -1 => rejected - Status int32 `json:"status"` - ChainType string `json:"chain_type"` - Desc string `json:"desc"` - Version string `json:"version"` - PublicKey string `json:"public_key"` + api api.CoreAPI + privKey crypto.PrivateKey + from *types.Address + normalNonce uint64 } func (suite *RegisterAppchain) SetupSuite() { @@ -43,6 +33,7 @@ func (suite *RegisterAppchain) SetupSuite() { suite.from, err = suite.privKey.PublicKey().Address() suite.Require().Nil(err) + suite.normalNonce = 1 } // Appchain registers in bitxhub @@ -51,7 +42,7 @@ func (suite *RegisterAppchain) TestRegisterAppchain() { suite.Require().Nil(err) args := []*pb.Arg{ - pb.String(""), + pb.String("validators"), pb.Int32(0), pb.String("hyperchain"), pb.String("税务链"), @@ -60,9 +51,19 @@ func (suite *RegisterAppchain) TestRegisterAppchain() { pb.String(string(pub)), } - ret, err := invokeBVMContract(suite.api, suite.privKey, 1, constant.AppchainMgrContractAddr.Address(), "Register", args...) + ret, err := invokeBVMContract(suite.api, suite.privKey, suite.normalNonce, constant.AppchainMgrContractAddr.Address(), "Register", args...) suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + suite.normalNonce++ + registerRes := &contracts.RegisterResult{} + err = json.Unmarshal(ret.Ret, registerRes) + suite.Require().Nil(err) + chainId := registerRes.ChainID + + ret, err = invokeBVMContract(suite.api, suite.privKey, suite.normalNonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(chainId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + suite.normalNonce++ suite.Require().Equal("hyperchain", gjson.Get(string(ret.Ret), "chain_type").String()) } @@ -92,11 +93,7 @@ func (suite *RegisterAppchain) TestFetchAppchains() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ - - appchain := Appchain{} - err = json.Unmarshal(ret.Ret, &appchain) - suite.Require().Nil(err) - id1 := appchain.ID + id1 := gjson.Get(string(ret.Ret), "chain_id").String() args = []*pb.Arg{ pb.String(""), @@ -126,7 +123,7 @@ func (suite *RegisterAppchain) TestFetchAppchains() { suite.Require().GreaterOrEqual(num, len(result.Array())) k2Nonce++ - ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "CountApprovedAppchains") + ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "CountAvailableAppchains") suite.Require().Nil(err) suite.Require().True(ret.IsSuccess()) num, err = strconv.Atoi(string(ret.Ret)) @@ -134,12 +131,6 @@ func (suite *RegisterAppchain) TestFetchAppchains() { suite.Require().EqualValues(0, num) k2Nonce++ - //FetchAuditRecords - ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "FetchAuditRecords", pb.String(string(id1))) - suite.Require().Nil(err) - suite.Require().True(ret.IsSuccess()) - k1Nonce++ - //AppChain ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Appchain") suite.Require().Nil(err) @@ -194,11 +185,15 @@ func (suite *RegisterAppchain) TestGetPubKeyByChainID() { suite.Require().True(ret.IsSuccess(), string(ret.Ret)) suite.Require().Nil(err) k2Nonce++ + id2 := gjson.Get(string(ret.Ret), "chain_id").String() - appchain2 := Appchain{} + ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k2Nonce++ + appchain2 := appchainMgr.Appchain{} err = json.Unmarshal(ret.Ret, &appchain2) suite.Require().Nil(err) - id2 := appchain2.ID //GetPubKeyByChainID ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetPubKeyByChainID", pb.String(string(id2))) @@ -229,13 +224,8 @@ func (suite *RegisterAppchain) TestUpdateAppchains() { suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ - appchain := Appchain{} - err = json.Unmarshal(ret.Ret, &appchain) - suite.Require().Nil(err) - id1 := appchain.ID - //Admin Chain - path := "./test_data/config/node1/key.json" + path := "./test_data/config/node2/key.json" keyPath := filepath.Join(path) priAdmin, err := asym.RestorePrivateKey(keyPath, "bitxhub") suite.Require().Nil(err) @@ -257,16 +247,6 @@ func (suite *RegisterAppchain) TestUpdateAppchains() { suite.Require().True(ret.IsSuccess(), string(ret.Ret)) adminNonce++ - //Audit - ret, err = invokeBVMContract(suite.api, priAdmin, adminNonce, constant.AppchainMgrContractAddr.Address(), "Audit", - pb.String(string(id1)), - pb.Int32(1), - pb.String("通过"), - ) - suite.Require().Nil(err) - suite.Require().True(ret.IsSuccess()) - adminNonce++ - //UpdateAppchain args = []*pb.Arg{ pb.String(""), @@ -279,10 +259,11 @@ func (suite *RegisterAppchain) TestUpdateAppchains() { } ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "UpdateAppchain", args...) suite.Require().Nil(err) - suite.Require().True(ret.IsSuccess()) + // this appchain is registing, can not be updated + suite.Require().False(ret.IsSuccess()) k1Nonce++ } -func TestRegisterAppchain(t *testing.T) { - suite.Run(t, &RegisterAppchain{}) -} +//func TestRegisterAppchain(t *testing.T) { +// suite.Run(t, &RegisterAppchain{}) +//} diff --git a/tester/case003_interchain_test.go b/tester/case003_interchain_test.go index df3526d..3e93db1 100644 --- a/tester/case003_interchain_test.go +++ b/tester/case003_interchain_test.go @@ -2,11 +2,15 @@ package tester import ( "crypto/sha256" - "encoding/json" "io/ioutil" - "testing" "time" + "github.com/meshplus/bitxhub/internal/executor/contracts" + + "github.com/tidwall/gjson" + + appchain_mgr "github.com/meshplus/bitxhub-core/appchain-mgr" + "github.com/meshplus/bitxhub-kit/crypto" "github.com/meshplus/bitxhub-kit/crypto/asym" "github.com/meshplus/bitxhub-model/constant" @@ -53,6 +57,21 @@ func (suite *Interchain) TestHandleIBTP() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ + id1 := gjson.Get(string(ret.Ret), "chain_id").String() + + ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k1Nonce++ + + ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager", + pb.String(string(appchain_mgr.EventRegister)), + pb.String(string(contracts.APPOVED)), + pb.Bytes(ret.Ret), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k1Nonce++ ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register", pb.String(""), @@ -66,6 +85,21 @@ func (suite *Interchain) TestHandleIBTP() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess()) k2Nonce++ + id2 := gjson.Get(string(ret.Ret), "chain_id").String() + + ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k2Nonce++ + + ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Manager", + pb.String(string(appchain_mgr.EventRegister)), + pb.String(string(contracts.APPOVED)), + pb.Bytes(ret.Ret), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k2Nonce++ // deploy rule bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm") @@ -126,6 +160,21 @@ func (suite *Interchain) TestGetIBTPByID() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ + id1 := gjson.Get(string(ret.Ret), "chain_id").String() + + ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k1Nonce++ + + ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager", + pb.String(string(appchain_mgr.EventRegister)), + pb.String(string(contracts.APPOVED)), + pb.Bytes(ret.Ret), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k1Nonce++ ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register", pb.String(""), @@ -139,6 +188,21 @@ func (suite *Interchain) TestGetIBTPByID() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k2Nonce++ + id2 := gjson.Get(string(ret.Ret), "chain_id").String() + + ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id2)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k2Nonce++ + + ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Manager", + pb.String(string(appchain_mgr.EventRegister)), + pb.String(string(contracts.APPOVED)), + pb.Bytes(ret.Ret), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k2Nonce++ contractByte, err := ioutil.ReadFile("./test_data/fabric_policy.wasm") suite.Require().Nil(err) @@ -188,21 +252,6 @@ func (suite *Interchain) TestGetIBTPByID() { k1Nonce++ } -func (suite *Interchain) TestAudit() { - k, err := asym.GenerateKeyPair(crypto.Secp256k1) - suite.Require().Nil(err) - kNonce := uint64(1) - - ret, err := invokeBVMContract(suite.api, k, kNonce, constant.AppchainMgrContractAddr.Address(), "Audit", - pb.String("0x123"), - pb.Int32(1), - pb.String("通过"), - ) - suite.Require().Nil(err) - suite.Contains(string(ret.Ret), "caller is not an admin account") - kNonce++ -} - func (suite *Interchain) TestInterchain() { k1, err := asym.GenerateKeyPair(crypto.Secp256k1) suite.Require().Nil(err) @@ -223,11 +272,21 @@ func (suite *Interchain) TestInterchain() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ + id1 := gjson.Get(string(ret.Ret), "chain_id").String() - appchain := Appchain{} - err = json.Unmarshal(ret.Ret, &appchain) + ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(id1)) suite.Require().Nil(err) - id1 := appchain.ID + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k1Nonce++ + + ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.AppchainMgrContractAddr.Address(), "Manager", + pb.String(string(appchain_mgr.EventRegister)), + pb.String(string(contracts.APPOVED)), + pb.Bytes(ret.Ret), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + k1Nonce++ ret, err = invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "Interchain") suite.Require().Nil(err) @@ -246,14 +305,16 @@ func (suite *Interchain) TestInterchain() { func (suite *Interchain) TestRegister() { k1, err := asym.GenerateKeyPair(crypto.Secp256k1) suite.Require().Nil(err) + from1, err := k1.PublicKey().Address() + suite.Require().Nil(err) k1Nonce := uint64(1) - ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "Register") + ret, err := invokeBVMContract(suite.api, k1, k1Nonce, constant.InterchainContractAddr.Address(), "Register", pb.String(from1.Address)) suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ } -func TestInterchain(t *testing.T) { - suite.Run(t, &Interchain{}) -} +//func TestInterchain(t *testing.T) { +// suite.Run(t, &Interchain{}) +//} diff --git a/tester/case004_role_test.go b/tester/case004_role_test.go index b9d18c3..502f089 100644 --- a/tester/case004_role_test.go +++ b/tester/case004_role_test.go @@ -136,11 +136,7 @@ func (suite *Role) TestGetRuleAddress() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k1Nonce++ - - appchain := Appchain{} - err = json.Unmarshal(ret.Ret, &appchain) - suite.Require().Nil(err) - id1 := appchain.ID + id1 := gjson.Get(string(ret.Ret), "chain_id").String() ret, err = invokeBVMContract(suite.api, k2, k2Nonce, constant.AppchainMgrContractAddr.Address(), "Register", pb.String(""), @@ -154,11 +150,7 @@ func (suite *Role) TestGetRuleAddress() { suite.Require().Nil(err) suite.Require().True(ret.IsSuccess(), string(ret.Ret)) k2Nonce++ - - appchain = Appchain{} - err = json.Unmarshal(ret.Ret, &appchain) - suite.Require().Nil(err) - id2 := appchain.ID + id2 := gjson.Get(string(ret.Ret), "chain_id").String() // deploy rule bytes, err := ioutil.ReadFile("./test_data/hpc_rule.wasm") diff --git a/tester/case006_vote_test.go b/tester/case006_vote_test.go new file mode 100644 index 0000000..d631243 --- /dev/null +++ b/tester/case006_vote_test.go @@ -0,0 +1,210 @@ +package tester + +import ( + "encoding/json" + "path/filepath" + "strconv" + + appchainMgr "github.com/meshplus/bitxhub-core/appchain-mgr" + "github.com/meshplus/bitxhub-kit/crypto" + "github.com/meshplus/bitxhub-kit/crypto/asym" + "github.com/meshplus/bitxhub-model/constant" + "github.com/meshplus/bitxhub-model/pb" + "github.com/meshplus/bitxhub/internal/coreapi/api" + "github.com/meshplus/bitxhub/internal/executor/contracts" + "github.com/stretchr/testify/suite" + "github.com/tidwall/gjson" +) + +type Governance struct { + suite.Suite + api api.CoreAPI + privKey crypto.PrivateKey +} + +func (suite *Governance) SetupSuite() { +} + +func (suite *Governance) TestGovernance() { + path1 := "./test_data/config/node1/key.json" + path2 := "./test_data/config/node2/key.json" + path3 := "./test_data/config/node3/key.json" + path4 := "./test_data/config/node4/key.json" + keyPath1 := filepath.Join(path1) + keyPath2 := filepath.Join(path2) + keyPath3 := filepath.Join(path3) + keyPath4 := filepath.Join(path4) + priAdmin1, err := asym.RestorePrivateKey(keyPath1, "bitxhub") + suite.Require().Nil(err) + priAdmin2, err := asym.RestorePrivateKey(keyPath2, "bitxhub") + suite.Require().Nil(err) + priAdmin3, err := asym.RestorePrivateKey(keyPath3, "bitxhub") + suite.Require().Nil(err) + priAdmin4, err := asym.RestorePrivateKey(keyPath4, "bitxhub") + suite.Require().Nil(err) + fromAdmin1, err := priAdmin1.PublicKey().Address() + suite.Require().Nil(err) + fromAdmin2, err := priAdmin2.PublicKey().Address() + suite.Require().Nil(err) + fromAdmin3, err := priAdmin3.PublicKey().Address() + suite.Require().Nil(err) + fromAdmin4, err := priAdmin4.PublicKey().Address() + suite.Require().Nil(err) + adminNonce1 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin1.String()) + adminNonce2 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin2.String()) + adminNonce3 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin3.String()) + adminNonce4 := suite.api.Broker().GetPendingNonceByAccount(fromAdmin4.String()) + + appchainPri, err := asym.GenerateKeyPair(crypto.Secp256k1) + suite.Require().Nil(err) + appchainPub, err := appchainPri.PublicKey().Bytes() + suite.Require().Nil(err) + appchainNonce := uint64(1) + + // 1. Register ============================================== + ret, err := invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register", + pb.String("validators"), + pb.Int32(0), + pb.String("hyperchain"), + pb.String("税务链"), + pb.String("趣链税务链"), + pb.String("1.8"), + pb.String(string(appchainPub)), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + appchainNonce++ + chainId := gjson.Get(string(ret.Ret), "chain_id").String() + registerProposalId := gjson.Get(string(ret.Ret), "proposal_id").String() + + // repeated registration + ret, err = invokeBVMContract(suite.api, appchainPri, appchainNonce, constant.AppchainMgrContractAddr.Address(), "Register", + pb.String("validators"), + pb.Int32(0), + pb.String("hyperchain"), + pb.String("税务链"), + pb.String("趣链税务链"), + pb.String("1.8"), + pb.String(string(appchainPub)), + ) + suite.Require().Nil(err) + suite.Require().False(ret.IsSuccess(), string(ret.Ret)) + appchainNonce++ + + // get proposal + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "GetProposal", pb.String(registerProposalId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + p := contracts.Proposal{} + err = json.Unmarshal(ret.Ret, &p) + suite.Require().Nil(err) + suite.Require().Equal("register", p.Des, "des") + + // get chain status + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(chainId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + chain := appchainMgr.Appchain{} + err = json.Unmarshal(ret.Ret, &chain) + suite.Require().Nil(err) + suite.Require().Equal(appchainMgr.AppchainRegisting, chain.Status) + + // get role weight + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.RoleContractAddr.Address(), "GetRoleWeight", pb.String(fromAdmin1.Address)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + w, err := strconv.Atoi(string(ret.Ret)) + suite.Require().Nil(err) + suite.Require().Equal(1, w, "weight") + + // vote1: approve + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "Vote", + pb.String(registerProposalId), + pb.String(contracts.BallotApprove), + pb.String("reason"), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + + // get ballot + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "GetBallot", + pb.String(fromAdmin1.Address), + pb.String(registerProposalId), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + b := &contracts.Ballot{} + err = json.Unmarshal(ret.Ret, &b) + suite.Require().Nil(err) + suite.Require().Equal(string(contracts.APPOVED), b.Approve) + + // vote2: reject + ret, err = invokeBVMContract(suite.api, priAdmin2, adminNonce2, constant.GovernanceContractAddr.Address(), "Vote", + pb.String(registerProposalId), + pb.String(contracts.BallotReject), + pb.String("reason"), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce2++ + + // vote3: approve -> proposal approve + ret, err = invokeBVMContract(suite.api, priAdmin3, adminNonce3, constant.GovernanceContractAddr.Address(), "Vote", + pb.String(registerProposalId), + pb.String(contracts.BallotApprove), + pb.String("reason"), + ) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce3++ + + // vote4: error, the proposal is closed + ret, err = invokeBVMContract(suite.api, priAdmin4, adminNonce4, constant.GovernanceContractAddr.Address(), "Vote", + pb.String(registerProposalId), + pb.String(contracts.BallotApprove), + pb.String("reason"), + ) + suite.Require().Nil(err) + suite.Require().False(ret.IsSuccess(), string(ret.Ret)) + adminNonce4++ + + // get approve num + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "GetApproveNum", pb.String(registerProposalId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + num, err := strconv.Atoi(string(ret.Ret)) + suite.Require().Nil(err) + suite.Require().Equal(2, num, "approveNum") + + // get against num + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "GetAgainstNum", pb.String(registerProposalId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + num, err = strconv.Atoi(string(ret.Ret)) + suite.Require().Nil(err) + suite.Require().Equal(1, num, "againstNum") + + // get proposal status + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.GovernanceContractAddr.Address(), "GetStatus", pb.String(registerProposalId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + proposalStatus := string(ret.Ret) + suite.Require().Equal(contracts.APPOVED, contracts.ProposalStatus(proposalStatus)) + + // get chain status + ret, err = invokeBVMContract(suite.api, priAdmin1, adminNonce1, constant.AppchainMgrContractAddr.Address(), "GetAppchain", pb.String(chainId)) + suite.Require().Nil(err) + suite.Require().True(ret.IsSuccess(), string(ret.Ret)) + adminNonce1++ + err = json.Unmarshal(ret.Ret, &chain) + suite.Require().Nil(err) + suite.Require().Equal(appchainMgr.AppchainAvailable, chain.Status) +} diff --git a/tester/test_data/config/node1/bitxhub.toml b/tester/test_data/config/node1/bitxhub.toml index 3800e12..6c6a6b0 100755 --- a/tester/test_data/config/node1/bitxhub.toml +++ b/tester/test_data/config/node1/bitxhub.toml @@ -33,9 +33,20 @@ solo = false plugin = "plugins/raft.so" [genesis] -addresses = [ - "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", - "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", - "0x97c8B516D19edBf575D72a172Af7F418BE498C37", - "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" -] \ No newline at end of file + [[genesis.admins]] + address = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013" + weight = 1 + [[genesis.admins]] + address = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34" + weight = 1 + [[genesis.admins]] + address = "0x97c8B516D19edBf575D72a172Af7F418BE498C37" + weight = 1 + [[genesis.admins]] + address = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" + weight = 1 + [genesis.strategy] + AppchainMgr = "SimpleMajority" + RuleMgr = "SimpleMajority" + NodeMgr = "SimpleMajority" + ServiceMgr = "SimpleMajority" \ No newline at end of file diff --git a/tester/test_data/config/node2/bitxhub.toml b/tester/test_data/config/node2/bitxhub.toml index 3200e95..244e453 100755 --- a/tester/test_data/config/node2/bitxhub.toml +++ b/tester/test_data/config/node2/bitxhub.toml @@ -33,9 +33,20 @@ solo = false plugin = "plugins/raft.so" [genesis] -addresses = [ - "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", - "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", - "0x97c8B516D19edBf575D72a172Af7F418BE498C37", - "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" -] \ No newline at end of file + [[genesis.admins]] + address = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013" + weight = 1 + [[genesis.admins]] + address = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34" + weight = 1 + [[genesis.admins]] + address = "0x97c8B516D19edBf575D72a172Af7F418BE498C37" + weight = 1 + [[genesis.admins]] + address = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" + weight = 1 + [genesis.strategy] + AppchainMgr = "SimpleMajority" + RuleMgr = "SimpleMajority" + NodeMgr = "SimpleMajority" + ServiceMgr = "SimpleMajority" \ No newline at end of file diff --git a/tester/test_data/config/node3/bitxhub.toml b/tester/test_data/config/node3/bitxhub.toml index a1bee36..f781f6b 100755 --- a/tester/test_data/config/node3/bitxhub.toml +++ b/tester/test_data/config/node3/bitxhub.toml @@ -33,9 +33,20 @@ solo = false plugin = "plugins/raft3.so" [genesis] -addresses = [ - "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", - "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", - "0x97c8B516D19edBf575D72a172Af7F418BE498C37", - "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" -] \ No newline at end of file + [[genesis.admins]] + address = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013" + weight = 1 + [[genesis.admins]] + address = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34" + weight = 1 + [[genesis.admins]] + address = "0x97c8B516D19edBf575D72a172Af7F418BE498C37" + weight = 1 + [[genesis.admins]] + address = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" + weight = 1 + [genesis.strategy] + AppchainMgr = "SimpleMajority" + RuleMgr = "SimpleMajority" + NodeMgr = "SimpleMajority" + ServiceMgr = "SimpleMajority" \ No newline at end of file diff --git a/tester/test_data/config/node4/bitxhub.toml b/tester/test_data/config/node4/bitxhub.toml index da1a1bf..b41959e 100755 --- a/tester/test_data/config/node4/bitxhub.toml +++ b/tester/test_data/config/node4/bitxhub.toml @@ -33,9 +33,20 @@ solo = false plugin = "plugins/raft4.so" [genesis] -addresses = [ - "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013", - "0x79a1215469FaB6f9c63c1816b45183AD3624bE34", - "0x97c8B516D19edBf575D72a172Af7F418BE498C37", - "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" -] \ No newline at end of file + [[genesis.admins]] + address = "0xc7F999b83Af6DF9e67d0a37Ee7e900bF38b3D013" + weight = 1 + [[genesis.admins]] + address = "0x79a1215469FaB6f9c63c1816b45183AD3624bE34" + weight = 1 + [[genesis.admins]] + address = "0x97c8B516D19edBf575D72a172Af7F418BE498C37" + weight = 1 + [[genesis.admins]] + address = "0xc0Ff2e0b3189132D815b8eb325bE17285AC898f8" + weight = 1 + [genesis.strategy] + AppchainMgr = "SimpleMajority" + RuleMgr = "SimpleMajority" + NodeMgr = "SimpleMajority" + ServiceMgr = "SimpleMajority" \ No newline at end of file diff --git a/tester/tester_test.go b/tester/tester_test.go index 60aedf7..9002e7a 100644 --- a/tester/tester_test.go +++ b/tester/tester_test.go @@ -43,6 +43,7 @@ func TestTester(t *testing.T) { suite.Run(t, &Interchain{api: node3}) suite.Run(t, &Role{api: node4}) suite.Run(t, &Store{api: node1}) + suite.Run(t, &Governance{api: node2}) } func setupNode(t *testing.T, path string) api.CoreAPI { @@ -115,7 +116,7 @@ func newTesterBitXHub(rep *repo.Repo) (*app.BitXHub, error) { } func cleanStorage(basePath string) { - filePath := path.Join(basePath,"storage") + filePath := path.Join(basePath, "storage") err := os.RemoveAll(filePath) if err != nil { fmt.Printf("Clean storage failed, error: %s", err.Error())