当前位置: 首页 > 工具软件 > quorum > 使用案例 >

quorum examples初探

上官凯歌
2023-12-01

环境

  • Ubuntu18.04
  • Docker Engine 18.02.0
  • Docker Compose 1.21+

引言

quorum examples包含Quorum平台的安装示例,启动由7个独立节点组成的功能齐全的Quorum环境,从这个例子中可以测试以太坊平台的共识、隐私和所有预期功能。

1. 安装docker

参考ubuntu安装、更新docker社区版

2. 安装docker-compose

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

3. 启动quorum网络

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

4. 检查网络

  • 运行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
  • docker logs -f 查看特定容器的日志

5. 隐私交易测试

这里我们将做以下实验测试交易:

  • 在node1和node7之间发送隐私交易
  • 证明只有node1和node7可以查看合约的初始状态
  • node1更新合约的状态,并且一旦包含更新交易的区块被网络验证,再次验证只有node1和node7能够看到合约的更新状态

5.1 发送隐私交易

连接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的公钥。

5.1 检查quorum node

通过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.3 检查合约状态

可以通过以下命令得到合约地址(在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)
  • 终端窗口1(node1):
    > private.get()
    42
    
  • 终端窗口2(node4):
    > private.get()
    0
    
  • 终端窗口3(node4):
    > private.get()
    42
    

因此,我们可以看到node1和node7能够读取隐私合约的状态,并且其初始值为42。如果您进行查看private-contract.js您将看到这是创建合同时设置的值。而node4无法读取状态。

5.3 更新合约状态

接下来我们在node1上将合约的状态值更新为4,并在node4和node7上查看最新的状态。

终端窗口1(node1):

> private.set(4,{from:"0xed9d02e382b34818e88b88a309c7fe71e65f419d",privateFor:["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]});
"0x4a5618b41019161f710b16f3c749f88de605ddbb9b3c495809f461d64b30b76e"
  • 终端窗口1(node1):
    > private.get()
    4
    
  • 终端窗口2(node4):
    > private.get()
    0
    
  • 终端窗口3(node4):
    > private.get()
    4
    

所有7个节点都在验证相同的交易区块链,隐私交易仅包含512位哈希值来代替交易数据,并且只有隐私交易的参与方才能查看和更新​​隐私合约状态。

6. 节点许可测试

节点许可是Quorum中的一项功能,它仅允许一组预定义的节点连接到许可的网络。
在此演示中,我们将:

  • 查看当前节点已连接的peer
  • 看一下permissioned-nodes.json文件的细节
  • 证明只有permissioned-nodes.json中指定的节点才能连接到网络
    单个节点可以通过传递-permissioned命令行标志来启用/禁用。如果启用,则只有其中的节点/permissioned-nodes.json可以连接到它。

6.1 查看已经连接的节点

进入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
      }
    }
}]

6.2 节点许可配置

配置在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"
]

6.3 禁用其中一个节点

删掉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

7. cakeshop dashboard

cakeshop是一个集成开发环境,提供管理控制ui,包含节点管理、合约管理、编译沙箱环境、区块浏览器、钱包管理、peer管理。

打开 http://yourip:8999,观察节点是否正常。

8. 停止(关闭)quorum网络

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

 类似资料: