quorum examples
包含Quorum平台的安装示例,启动由7个独立节点组成的功能齐全的Quorum环境,从这个例子中可以测试以太坊平台的共识、隐私和所有预期功能。
参考ubuntu安装、更新docker社区版
sudo curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
git clone https://github.com/jpmorganchase/quorum-examples
cd quorum-examples
docker-compose up -d
默认使用Tessera隐私管理器和Istanbul BFT共识,如果要使用raft共识,请设置QUORUM_CONSENSUS=raft
,启动没有关联的隐私事务管理器,请设置PRIVATE_CONFIG=ignore
,两者可以一同实用。
PRIVATE_CONFIG=ignore QUORUM_CONSENSUS=raft docker-compose up -d
docker ps
检查所有的容器是否正常运行(7个节点、7个TX管理器、一个cakeshop:Quorum的集成开发环境和SDK) IMAGE PORTS
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22005->8545/tcp
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22006->8545/tcp
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22004->8545/tcp
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22000->8545/tcp
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22001->8545/tcp
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22002->8545/tcp
quorumengineering/quorum:2.5.0 8546/tcp, 21000/tcp, 30303/tcp, 50400/tcp, 30303/udp, 0.0.0.0:22003->8545/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9082->9080/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9087->9080/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9083->9080/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9084->9080/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9081->9080/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9085->9080/tcp
quorumengineering/cakeshop:0.11.0 8080/tcp, 8102/tcp, 0.0.0.0:8999->8999/tcp
quorumengineering/tessera:0.10.4 9000/tcp, 0.0.0.0:9086->9080/tcp
这里我们将做以下实验测试交易:
连接node1
docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
发送一笔私有交易
> loadScript('/examples/private-contract.js')
Contract transaction send: TransactionHash: 0x88103cbf9b6e4823e214af0e50e21941ff416cc81320d20049f4d324627bcfcb waiting to be mined...
true
> Contract mined! Address: 0x1932c48b2bf8102ba33b4a6b545c32236e342f34
[object Object]
记录下TransactionHash
的值。
private-contract.js
内容如下:
a = eth.accounts[0]
web3.eth.defaultAccount = a;
// abi and bytecode generated from simplestorage.sol:
// > solcjs --bin --abi simplestorage.sol
var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"payable":false,"type":"constructor"}];
var bytecode = "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029";
var simpleContract = web3.eth.contract(abi);
var simple = simpleContract.new(42, {from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}, function(e, contract) {
if (e) {
console.log("err creating contract", e);
} else {
if (!contract.address) {
console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address);
console.log(contract);
}
}
});
privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]
为node7的公钥。
通过geth attach打开Geth JavaScript控制台,这里我们检查node1、node4、node7.
打开三个终端:
docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
docker exec -it quorum-examples_node4_1 geth attach /qdata/dd/geth.ipc
docker exec -it quorum-examples_node7_1 geth attach /qdata/dd/geth.ipc
在其中一个终端执行以下命令(参数为上一步的TransactionHash
):
>eth.getTransaction("0x88103cbf9b6e4823e214af0e50e21941ff416cc81320d20049f4d324627bcfcb")
{
blockHash: "0x57766df2f6eee01b9babacbb1978ad5d60d2fbcbe117f8ba0d7f68052edb4cac",
blockNumber: 516,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 4700000,
gasPrice: 0,
hash: "0x88103cbf9b6e4823e214af0e50e21941ff416cc81320d20049f4d324627bcfcb",
input: "0x7958c99b76a16c71637b9fa1f7e8728fcdb93c656c18e705ec8daf9b2c7221b7eae9afcb67961dc0bcbac0cc35aa3e51f21c5d7f11a465db6b8d3fe4b96e7426",
nonce: 0,
r: "0x3323ee2c9c4df841b1ac049a048a0e27a09b203c630e92a7e9daa7075e43ef52",
s: "0xe09387feecaf051f4a7fe6cebdc00d5395b2dee56913bddecbc28de0da59398",
to: null,
transactionIndex: 0,
v: "0x25",
value: 0
}
请注意或的v字段值是 “0x25” or “0x26”(十进制为37或38),它标示交易(事务)是是隐私交易。
可以通过以下命令得到合约地址(在5.1中也发送交易后也打印了)
>eth.getTransactionReceipt("0x88103cbf9b6e4823e214af0e50e21941ff416cc81320d20049f4d324627bcfcb")
在三个终端运行以下命令:
定义合约地址
var address = "0x1932c48b2bf8102ba33b4a6b545c32236e342f34"; //替换成你自己的合约地址
定义abi
> var abi = [{"constant":true,"inputs":[],"name":"storedData","outputs":[{"name":"","type":"uint256"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"x","type":"uint256"}],"name":"set","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"get","outputs":[{"name":"retVal","type":"uint256"}],"payable":false,"type":"function"},{"inputs":[{"name":"initVal","type":"uint256"}],"type":"constructor"}];
调用合约
>var private = eth.contract(abi).at(address)
> private.get()
42
> private.get()
0
> private.get()
42
因此,我们可以看到node1和node7能够读取隐私合约的状态,并且其初始值为42。如果您进行查看private-contract.js您将看到这是创建合同时设置的值。而node4无法读取状态。
接下来我们在node1上将合约的状态值更新为4
,并在node4和node7上查看最新的状态。
终端窗口1(node1):
> private.set(4,{from:"0xed9d02e382b34818e88b88a309c7fe71e65f419d",privateFor:["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]});
"0x4a5618b41019161f710b16f3c749f88de605ddbb9b3c495809f461d64b30b76e"
> private.get()
4
> private.get()
0
> private.get()
4
所有7个节点都在验证相同的交易区块链,隐私交易仅包含512位哈希值来代替交易数据,并且只有隐私交易的参与方才能查看和更新隐私合约状态。
节点许可是Quorum中的一项功能,它仅允许一组预定义的节点连接到许可的网络。
在此演示中,我们将:
进入node1的JavaScript控制台
docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
查看已连接的peer
> admin.peers
[{
caps: ["istanbul/64"],
enode: "enode://239c1f044a2b03b6c4713109af036b775c5418fe4ca63b04b1ce00124af00ddab7cc088fc46020cdc783b6207efe624551be4c06a994993d8d70f684688fb7cf@172.16.239.17:21000?discport=0&raftport=50400",
id: "3cb4aaea0f49f73c9de4a34db131288f355bc27090e542ae0be213c20350b767",
name: "Geth/node7-istanbul/v1.8.18-stable-685f59fb(quorum-v2.5.0)/linux-amd64/go1.11.13",
network: {
inbound: false,
localAddress: "172.16.239.11:48834",
remoteAddress: "172.16.239.17:21000",
static: true,
trusted: false
},
protocols: {
istanbul: {
difficulty: 36998,
head: "0xeb173a1f54a705a880fa8f7ee65ee8e8214563713e6a3ca64146d73f5e97f6ac",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416@172.16.239.12:43910",
id: "995dbe18829f1affb75402e66571d97f340c8495b661a823f2c2145ca47d63c2",
name: "Geth/node2-istanbul/v1.8.18-stable-685f59fb(quorum-v2.5.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.12:43910",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 36998,
head: "0xeb173a1f54a705a880fa8f7ee65ee8e8214563713e6a3ca64146d73f5e97f6ac",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://eacaa74c4b0e7a9e12d2fe5fee6595eda841d6d992c35dbbcc50fcee4aa86dfbbdeff7dc7e72c2305d5a62257f82737a8cffc80474c15c611c037f52db1a3a7b@172.16.239.16:52748",
id: "ab62dd7df5863a5f3bb61f458157d4437104e3b8df4451a85f7b2438ef6699ff",
name: "Geth/node6-istanbul/v1.8.18-stable-685f59fb(quorum-v2.5.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.16:52748",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 36997,
head: "0xfe5892fc865d7c3c759d559e5ca0fe4f52ba30ad38021ddf72e320c5a4b9e6b8",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://579f786d4e2830bbcc02815a27e8a9bacccc9605df4dc6f20bcc1a6eb391e7225fff7cb83e5b4ecd1f3a94d8b733803f2f66b7e871961e7b029e22c155c3a778@172.16.239.13:49578",
id: "c39143f98d04e97bd9e31ac1e36cbeb565b061217930767886474e3cde903ac5",
name: "Geth/node3-istanbul/v1.8.18-stable-685f59fb(quorum-v2.5.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.13:49578",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 36997,
head: "0xfe5892fc865d7c3c759d559e5ca0fe4f52ba30ad38021ddf72e320c5a4b9e6b8",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://3d9ca5956b38557aba991e31cf510d4df641dce9cc26bfeb7de082f0c07abb6ede3a58410c8f249dabeecee4ad3979929ac4c7c496ad20b8cfdd061b7401b4f5@172.16.239.14:37980",
id: "c75f7dcb9fd6063f0ada0998f512a992f3fb749857d758ffda1330e590fa915e",
name: "Geth/node4-istanbul/v1.8.18-stable-685f59fb(quorum-v2.5.0)/linux-amd64/go1.11.13",
network: {
inbound: true,
localAddress: "172.16.239.11:21000",
remoteAddress: "172.16.239.14:37980",
static: false,
trusted: false
},
protocols: {
istanbul: {
difficulty: 36998,
head: "0xeb173a1f54a705a880fa8f7ee65ee8e8214563713e6a3ca64146d73f5e97f6ac",
version: 64
}
}
}, {
caps: ["istanbul/64"],
enode: "enode://3701f007bfa4cb26512d7df18e6bbd202e8484a6e11d387af6e482b525fa25542d46ff9c99db87bd419b980c24a086117a397f6d8f88e74351b41693880ea0cb@172.16.239.15:21000?discport=0&raftport=50400",
id: "f06c06f1e958cb2edf90d8bfb912de287f9b047b4228436e94b5b78e3ee16171",
name: "Geth/node5-istanbul/v1.8.18-stable-685f59fb(quorum-v2.5.0)/linux-amd64/go1.11.13",
network: {
inbound: false,
localAddress: "172.16.239.11:51332",
remoteAddress: "172.16.239.15:21000",
static: true,
trusted: false
},
protocols: {
istanbul: {
difficulty: 36998,
head: "0xeb173a1f54a705a880fa8f7ee65ee8e8214563713e6a3ca64146d73f5e97f6ac",
version: 64
}
}
}]
配置在examples/7nodes/permissioned-nodes.json:
[
"enode://ac6b1096ca56b9f6d004b779ae3728bf83f8e22453404cc3cef16a3d9b96608bc67c4b30db88e0a5a6c6390213f7acbe1153ff6d23ce57380104288ae19373ef@127.0.0.1:21000?discport=0&raftport=50401",
"enode://0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416@127.0.0.1:21001?discport=0&raftport=50402",
"enode://579f786d4e2830bbcc02815a27e8a9bacccc9605df4dc6f20bcc1a6eb391e7225fff7cb83e5b4ecd1f3a94d8b733803f2f66b7e871961e7b029e22c155c3a778@127.0.0.1:21002?discport=0&raftport=50403",
"enode://3d9ca5956b38557aba991e31cf510d4df641dce9cc26bfeb7de082f0c07abb6ede3a58410c8f249dabeecee4ad3979929ac4c7c496ad20b8cfdd061b7401b4f5@127.0.0.1:21003?discport=0&raftport=50404",
"enode://3701f007bfa4cb26512d7df18e6bbd202e8484a6e11d387af6e482b525fa25542d46ff9c99db87bd419b980c24a086117a397f6d8f88e74351b41693880ea0cb@127.0.0.1:21004?discport=0&raftport=50405",
"enode://eacaa74c4b0e7a9e12d2fe5fee6595eda841d6d992c35dbbcc50fcee4aa86dfbbdeff7dc7e72c2305d5a62257f82737a8cffc80474c15c611c037f52db1a3a7b@127.0.0.1:21005?discport=0&raftport=50406",
"enode://239c1f044a2b03b6c4713109af036b775c5418fe4ca63b04b1ce00124af00ddab7cc088fc46020cdc783b6207efe624551be4c06a994993d8d70f684688fb7cf@127.0.0.1:21006?discport=0&raftport=50407"
]
删掉permissioned-nodes.json中的一个node6
vi examples/7nodes/permissioned-nodes.json
重启网络
docker-compose restart
连接到node1
docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
查看peer
>admin.peers
可以观察,比原来少了一个peer,也可以在cakeshop dashboard
中查看,切换到node6 可以发现peer数量为0
cakeshop是一个集成开发环境,提供管理控制ui,包含节点管理、合约管理、编译沙箱环境、区块浏览器、钱包管理、peer管理。
打开 http://yourip:8999,观察节点是否正常。
docker-compose down
docker-compose rm
cakeshop容器报错,不能解析host.docker.internal
解决办法:修改docker-compose.yaml,在启动cakeshop的时候将ip地址写入 /etc/hosts
x-cakeshop-def:
&cakeshop-def
image: "${CAKESHOP_DOCKER_IMAGE:-quorumengineering/cakeshop:0.11.0}"
expose:
- "8999"
restart: "no"
healthcheck:
test: ["CMD", "wget", "--spider", "--proxy=off", "http://localhost:8999/actuator/health"]
interval: 5s
timeout: 5s
retries: 20
start_period: 5s
entrypoint:
- /bin/sh
- -c
- |
ip -4 route list match 0/0 | awk '{print $$3" host.docker.internal"}' >> /etc/hosts
DDIR=/qdata/cakeshop/local
rm -rf $${DDIR}
mkdir -p $${DDIR}
DOCKER_IMAGE="${CAKESHOP_DOCKER_IMAGE:-quorumengineering/cakeshop:0.11.0}"
cp /examples/cakeshop/application.properties.template $${DDIR}/application.properties
cp /examples/cakeshop/7nodes_docker.json $${DDIR}/7nodes.json
java -Xms128M -Xmx128M -Dcakeshop.config.dir=/qdata/cakeshop -Dlogging.path=/qdata/logs/cakeshop -jar /opt/cakeshop/cakeshop.war
;;
https://github.com/jpmorganchase/quorum-examples
https://github.com/docker/for-linux/issues/264