Quorum documentation update (#701)

Consolidate documentation for Quorum, its Privacy Manager and usage
This commit is contained in:
Krish1979 2019-05-02 17:38:58 +01:00 committed by Samer Falah
parent be7cc31ce2
commit b2be1cb1a3
57 changed files with 4234 additions and 196 deletions

View File

@ -0,0 +1,11 @@
With no need for POW/POS in a permissioned network, Quorum instead offers multiple consensus mechanisms that are more appropriate for consortium chains:
* __Raft-based Consensus__: A consensus model for faster blocktimes, transaction finality, and on-demand block creation. See [Raft-based consensus for Ethereum/Quorum](../raft) for more information
* __Istanbul BFT (Byzantine Fault Tolerance) Consensus__: A PBFT-inspired consensus algorithm with transaction finality, by AMIS. See [Istanbul BFT Consensus documentation](https://github.com/ethereum/EIPs/issues/650), the [RPC API](../istanbul-rpc-api), and this [technical web article](https://medium.com/getamis/istanbul-bft-ibft-c2758b7fe6ff) for more information
* __Clique POA Consensus__: a default POA consensus algorithm bundled with Go Ethereum. See [Clique POA Consensus Documentation](https://github.com/ethereum/EIPs/issues/225) and a [guide to setup clique json](https://hackernoon.com/hands-on-creating-your-own-local-private-geth-node-beginner-friendly-3d45902cc612) with [puppeth](https://blog.ethereum.org/2017/04/14/geth-1-6-puppeth-master/)

View File

@ -2,7 +2,7 @@
This is an up to date copy of original wiki entry located here https://github.com/getamis/go-ethereum/wiki/RPC-API
# Getting Started
## Getting Started
1. Run Istanbul geth with `--rpcapi "istanbul"`
2. `geth attach`

View File

@ -2,7 +2,7 @@
## Introduction
This directory holds an implementation of a [Raft](https://raft.github.io)-based consensus mechanism (using [etcd](https://github.com/coreos/etcd)'s [Raft implementation](https://github.com/coreos/etcd/tree/master/raft)) as an alternative to Ethereum's default proof-of-work. This is useful for closed-membership/consortium settings where byzantine fault tolerance is not a requirement, and there is a desire for faster blocktimes (on the order of milliseconds instead of seconds) and transaction finality (the absence of forking.) Also, compared with QuorumChain, this consensus mechanism does not "unnecessarily" create empty blocks, and effectively creates blocks "on-demand."
The link attached holds an implementation of a [Raft](https://raft.github.io)-based consensus mechanism (using [etcd](https://github.com/coreos/etcd)'s [Raft implementation](https://github.com/coreos/etcd/tree/master/raft)) as an alternative to Ethereum's default proof-of-work. This is useful for closed-membership/consortium settings where byzantine fault tolerance is not a requirement, and there is a desire for faster blocktimes (on the order of milliseconds instead of seconds) and transaction finality (the absence of forking.) Also, compared with QuorumChain, this consensus mechanism does not "unnecessarily" create empty blocks, and effectively creates blocks "on-demand."
When the `geth` binary is passed the `--raft` flag, the node will operate in "raft mode."
@ -31,7 +31,7 @@ We use the existing Ethereum p2p transport layer to communicate transactions bet
When the minter creates a block, unlike in vanilla Ethereum where the block is written to the database and immediately considered the new head of the chain, we only insert the block or set it to be the new head of the chain once the block has flown through Raft. All nodes will extend the chain together in lock-step as they "apply" their Raft log.
From the point of view of Ethereum, Raft is integrated via an implementation of the [`Service`](https://godoc.org/github.com/jpmorganchase/quorum/node#Service) interface in [node/service.go](https://github.com/jpmorganchase/quorum/blob/master/node/service.go): "an individual protocol that can be registered into a node". Other examples of services are [`Ethereum`](https://godoc.org/github.com/jpmorganchase/quorum/eth#Ethereum), [`ReleaseService`](https://godoc.org/github.com/jpmorganchase/quorum/contracts/release#ReleaseService), and [`Whisper`](https://godoc.org/github.com/jpmorganchase/quorum/whisper/whisperv5#Whisper).
From the point of view of Ethereum, Raft is integrated via an implementation of the [`Service`](https://godoc.org/github.com/jpmorganchase/quorum/node#Service) interface in [`node/service.go`](https://github.com/jpmorganchase/quorum/blob/master/node/service.go): "an individual protocol that can be registered into a node". Other examples of services are [`Ethereum`](https://godoc.org/github.com/jpmorganchase/quorum/eth#Ethereum) and [`Whisper`](https://godoc.org/github.com/jpmorganchase/quorum/whisper/whisperv5#Whisper).
## The lifecycle of a transaction
@ -52,7 +52,7 @@ Let's follow the lifecycle of a typical transaction:
6. _At this point, Raft comes to consensus and appends the log entry containing our block to the Raft log. (The way this happens at the Raft layer is that the leader sends an `AppendEntries` to all followers, and they acknowledge receipt of the message. Once the leader has received a quorum of such acknowledgements, it notifies each node that this new entry has been committed permanently to the log)._
7. Having crossed the network through Raft, the block reaches the `eventLoop` (which processes new Raft log entries.) It has arrived from the leader through `pm.transport`, an instance of [`rafthttp.Transport`](https://godoc.org/github.com/coreos/etcd/rafthttp#Transport).
7. Having crossed the network through Raft, the block reaches the `eventLoop` (which processes new Raft log entries.) It has arrived from the leader through `pm.transport`, an instance of `rafthttp.Transport`.
8. The block is now handled by `applyNewChainHead`. This method checks whether the block extends the chain (i.e. it's parent is the current head of the chain; see below). If it does not extend the chain, it is simply ignored as a no-op. If it does extend chain, the block is validated and then written as the new head of the chain by [`InsertChain`](https://godoc.org/github.com/jpmorganchase/quorum/core#BlockChain.InsertChain).
@ -158,11 +158,11 @@ To add a node to the cluster, attach to a JS console and issue `raft.addPeer(eno
## FAQ
### Could you have a single- or two-node cluster? More generally, could you have an even number of nodes?
**Could you have a single- or two-node cluster? More generally, could you have an even number of nodes ?**
A cluster can tolerate failures that leave a quorum (majority) available. So a cluster of two nodes can't tolerate any failures, three nodes can tolerate one, and five nodes can tolerate two. Typically Raft clusters have an odd number of nodes, since an even number provides no failure tolerance benefit.
### What happens if you don't assume minter and leader are the same node?
**What happens if you don't assume minter and leader are the same node?**
There's no hard reason they couldn't be different. We just co-locate the minter and leader as an optimization.
@ -171,14 +171,14 @@ There's no hard reason they couldn't be different. We just co-locate the minter
Additionally there could even be multiple minters running at the same time, but this would produce contention for which blocks actually extend the chain, reducing the productivity of the cluster (see "races" above).
### I thought there were no forks in a Raft-based blockchain. What's the deal with "speculative minting"?
**I thought there were no forks in a Raft-based blockchain. What's the deal with "speculative minting"?**
"Speculative chains" are not forks in the blockchain. They represent a series ("chain") of blocks that have been sent through Raft, after which each of the blocks may or may not actually end up being included in *the blockchain*.
### Can transactions be reversed? Since raft log entries can be disregarded as "no-ops", does this imply transaction reversal?
**Can transactions be reversed? Since raft log entries can be disregarded as "no-ops", does this imply transaction reversal?**
No. When a Raft log entry containing a new block is disregarded as a "no-op", its transactions will remain in the transaction pool, and so they will be included in a future block in the chain.
### What's the deal with the block timestamp being stored in nanoseconds (instead of seconds, like other consensus mechanisms)?
**What's the deal with the block timestamp being stored in nanoseconds (instead of seconds, like other consensus mechanisms)?**
With raft-based consensus we can produce far more than one block per second, which vanilla Ethereum implicitly disallows (as the default timestamp resolution is in seconds and every block must have a timestamp greater than its parent). For Raft, we store the timestamp in nanoseconds and ensure it is incremented by at least 1 nanosecond per block.

View File

@ -0,0 +1,220 @@
# 7nodes
## Demonstrating Privacy
The 7nodes example comes with some simple contracts to demonstrate the privacy features of Quorum. In this demo we will:
* Send a private transaction between nodes 1 and 7
* Show that only nodes 1 and 7 are able to view the initial state of the contract
* Have Node 1 update the state of the contract and, once the block containing the updated transaction is validated by the network, again verify that only nodes 1 and 7 are able to see the updated state of the contract
!!! tip
[Constellation](../../Privacy/Constellation/Constellation) or [Tessera](../../Privacy/Tessera/Tessera) is used to enable the privacy features of Quorum. To start a Quorum node without its associated privacy transaction manager, set `PRIVATE_CONFIG=ignore` when starting the node.
### Sending a private transaction
First start running the 7nodes example by following the instructions in the [quorum-examples](../Quorum-Examples#getting-started), then send an example private contract from Node 1 to Node 7 (this is denoted by the public key passed via `privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]` in `private-contract.js`):
``` bash
./runscript.sh private-contract.js
```
Make note of the `TransactionHash` printed to the terminal.
### Inspecting the Quorum nodes
We can inspect any of the Quorum nodes by using `geth attach` to open the Geth JavaScript console. For this demo, we will be inspecting Node 1, Node 7 and Node 4.
It is recommended to use separate terminal windows for each node we are inspecting. In each terminal, ensure you are in the `path/to/7nodes` directory, then:
- If you aren't already running the 7nodes example, in terminal 1 run `./{consensus}-init.sh` followed by `./{consensus}-start.sh`
- In terminal 1 run `geth attach ipc:qdata/dd1/geth.ipc` to attach to node 1
- In terminal 2 run `geth attach ipc:qdata/dd4/geth.ipc` to attach to node 4
- In terminal 3 run `geth attach ipc:qdata/dd7/geth.ipc` to attach to node 7
To look at the private transaction that was just sent, run the following command in one of the terminals:
``` sh
eth.getTransaction("0xe28912c5694a1b8c4944b2252d5af21724e9f9095daab47bac37b1db0340e0bf")
```
where you should replace this hash with the TransactionHash that was previously printed to the terminal. This will print something of the form:
``` sh
{
blockHash: "0x4d6eb0d0f971b5e0394a49e36ba660c69e62a588323a873bb38610f7b9690b34",
blockNumber: 1,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 4700000,
gasPrice: 0,
hash: "0xe28912c5694a1b8c4944b2252d5af21724e9f9095daab47bac37b1db0340e0bf",
input: "0x58c0c680ee0b55673e3127eb26e5e537c973cd97c70ec224ccca586cc4d31ae042d2c55704b881d26ca013f15ade30df2dd196da44368b4a7abfec4a2022ec6f",
nonce: 0,
r: "0x4952fd6cd1350c283e9abea95a2377ce24a4540abbbf46b2d7a542be6ed7cce5",
s: "0x4596f7afe2bd23135fa373399790f2d981a9bb8b06144c91f339be1c31ec5aeb",
to: null,
transactionIndex: 0,
v: "0x25",
value: 0
}
```
Note the `v` field value of `"0x25"` or `"0x26"` (37 or 38 in decimal) which indicates this transaction has a private payload (input).
#### Checking the state of the contract
For each of the 3 nodes we'll use the Geth JavaScript console to create a variable called `address` which we will assign to the address of the contract created by Node 1. The contract address can be found in two ways:
- In Node 1's log file: `7nodes/qdata/logs/1.log`
- By reading the `contractAddress` param after calling `eth.getTransactionReceipt(txHash)` ([Ethereum API documentation](https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethgettransactionreceipt)) where `txHash` is the hash printed to the terminal after sending the transaction.
Once you've identified the contract address, run the following command in each terminal:
``` javascript
> var address = "0x1932c48b2bf8102ba33b4a6b545c32236e342f34"; //replace with your contract address
```
Next we'll use ```eth.contract``` to define a contract class with the simpleStorage ABI definition in each terminal:
``` javascript
> 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)
```
The function calls are now available on the contract instance and you can call those methods on the contract. Let's start by examining the initial value of the contract to make sure that only nodes 1 and 7 can see the initialized value.
- In terminal window 1 (Node 1):
``` javascript
> private.get()
42
```
- In terminal window 2 (Node 4):
``` javascript
> private.get()
0
```
- In terminal window 3 (Node 7):
``` javascript
> private.get()
42
```
So we can see nodes 1 and 7 are able to read the state of the private contract and its initial value is 42. If you look in `private-contract.js` you will see that this was the value set when the contract was created. Node 4 is unable to read the state.
### Updating the state of the contract
Next we'll have Node 1 set the state to the value `4` and verify only nodes 1 and 7 are able to view the new state.
In terminal window 1 (Node 1):
``` javascript
> private.set(4,{from:eth.coinbase,privateFor:["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]});
"0xacf293b491cccd1b99d0cfb08464a68791cc7b5bc14a9b6e4ff44b46889a8f70"
```
You can check the log files in `7nodes/qdata/logs/` to see each node validating the block with this new private transaction. Once the block containing the transaction has been validated we can once again check the state from each node 1, 4, and 7.
- In terminal window 1 (Node 1):
``` javascript
> private.get()
4
```
- In terminal window 2 (Node 4):
``` javascript
> private.get()
0
```
- In terminal window 3 (Node 7):
``` javascript
> private.get()
4
```
And there you have it. All 7 nodes are validating the same blockchain of transactions, the private transactions carrying only a 512 bit hash, and only the parties to private transactions are able to view and update the state of private contracts.
## Permissions
Node Permissioning is a feature in Quorum that allows only a pre-defined set of nodes (as identified by their remotekey/enodes) to connect to the permissioned network.
In this demo we will:
- Set up a network with a combination of permissioned and non-permissioned nodes in the cluster
- Look at the details of the `permissioned-nodes.json` file
- Demonstrate that only the nodes that are specified in `permissioned-nodes.json` can connect to the network
### Verify only permissioned nodes are connected to the network.
Attach to the individual nodes via `geth attach path/to/geth.ipc` and use `admin.peers` to check the connected nodes:
``` sh
geth attach qdata/dd1/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.7.2-stable/darwin-amd64/go1.9.2
coinbase: 0xed9d02e382b34818e88b88a309c7fe71e65f419d
at block: 1 (Mon, 29 Oct 47909665359 22:09:51 EST)
datadir: /Users/joel/jpm/quorum-examples/examples/7nodes/qdata/dd1
modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 raft:1.0 rpc:1.0 txpool:1.0 web3:1.0
> admin.peers
[{
caps: ["eth/63"],
id: "0ba6b9f606a43a95edc6247cdb1c1e105145817be7bcafd6b2c0ba15d58145f0dc1a194f70ba73cd6f4cdd6864edc7687f311254c7555cc32e4d45aeb1b80416",
name: "Geth/v1.7.2-stable/darwin-amd64/go1.9.2",
network: {
localAddress: "127.0.0.1:65188",
remoteAddress: "127.0.0.1:21001"
},
protocols: {
eth: {
difficulty: 0,
head: "0xc23b4ebccc79e2636d66939924d46e618269ca1beac5cf1ec83cc862b88b1b71",
version: 63
}
}
},
...
]
```
You can also inspect the log files under `qdata/logs/*.log` for further diagnostics messages around incoming / outgoing connection requests. `grep` for `ALLOWED-BY` or `DENIED-BY`. Be sure to enable verbosity for p2p module.
### Permissioning configuration
Permissioning is granted based on the remote key of the geth node. The remote keys are specified in the `permissioned-nodes.json` and is placed under individual node's `<datadir>`.
The below sample `permissioned-nodes.json` provides a list of nodes permissioned to join the network (node ids truncated for clarity):
``` json
[
"enode://8475a01f22a1f48116dc1f0d22ecaaaf77e@127.0.0.1:30301",
"enode://b5660501f496e60e59ded734a889c97b7da@127.0.0.1:30302",
"enode://54bd7ff4bd971fb80493cf4706455395917@127.0.0.1:30303"
]
```
### Enabling/Disabling permissions
An individual node can enable/disable permissioning by passing the `-permissioned` command line flag. If enabled, then only the nodes that are in the `<datadir>/permissioned-nodes.json` can connect to it. Further, these are the only nodes that this node can make outbound connections to as well.
```
MISCELLANEOUS OPTIONS:
--permissioned If enabled, the node will allow only a defined list of nodes to connect
```
## Next steps
Additional samples can be found in `quorum-examples/examples/7nodes/samples` for you to use and edit. You can also create your own contracts to help you understand how the nodes in a Quorum network work together.
## Reducing the number of nodes
It is easy to reduce the number of nodes used in the example. You may want to do this for memory usage reasons or just to experiment with a different network configuration.
To run the example with 5 nodes instead of 7, the following changes need to be made:
1. In __`raft-start.sh`__:
Comment out the following lines used to start Quorum nodes 6 & 7
```sh
# PRIVATE_CONFIG=qdata/c6/tm.ipc nohup geth --datadir qdata/dd6 $ARGS --raftport 50406 --rpcport 22005 --port 21005 --unlock 0 --password passwords.txt 2>>qdata/logs/6.log &
# PRIVATE_CONFIG=qdata/c7/tm.ipc nohup geth --datadir qdata/dd7 $ARGS --raftport 50407 --rpcport 22006 --port 21006 --unlock 0 --password passwords.txt 2>>qdata/logs/7.log &
```
1. In __`constellation-start.sh`__ or __`tessera-start.sh`__ (depending on which privacy manager you are using):
Change the 2 instances of `for i in {1..7}` to `for i in {1..5}`
After making these changes, the `raft-init.sh` and `raft-start.sh` scripts can be run as normal.
`private-contract.js` will also need to be updated as this is set up to send a transaction from node 1 to node 7. To update the private contract to instead send to node 5, the following steps need to be followed:
1. Copy node 5's public key from `./keys/tm5.pub`
2. Replace the existing `privateFor` in `private-contract.js` with the key copied from `tm5.pub` key, e.g.:
``` javascript
var simple = simpleContract.new(42, {from:web3.eth.accounts[0], data: bytecode, gas: 0x47b760, privateFor: ["R56gy4dn24YOjwyesTczYa8m5xhP6hF2uTMCju/1xkY="]}, function(e, contract) {...}
```
After saving this change, the `./runscript.sh private-contract.js` command can be run as usual to submit the private contract. You can then follow steps described above to verify that node 5 can see the transaction payload and that nodes 2-4 are unable to see the payload.

View File

@ -0,0 +1,66 @@
# Getting started from scratch
## Quorum with Raft consensus
1. Build Quorum as described in the [getting set up](../Setup%20Overview%20%26%20Quickstart) section. Ensure that PATH contains geth and bootnode
2. Create a working directory which will be the base for the new node(s) and change into it
3. Generate one or more accounts for this node using `geth --datadir new-node-1 account new` and take down the account address. A funded account may be required depending what you are trying to accomplish
4. Create a `genesis.json` file see example [here](../genesis). The `alloc` field should be pre-populated with the account you generated at previous step
5. Generate node key `bootnode --genkey=nodekey` and copy it into datadir
6. Execute `bootnode --nodekey=new-node-1/nodekey --writeaddress` and take note of the displayed output. This is the enode id of the new node
7. Create a file called `static-nodes.json` and edit it to match this [example](../permissioned-nodes). Your file should contain a single line for your node with your enode's id and the ports you are going to use for devp2p and raft. Ensure that this file is in your nodes data directory
8. Initialize new node with `geth --datadir new-node-1 init genesis.json`
9. Start your node and send into background with `PRIVATE_CONFIG=ignore nohup geth --datadir new-node-1 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50000 --rpc --rpcaddr 0.0.0.0 --rpcport 22000 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft --emitcheckpoints --port 21000 2>>node.log &`
Your node is now operational and you may attach to it with `geth attach new-node-1/geth.ipc`. This configuration starts Quorum without privacy support as could be evidenced in prefix `PRIVATE_CONFIG=ignore`, please see below sections on [how to enable privacy with privacy transaction managers](../Getting-Started-From-Scratch#adding-privacy-transaction-manager).
### Adding additional node
1. Complete steps 1, 2, 5, and 6 from the previous guide
2. Retrieve current chains `genesis.json` and `static-nodes.json`. `static-nodes.json` should be placed into new nodes data dir
3. Initialize new node with `geth --datadir new-node-2 init genesis.json`
4. Edit `static-nodes.json` and add new entry for the new node you are configuring (should be last)
5. Start your node and send into background with `PRIVATE_CONFIG=ignore nohup geth --datadir new-node-2 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50005 --rpc --rpcaddr 0.0.0.0 --rpcport 22005 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft --emitcheckpoints --port 21005 2>>node.log &`
6. Connect to an already running node of the chain and execute `raft.addPeer('enode://new-nodes-enode-address-from-step-6-of-the-above@127.0.0.1:21005?discport=0&raftport=50005')`
7. Optional: share new `static-nodes.json` with all other chain participants
Your additional node is now operational and is part of the same chain as the previously set up node.
## Quorum with Istanbul BFT consensus
1. Build Quorum as described in the [getting set up](../Setup%20Overview%20%26%20Quickstart) section. Ensure that PATH contains geth and bootnode
2. Install [istanbul-tools](https://github.com/jpmorganchase/istanbul-tools) and place `istanbul` binary into PATH
3. Create a working directory for each of the X number of initial validator nodes
4. Change into the lead (whichever one you consider first) node's working directory and generate the setup files for X initial validator nodes by executing `istanbul setup --num X --nodes --quorum --save --verbose` **only execute this instruction once, i.e. not X times**. This command will generate several items of interest: `static-nodes.json`, `genesis.json`, and nodekeys for all the initial validator nodes which will sit in numbered directories from 0 to X-1
5. Update `static-nodes.json` to include the intended IP and port numbers of all initial validator nodes. In `static-nodes.json`, you will see a different row for each node. For the rest of the installation guide, row Y refers to node Y and row 1 is assumed to correspond to the lead node
6. In each node's working directory, create a data directory called `data`, and inside `data` create the `geth` directory
7. Now we will generate initial accounts for any of the nodes by executing `geth --datadir data account new` in the required node's working directory. The resulting public account address printed in the terminal should be recorded. Repeat as many times as necessary. A set of funded accounts may be required depending what you are trying to accomplish
8. To add accounts to the initial block, edit the `genesis.json` file in the lead node's working directory and update the `alloc` field with the account(s) that were generated at previous step
9. Next we need to distribute the files created in part 4, which currently reside in the lead node's working directory, to all other nodes. To do so, place `genesis.json` in the working directory of all nodes, place `static-nodes.json` in the data folder of each node and place `X/nodekey` in node (X-1)'s `data/geth` directory
10. Switch into working directory of lead node and initialize it with `geth --datadir data init genesis.json`. Repeat for every working directory X created in step 3. *The resulting hash given by executing `geth init` must match for every node*
11. Start all nodes and send into background with `PRIVATE_CONFIG=ignore nohup geth --datadir data --permissioned --nodiscover --istanbul.blockperiod 5 --syncmode full --mine --minerthreads 1 --verbosity 5 --networkid 10 --rpc --rpcaddr 0.0.0.0 --rpcport YOUR_NODES_RPC_PORT_NUMBER --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,istanbul --emitcheckpoints --port YOUR_NODES_PORT_NUMBER 2>>node.log &`, remember to replace `YOUR_NODES_RPC_PORT_NUMBER` and `YOUR_NODES_PORT_NUMBER` with your node's designated port numbers. `YOUR_NODES_PORT_NUMBER` must match the port number for this node decided on in part 5
Your node is now operational and you may attach to it with `geth attach data/geth.ipc`. This configuration starts Quorum without privacy support as could be evidenced in prefix `PRIVATE_CONFIG=ignore`, please see below sections on [[how to enable privacy with privacy transaction managers|From-Scratch#adding-privacy-transaction-manager]].
Please note that istanbul-tools may be used to generate X number of nodes, more information is available in the [docs](https://github.com/jpmorganchase/istanbul-tools).
## Adding privacy transaction manager
### Tessera
1. Build Quorum and install [Tessera](https://github.com/jpmorganchase/tessera/releases) as described in the [getting set up](../Setup%20Overview%20%26%20Quickstart) section. Ensure that PATH contains geth and bootnode. Be aware of the location of the `tessera.jar` release file
2. Generate new keys using `java -jar /path-to-tessera/tessera.jar -keygen -filename new-node-1`
3. Create new configuration file referencing samples [here](../../Privacy/Tessera/Configuration/Sample%20Configuration) with newly generated keys referenced. Note the name of the file or name it `config.json` as done in this example
4. Start your tessera node and send it into background with `java -jar /path-to-tessera/tessera.jar -configfile config.json >> tessera.log 2>&1 &`
5. Start your node and send it into background with `PRIVATE_CONFIG=tm.ipc nohup geth --datadir new-node-1 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50000 --rpc --rpcaddr 0.0.0.0 --rpcport 22000 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft --emitcheckpoints --port 21000 2>>node.log &`
Your node is now operational and you may attach to it with `geth attach new-node-1/geth.ipc`. Tessera IPC bridge will be over a file name defined in your `config.json`, usually named `tm.ipc` as evidenced in prefix `PRIVATE_CONFIG=tm.ipc`. Your node is now able to send and receive private transactions, advertised public node key will be in the `new-node-1.pub` file. Tessera offers a lot of configuration flexibility, please refer [Configuration](../../Privacy/Tessera/Configuration/Configuration%20Overview) section under Tessera for complete and up to date configuration options.
### Constellation
1. Build Quorum and install [Constellation](https://github.com/jpmorganchase/constellation/releases) as described in the [getting set up](../Setup%20Overview%20%26%20Quickstart) section. Ensure that PATH contains geth, bootnode, and constellation-node binaries
2. Generate new keys with `constellation-node --generatekeys=new-node-1`
3. Start your constellation node and send it into background with `constellation-node --url=https://127.0.0.1:9001/ --port=9001 --workdir=. --socket=tm.ipc --publickeys=new-node-1.pub --privatekeys=new-node-1.key --othernodes=https://127.0.0.1:9001/ >> constellation.log 2>&1 &`
4. Start your node and send it into background with `PRIVATE_CONFIG=tm.ipc nohup geth --datadir new-node-1 --nodiscover --verbosity 5 --networkid 31337 --raft --raftport 50000 --rpc --rpcaddr 0.0.0.0 --rpcport 22000 --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3,quorum,raft --emitcheckpoints --port 21000 2>>node.log &`
Your node is now operational and you may attach to it with `geth attach new-node-1/geth.ipc`. Constellation IPC bridge will be over a file name defined in your configuration: in above step #3 see option `--socket=file-name.ipc`. Your node is now able to send and receive private transactions, advertised public node key will be in the `new-node-1.pub` file.
## Enabling permissioned configuration
Quorum ships with a permissions system based on a custom whitelist. Detailed documentation is available in [Network Permissioning](../../Security/Security%20%26%20Permissioning).

View File

@ -0,0 +1,179 @@
# Quorum Examples
This repository contains setup examples for Quorum.
Current examples include:
* [7nodes](../7Nodes/): Starts up a fully-functioning Quorum environment consisting of 7 independent nodes. From this example one can test consensus, privacy, and all the expected functionality of an Ethereum platform.
* [5nodesRTGS](https://github.com/bacen/quorum-examples/tree/master/examples/5nodesRTGS): [__Note__: This links to an external repo which you will need to clone, thanks to @rsarres for this contribution!] Starts up a set of 5 nodes that simulates a Real-time Gross Setlement environment with 3 banks, one regulator (typically a central bank) and an observer that cannot access the private data.
The easiest way to get started with running the examples is to use the vagrant environment (see below).
**Important note**: Any account/encryption keys contained in this repository are for
demonstration and testing purposes only. Before running a real environment, you should
generate new ones using Geth's `account` tool and the `--generate-keys` option for Constellation (or `-keygen` option for Tessera).
## Getting Started
The 7nodes example can be run in three ways:
1. By running a preconfigured Vagrant environment which comes complete with Quorum, Constellation, Tessera and the 7nodes example (__works on any machine__).
1. By running [`docker-compose`](https://docs.docker.com/compose/) against a preconfigured `compose` file ([example](https://github.com/jpmorganchase/quorum-examples/blob/master/docker-compose.yml) from the `quorum-examples` repo) which starts 7nodes example (tested on Windows 10, macOS Mojave & Ubuntu 18.04).
1. By downloading and locally running Quorum, Tessera and the examples (__requires an Ubuntu-based/macOS machine; note that Constellation does not support running locally__)
### Setting up Vagrant
1. Install [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
2. Install [Vagrant](https://www.vagrantup.com/downloads.html)
3. Download and start the Vagrant instance (note: running `vagrant up` takes approx 5 mins):
```sh
git clone https://github.com/jpmorganchase/quorum-examples
cd quorum-examples
vagrant up
vagrant ssh
```
4. To shutdown the Vagrant instance, run `vagrant suspend`. To delete it, run
`vagrant destroy`. To start from scratch, run `vagrant up` after destroying the
instance.
#### Troubleshooting Vagrant
* If you are behind a proxy server, please see https://github.com/jpmorganchase/quorum/issues/23.
* If you are using macOS and get an error saying that the ubuntu/xenial64 image doesn't
exist, please run `sudo rm -r /opt/vagrant/embedded/bin/curl`. This is usually due to
issues with the version of curl bundled with Vagrant.
* If you receive the error `default: cp: cannot open '/path/to/geth.ipc' for reading: Operation not supported` after running `vagrant up`, run `./raft-init.sh` within the 7nodes directory on your local machine. This will remove temporary files created after running 7nodes locally and will enable `vagrant up` to execute correctly.
#### Troubleshooting Vagrant: Memory usage
* The Vagrant instance is allocated 6 GB of memory. This is defined in the `Vagrantfile`, `v.memory = 6144`. This has been deemed a suitable value to allow the VM and examples to run as expected. The memory allocation can be changed by updating this value and running `vagrant reload` to apply the change.
* If the machine you are using has less than 8 GB memory you will likely encounter system issues such as slow down and unresponsiveness when starting the Vagrant instance as your machine will not have the capacity to run the VM. There are several steps that can be taken to overcome this:
1. Shutdown any running processes that are not required
1. If running the [7nodes example](../7Nodes), reduce the number of nodes started up. See the [7nodes: Reducing the number of nodes](../7Nodes#reducing-the-number-of-nodes) for info on how to do this.
1. Set up and run the examples locally. Running locally reduces the load on your memory compared to running in Vagrant.
### Setting up Docker
1. Install Docker (https://www.docker.com/get-started)
* If your Docker distribution does not contain `docker-compose`, follow [this](https://docs.docker.com/compose/install/) to install Docker Compose
* Make sure your Docker daemon has at least 4G memory
1. Download and run `docker-compose`
```sh
git clone https://github.com/jpmorganchase/quorum-examples
cd quorum-examples
docker-compose up -d
```
1. By default, Quorum Network is created using Tessera transaction manager and Istanbul BFT consensus. If you wish to change consensus configuration to Raft, set the environment variable `QUORUM_CONSENSUS=raft` before running `docker-compose`
```sh
QUORUM_CONSENSUS=raft docker-compose up -d
```
1. Run `docker ps` to verify that all quorum-examples containers (7 nodes and 7 tx managers) are **healthy**
1. __Note__: to run the 7nodes demo, use the following snippet to open `geth` Javascript console to a desired node (using container name from `docker ps`) and send a private transaction
```sh
$ docker exec -it quorum-examples_node1_1 geth attach /qdata/dd/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/node1-istanbul/v1.7.2-stable/linux-amd64/go1.9.7
coinbase: 0xd8dba507e85f116b1f7e231ca8525fc9008a6966
at block: 70 (Thu, 18 Oct 2018 14:49:47 UTC)
datadir: /qdata/dd
modules: admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
> loadScript('/examples/private-contract.js')
```
1. Shutdown Quorum Network
```sh
docker-compose down
```
#### Troubleshooting Docker
1. Docker is frozen
* Check if your Docker daemon is allocated enough memory (minimum 4G)
1. Tessera is crashed due to missing file/directory
* This is due to the location of `quorum-examples` folder is not shared
* Please refer to Docker documentation for more details:
* [Docker Desktop for Windows](https://docs.docker.com/docker-for-windows/troubleshoot/#shared-drives)
* [Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/#file-sharing)
* [Docker Machine](https://docs.docker.com/machine/overview/): this depends on what Docker machine provider is used. Please refer to its documentation on how to configure shared folders/drives
### Setting up locally
!!! info
This is only possible with Tessera. Constellation is not supported when running the examples locally. To use Constellation, the examples must be run in Vagrant.
1. Install [Golang](https://golang.org/dl/)
2. Download and build [Quorum](https://github.com/jpmorganchase/quorum/):
```sh
git clone https://github.com/jpmorganchase/quorum
cd quorum
make
GETHDIR=`pwd`; export PATH=$GETHDIR/build/bin:$PATH
cd ..
```
3. Download and build Tessera (see [README](https://github.com/jpmorganchase/tessera) for build options)
```bash
git clone https://github.com/jpmorganchase/tessera.git
cd tessera
mvn install
```
4. Download quorum-examples
```sh
git clone https://github.com/jpmorganchase/quorum-examples
```
### Running the 7nodes example
Shell scripts are included in the examples to make it simple to configure the network and start submitting transactions.
All logs and temporary data are written to the `qdata` folder.
#### Using Raft consensus
1. Navigate to the 7nodes example, configure the Quorum nodes and initialize accounts & keystores:
```sh
cd path/to/7nodes
./raft-init.sh
```
2. Start the Quorum and privacy manager nodes (Constellation or Tessera):
- If running in Vagrant:
```sh
./raft-start.sh
```
By default, Constellation will be used as the privacy manager. To use Tessera run the following:
```
./raft-start.sh tessera
```
By default, `raft-start.sh` will look in `/home/vagrant/tessera/tessera-app/target/tessera-app-{version}-app.jar` for the Tessera jar.
- If running locally with Tessera:
```
./raft-start.sh tessera --tesseraOptions "--tesseraJar /path/to/tessera-app.jar"
```
The Tessera jar location can also be specified by setting the environment variable `TESSERA_JAR`.
3. You are now ready to start sending private/public transactions between the nodes
#### Using Istanbul BFT consensus
To run the example using __Istanbul BFT__ consensus use the corresponding commands:
```sh
istanbul-init.sh
istanbul-start.sh
istanbul-start.sh tessera
stop.sh
```
#### Using Clique POA consensus
To run the example using __Clique POA__ consensus use the corresponding commands:
```sh
clique-init.sh
clique-start.sh
clique-start.sh tessera
stop.sh
```
### Next steps: Sending transactions
Some simple transaction contracts are included in quorum-examples to demonstrate the privacy features of Quorum. To learn how to use them see the [7nodes](../7Nodes).

View File

@ -0,0 +1,33 @@
# Setup Overview & Quickstart
Using Quorum requires that a Quorum Node and a Constellation/Tessera Node are installed, configured and
running (see build/installation instructions for both below). An overview of the steps to follow to manually set up Quorum, including key generation, genesis block & Constellation/Tessera configuration will be available soon, but for now the best way to get started is to use the Vagrant environment that has been made available for running the [Quorum Examples](../Quorum-Examples). The Vagrant environment automatically sets up a test Quorum network that is ready for development use within minutes and is the recommended approach if you are looking to get started with Quorum. If you don't want to use the Quorum Examples approach and instead would like to manually set up Quorum then please see below (Note: this documentation is Work In Progress)
## Building Quorum Node From Source
Clone the repository and build the source:
```
git clone https://github.com/jpmorganchase/quorum.git
cd quorum
make all
```
Binaries are placed in `$REPO_ROOT/build/bin`. Put that folder in your PATH to make `geth` and `bootnode` easily invokable, or copy those binaries to a folder already in PATH, e.g. `/usr/local/bin`.
An easy way to supplement PATH is to add `PATH=$PATH:/path/to/repository/build/bin` to your `~/.bashrc` or `~/.bash_aliases` file.
Run the tests:
```
make test
```
## Installing Constellation
Grab a package for your platform [here](https://github.com/jpmorganchase/constellation/releases), and place the extracted binaries somewhere in PATH, e.g. /usr/local/bin.
## Installing Tessera
Follow the installation instructions on the [Tessera project page](https://github.com/jpmorganchase/tessera).
## Getting Started from Scratch
Follow the instructions given [here](../Getting-Started-From-Scratch).

View File

@ -0,0 +1,39 @@
``` json
{
"alloc": {
"0xed9d02e382b34818e88b88a309c7fe71e65f419d": {
"balance": "1000000000000000000000000000"
},
"0xca843569e3427144cead5e4d5999a3d0ccf92b8e": {
"balance": "1000000000000000000000000000"
},
"0x0fbdc686b912d7722dc86510934589e0aaf3b55a": {
"balance": "1000000000000000000000000000"
},
"0x9186eb3d20cbd1f5f992a950d808c4495153abd5": {
"balance": "1000000000000000000000000000"
},
"0x0638e1574728b6d862dd5d3a3e0942c3be47d996": {
"balance": "1000000000000000000000000000"
}
},
"coinbase": "0x0000000000000000000000000000000000000000",
"config": {
"homesteadBlock": 0,
"byzantiumBlock": 0,
"chainId": 10,
"eip150Block": 0,
"eip155Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip158Block": 0,
"isQuorum": true
},
"difficulty": "0x0",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000",
"gasLimit": "0xE0000000",
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"nonce": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
```

View File

@ -0,0 +1,11 @@
```
[
"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"
]
```

View File

@ -0,0 +1,215 @@
# Running Quorum
## Developing Smart Contracts
Quorum uses standard [Solidity](https://solidity.readthedocs.io/en/develop/) for writing Smart Contracts, and generally, these can be designed as you would design Smart Contracts for Ethereum. Smart Contracts can either be public (i.e. visible and executable by all participants on a given Quorum network) or private to one or more network participants. Note, however, that Quorum does not introduce new contract Types. Instead, similar to [Transactions](../../Transaction%20Processing/Transaction%20Processing), the concept of public and private contracts is notional only.
### Creating Public Transactions/Contracts
Sending a standard Ethereum-style transaction to a given network will make it viewable and executable by all participants on the network. As with Ethereum, leave the `to` field empty for a contract-creation transaction.
Example JSON RPC API call to send a public transaction:
``` json
{
"jsonrpc":"2.0",
"method":"eth_sendTransaction",
"params":[
{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0", // 30400
"gasPrice": "0x9184e72a000", // 10000000000000
"value": "0x9184e72a", // 2441406250
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}
],
"id":1
}
```
See the [Quorum API](../../api) page for details on the `sendTransaction` call, which includes some modifications to the standard Ethereum call.
!!! info
See the Contract Design Considerations sections below for important points on creating Quorum contracts
### Creating Private Transactions/Contracts
In order to make a transaction/smart contract private and therefore only viewable and executable by a subset of the network, send a standard Ethereum Transaction but include the Quorum-specific `privateFor` parameter. `privateFor` is used to provide the list of participants for the transaction/contract. Each participant is identified by a Privacy Manager public key.
Example JSON RPC API call to send a private transaction:
``` json
{
"jsonrpc":"2.0",
"method":"eth_sendTransaction",
"params":[
{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0", // 30400
"gasPrice": "0x9184e72a000", // 10000000000000
"value": "0x9184e72a", // 2441406250
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675",
"privateFor": ["$PUBKEY1, $PUBKEY2"]
}
],
"id":1
}
```
See the [Quorum API](../../api) page for details on the `sendTransaction` call, which includes some modifications to the standard Ethereum call.
!!! info
See the Contract Design Considerations sections below for important points on creating Quorum contracts
### Quorum Contract Design Considerations
1. *Private contracts cannot update public contracts.* This is because not all participants will be able to execute a private contract, and so if that contract can update a public contract, then each participant will end up with a different state for the public contract.
2. *Once a contract has been made public, it can't later be made private.* If you do need to make a public contract private, it would need to be deleted from the blockchain and a new private contract created.
## Setting up a multi-node network
The [quorum-examples 7nodes](../Quorum-Examples) source files contain several scripts demonstrating how to set up a private test network made up of 7 nodes.
## Permissioned Networks
Node Permissioning is a feature of Quorum that is used to define:
1. The nodes that a particular Quorum node is able to connect to
2. The nodes that a particular Quorum node is able to receive connections from
Permissioning is managed at the individual node level by using the `--permissioned` command line flag when starting the node.
If a node is started with `--permissioned` set, the node will look for a `<data-dir>/permissioned-nodes.json` file. This file contains the list of enodes that this node can connect to and accept connections from. In other words, if permissioning is enabled, only the nodes that are listed in the `permissioned-nodes.json` file become part of the network.
If `--permissioned` is set, a `permissioned-nodes.json` file must be provided. If the flag is set but no nodes are present in this file, then the node will be unable to make any outward connections or accept any incoming connections.
The format of `permissioned-nodes.json` is similar to `static-nodes.json`:
```json
[
"enode://enodehash1@ip1:port1",
"enode://enodehash2@ip2:port2",
"enode://enodehash3@ip3:port3"
]
```
For example, including the hash, a sample file might look like:
```json
[
"enode://6598638ac5b15ee386210156a43f565fa8c48592489d3e66ac774eac759db9eb52866898cf0c5e597a1595d9e60e1a19c84f77df489324e2f3a967207c047470@127.0.0.1:30300"
]
```
In the current release, every node has its own copy of `permissioned-nodes.json`. In a future release, the permissioned nodes list will be moved to a smart contract, thereby keeping the list on-chain and requiring just one global list of nodes that connect to the network.
### Initialize chain
The first step is to generate the genesis block.
The `7nodes` directory in the `quorum-examples` repository contains several keys (using an empty password) that are used in the example genesis file:
```
key1 vote key 1
key2 vote key 2
key3 vote key 3
key4 block maker 1
key5 block maker 2
```
Example genesis file (copy to `genesis.json`):
``` json
"config": {
"homesteadBlock": 0,
"byzantiumBlock": 0,
"chainId": 10,
"eip150Block": 0,
"eip155Block": 0,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip158Block": 0,
"isQuorum": true
},
```
Now we can initialize geth:
```
geth init genesis.json
```
### Setup Bootnode
Optionally you can set up a bootnode that all the other nodes will first connect to in order to find other peers in the network. You will first need to generate a bootnode key:
1. To generate the key for the first time:
``` bash
bootnode -genkey tmp_file.txt // this will start a bootnode with an enode address and generate a key inside a “tmp_file.txt” file`
```
2. To later restart the bootnode using the same key (and hence use the same enode url):
``` bash
bootnode -nodekey tmp_file.txt
```
or
``` bash
bootnode -nodekeyhex 77bd02ffa26e3fb8f324bda24ae588066f1873d95680104de5bc2db9e7b2e510 // Key from tmp_file.txt
```
### Start node
Starting a node is as simple as `geth`. This will start the node without any of the roles and makes the node a spectator. If you have setup a bootnode then be sure to add the `--bootnodes` param to your startup command:
`geth --bootnodes $BOOTNODE_ENODE`
### Adding New Nodes:
Any additions to the `permissioned-nodes.json` file will be dynamically picked up by the server when subsequent incoming/outgoing requests are made. The node does not need to be restarted in order for the changes to take effect.
### Removing existing nodes:
Removing existing connected nodes from the `permissioned-nodes.json` file will not immediately drop those existing connected nodes. However, if the connection is dropped for any reason, and a subsequent connect request is made from the dropped node ids, it will be rejected as part of that new request.
## Quorum API
Please see the [Quorum API](../../api) page for details.
## Network and Chain ID
An Ethereum network is run using a Network ID and, after [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md), a Chain ID.
Before EIP-155, the names "Network ID" and "Chain ID" were used interchangeably, but after this they have separate meanings.
The network ID is a property of a peer, NOT of the chain the peer is managing. A network ID can be passed in via the command line by `--networkid <id>`. It's purpose is to separate peers that are running under a different network ID. Therefore, you cannot sync with anyone who is running a node with a different network ID. However, since it is trivial to change this, it is a less secure version of Quorum's `--permissioned` flag, and it only used for simple segregation.
The chain ID is a property of the chain managed by the node. It is used for replay protection of transactions - prior to EIP-155, a transaction run on one chain could be copied and sent to a different chain by anyone, since the transaction is already signed.
Setting the chain ID has the effect of changing one of the parameters of a transaction, namely the `V` parameter.
As the EIP explains, the `v` parameter is set to `2*ChainID + 35/36`. For the Ethereum Foundation Mainnet, which has a chain ID of `1`, this means that all transactions have a value of either `37` or `38`.
The chain ID set in the genesis configuration file, under the `config` section, and is only used when the block number is above the one set at `eip155Block`. See the [quorum-examples genesis files](../genesis) for an example. It can be changed as many times as needed whilst the chain is below the `eip155Block` number and re-rerunning `geth init` - this will not delete or modify any current sync process or saved blocks!
In Quorum, transactions are considered private if the `v` parameter is set to `37` or `38`, which clashes with networks which have a Chain ID of `1`. For this reason, Quorum will not run using chain ID `1` and will immediately quit if started with such a configuration from version 2.1.0 onwards.
If you are running a version prior to version 2.1.0, EIP-155 signing is not used, thus a chain ID of `1` was allowed; you will need to change this using `geth init` before running an updated version.
## ZSL Proof of Concept
J.P. Morgan and the Zcash team partnered to create a proof of concept (POC) implementation of ZSL for Quorum, which enables the issuance of digital assets using ZSL-enabled public smart contracts (z-contracts). We refer to such digital assets as “z-tokens”. Z-tokens can be shielded from public view and transacted privately. Proof that a shielded transaction has been executed can be presented to a private contract, thereby allowing the private contract to update its state in response to shielded transactions that are executed using public z-contracts.
This combination of Constellation/Tesseras private contracts with ZSLs z-contracts, allows obligations that arise from a private contract, to be settled using shielded transfers of z-tokens, while maintaining full privacy and confidentiality.
For more information, see the [ZSL](../../ZSL) page of this wiki.
## Configurable transaction size:
Quorum allows operators of blockchains to increase maximum transaction size of accepted transactions via the genesis block. The Quorum default is currently increased to `64kb` from Ethereum's default `32kb` transaction size. This is configurable up to `128kb` by adding `txnSizeLimit` to the config section of the genesis file:
``` json
"config": {
"chainId": 10,
"isQuorum":true.
...
"txnSizeLimit": 128
}
```

View File

@ -0,0 +1,60 @@
# Constellation
Constellation is a self-managing, peer-to-peer system in which each
node:
- Hosts a number of NaCl (Curve25519) public/private key pairs.
- Automatically discovers other nodes on the network after
synchronizing with as little as one other host.
- Synchronizes a directory of public keys mapped to recipient hosts
with other nodes on the network.
- Exposes a public API which allows other nodes to send encrypted
bytestrings to your node, and to synchronize, retrieving
information about the nodes that your node knows about.
- Exposes a private API which:
- Allows you to send a bytestring to one or more public keys,
returning a content-addressable identifier. This bytestring is
encrypted transparently and efficiently (at symmetric
encryption speeds) before being transmitted over the wire to
the correct recipient nodes (and only those nodes.) The
identifier is a hash digest of the encrypted payload that
every receipient node receives. Each recipient node also
receives a small blob encrypted for their public key which
contains the Master Key for the encrypted payload.
- Allows you to receive a decrypted bytestring
based on an identifier. Payloads which your node has sent or
received can be decrypted and retrieved in this way.
- Exposes methods for deletion, resynchronization, and other
management functions.
- Supports a number of storage backends including LevelDB,
BerkeleyDB, SQLite, and Directory/Maildir-style file storage
suitable for use with any FUSE adapter, e.g. for AWS S3.
- Uses mutually-authenticated TLS with modern settings and various trust
models including hybrid CA/tofu (default), tofu (think OpenSSH), and
whitelist (only some set of public keys can connect.)
- Supports access controls like an IP whitelist.
Conceptually, one can think of Constellation as an amalgamation of a
distributed key server, PGP encryption (using modern cryptography,)
and Mail Transfer Agents (MTAs.)
Constellation's current primary application is to implement the
"privacy engine" of Quorum, a fork of Ethereum with support for
private transactions that function exactly as described in this
README. Private transactions in Quorum contain only a flag indicating
that they're private and the content-addressable identifier described
here.
Constellation can be run stand-alone as a daemon via
`constellation-node`, or imported as a Haskell library, which allows
you to implement custom storage and encryption logic.

View File

@ -0,0 +1,102 @@
## How Constellation works
Each Constellation node hosts some number of key pairs, and advertises
a publicly accessible FQDN/port for other hosts to connect to.
Nodes can be started with a reference to existing nodes on the network
(with the `othernodes` configuration variable,) or without, in which
case some other node must later be pointed to this node to achieve
synchronization.
When a node starts up, it will reach out to each node in `othernodes`,
and learn about the public keys they host, as well as other nodes in
the network. In short order, the node's public key directory will be
the same as that of all other nodes, and you can start addressing
messages to any of the known public keys.
This is what happens when you use the `send` function of the Private
API to send the bytestring `foo` to the public key
`ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc=`:
1. You send a POST API request to the Private API socket like:
`{"payload": "foo", "from": "mypublickey", to: "ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="}`
2. The local node generates using `/dev/urandom` (or similar):
- A random Master Key (MK) and nonce
- A random recipient nonce
3. The local node encrypts the payload using NaCl `secretbox` using
the random MK and nonce.
4. The local node generates an MK container for each recipient
public key; in this case, simply one container for `ROAZ...`,
using NaCl `box` and the recipient nonce.
NaCl `box` works by deriving a shared key based
on your private key and the recipient's public key. This is known
as elliptic curve key agreement.
Note that the sender public key and recipient public key we
specified above aren't enough to perform the
encryption. Therefore, the node will check to see that it is
actually hosting the private key that corresponds to the given
public key before generating an MK container for each recipient
based on SharedKey(yourprivatekey, recipientpublickey) and the
recipient nonce.
We now have:
- An encrypted payload which is `foo` encrypted with the random
MK and a random nonce. This is the same for all recipients.
- A random recipient nonce that also is the same for all
recipients.
- For each recipient, the MK encrypted with the
shared key of your private key and their public key. This
MK container is unique per recipient, and is only transmitted to
that recipient.
5. For each recipient, the local node looks up the recipient host,
and transmits to it:
- The sender's (your) public key
- The encrypted payload and nonce
- The MK container for that recipient and the recipient nonce
6. The recipient node returns a SHA3-512 hash digest of the
encrypted payload, which represents its storage address.
(Note that it is not possible for the sender to dictate the
storage address. Every node generates it independently by hashing
the encrypted payload.)
7. The local node stores the payload locally, generating the same
hash digest.
8. The API call returns successfully once all nodes have confirmed
receipt and storage of the payload, and returned a hash digest.
Now, through some other mechanism, you'll inform the recipient that
they have a payload waiting for them with the identifier `owqkrokwr`,
and they will make a call to the `receive` method of their Private
API:
1. Make a call to the Private API socket `receive` method:
`{"key": "qrqwrqwr"}`
2. The local node will look in its storage for the key `qrqwrqwr`,
and abort if it isn't found.
3. When found, the node will use the information about the sender as
well as its private key to derive SharedKey(senderpublickey,
yourprivatekey) and decrypt the MK container using NaCl `box`
with the recipient nonce.
4. Using the decrypted MK, the local node will decrypt the encrypted
payload using NaCl `secretbox` using the main nonce.
5. The API call returns the decrypted data.

View File

@ -0,0 +1,41 @@
## Installation
### Prerequisites
1. Install supporting libraries:
- Ubuntu: `apt-get install libdb-dev libleveldb-dev libsodium-dev zlib1g-dev libtinfo-dev`
- Red Hat: `dnf install libdb-devel leveldb-devel libsodium-devel zlib-devel ncurses-devel`
- MacOS: `brew install berkeley-db leveldb libsodium`
### Downloading precompiled binaries
Constellation binaries for most major platforms can be downloaded [here](https://github.com/jpmorganchase/constellation/releases).
### Installation from source
1. First time only: Install Stack:
- Linux: `curl -sSL https://get.haskellstack.org/ | sh`
- MacOS: `brew install haskell-stack`
2. First time only: run `stack setup` to install GHC, the Glasgow
Haskell Compiler
3. Run `stack install`
## Generating keys
1. To generate a key pair "node", run `constellation-node --generatekeys=node`
If you choose to lock the keys with a password, they will be encrypted using
a master key derived from the password using Argon2id. This is designed to be
a very expensive operation to deter password cracking efforts. When
constellation encounters a locked key, it will prompt for a password after
which the decrypted key will live in memory until the process ends.
## Running
1. Run `constellation-node <path to config file>` or specify configuration
variables as command-line options (see `constellation-node --help`)
Please refer to the [Constellation client Go library](../constellation-go)
for an example of how to use Constellation.

View File

@ -0,0 +1,262 @@
``` yaml
#####
## Constellation configuration file example
## ----------------------------------------
## Every option listed here can also be specified on the command line, e.g.
## `constellation-node --url=http://www.foo.com --port 9001 ...`
## (lists are given using comma-separated strings)
## If both command line parameters and a configuration file are given, the
## command line options will take precedence.
##
## The only strictly necessary option is `port`, however it's recommended to
## set at least the following:
##
## --url The URL to advertise to other nodes (reachable by them)
## --port The local port to listen on
## --workdir The folder to put stuff in (default: .)
## --socket IPC socket to create for access to the Private API
## --othernodes "Boot nodes" to connect to to discover the network
## --publickeys Public keys hosted by this node
## --privatekeys Private keys hosted by this node (in corresponding order)
##
## Example usage:
##
## constellation-node --workdir=data --generatekeys=foo
## (To generate a keypair foo in the data directory)
##
## constellation-node --url=https://localhost:9000/ \
## --port=9000 \
## --workdir=data \
## --socket=constellation.ipc \
## --othernodes=https://localhost:9001/ \
## --publickeys=foo.pub \
## --privatekeys=foo.key
##
## constellation-node sample.conf
##
## constellation-node --port=9002 sample.conf
## (This overrides the port value given in sample.conf)
##
## Note on defaults: "Default:" below indicates the value that will be assumed
## if the option is not present either in the configuration file or as a command
## line parameter.
##
## Note about security: In the default configuration, Constellation will
## automatically generate TLS certificates and trust other nodes' certificates
## when they're first encountered (trust-on-first-use). See the documentation
## for tlsservertrust and tlsclienttrust below. To disable TLS entirely, e.g.
## when using Constellation in conjunction with a VPN like WireGuard, set tls to
## off.
#####
## Externally accessible URL for this node's public API (this is what's
## advertised to other nodes on the network, and must be reachable by them.)
url = "http://127.0.0.1:9001/"
## Port to listen on for the public API.
port = 9001
## Directory in which to put and look for other files referenced here.
##
## Default: The current directory
workdir = "data"
## Socket file to use for the private API / IPC. If this is commented out,
## the private API will not be accessible.
##
## Default: Not set
socket = "constellation.ipc"
## Initial (not necessarily complete) list of other nodes in the network.
## Constellation will automatically connect to other nodes not in this list
## that are advertised by the nodes below, thus these can be considered the
## "boot nodes."
##
## Default: []
othernodes = ["http://127.0.0.1:9000/"]
## The set of public keys this node will host.
##
## Default: []
publickeys = ["foo.pub"]
## The corresponding set of private keys. These must correspond to the public
## keys listed above.
##
## Default: []
privatekeys = ["foo.key"]
## Optional comma-separated list of paths to public keys to add as recipients
## for every transaction sent through this node, e.g. for backup purposes.
## These keys must be advertised by some Constellation node on the network, i.e.
## be in a node's publickeys/privatekeys lists.
##
## Default: []
alwayssendto = []
## Optional file containing the passwords needed to unlock the given privatekeys
## (the file should contain one password per line -- add an empty line if any
## one key isn't locked.)
##
## Default: Not set
# passwords = "passwords"
## Storage engine used to save payloads and related information. Options:
## - bdb:path (BerkeleyDB)
## - dir:path (Directory/file storage - can be used with e.g. FUSE-mounted
## file systems.)
## - leveldb:path (LevelDB - experimental)
## - memory (Contents are cleared when Constellation exits)
## - sqlite:path (SQLite - experimental)
##
## Default: "dir:storage"
storage = "dir:storage"
## Verbosity level (each level includes all prior levels)
## - 0: Only fatal errors
## - 1: Warnings
## - 2: Informational messages
## - 3: Debug messages
##
## At the command line this can be specified using -v0, -v1, -v2, -v3, or
## -v (2) and -vv (3).
##
## Default: 1
verbosity = 1
## Optional IP whitelist for the public API. If unspecified/empty,
## connections from all sources will be allowed (but the private API remains
## accessible only via the IPC socket above.) To allow connections from
## localhost when a whitelist is defined, e.g. when running multiple
## Constellation nodes on the same machine, add "127.0.0.1" and "::1" to
## this list.
##
## Default: Not set
# ipwhitelist = ["10.0.0.1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"]
## TLS status. Options:
##
## - strict: All connections to and from this node must use TLS with mutual
## authentication. See the documentation for tlsservertrust and
## tlsclienttrust below.
## - off: Mutually authenticated TLS is not used for in- and outbound
## connections, although unauthenticated connections to HTTPS hosts are
## still possible. This should only be used if another transport security
## mechanism like WireGuard is in place.
##
## Default: "strict"
tls = "strict"
## Path to a file containing the server's TLS certificate in Apache format.
## This is used to identify this node to other nodes in the network when they
## connect to the public API.
##
## This file will be auto-generated if it doesn't exist.
##
## Default: "tls-server-cert.pem"
tlsservercert = "tls-server-cert.pem"
## List of files that constitute the CA trust chain for the server certificate.
## This can be empty for auto-generated/non-PKI-based certificates.
##
## Default: []
tlsserverchain = []
## The private key file for the server TLS certificate.
##
## This file will be auto-generated if it doesn't exist.
##
## Default: "tls-server-key.pem"
tlsserverkey = "tls-server-key.pem"
## TLS trust mode for the server. This decides who's allowed to connect to it.
## Options:
##
## - whitelist: Only nodes that have previously connected to this node and
## been added to the tlsknownclients file below will be allowed to connect.
## This mode will not add any new clients to the tlsknownclients file.
##
## - tofu: (Trust-on-first-use) Only the first node that connects identifying
## as a certain host will be allowed to connect as the same host in the
## future. Note that nodes identifying as other hosts will still be able
## to connect -- switch to whitelist after populating the tlsknownclients
## list to restrict access.
##
## - ca: Only nodes with a valid certificate and chain of trust to one of
## the system root certificates will be allowed to connect. The folder
## containing trusted root certificates can be overriden with the
## SYSTEM_CERTIFICATE_PATH environment variable.
##
## - ca-or-tofu: A combination of ca and tofu: If a certificate is valid,
## it is always allowed and added to the tlsknownclients list. If it is
## self-signed, it will be allowed only if it's the first certificate this
## node has seen for that host.
##
## - insecure-no-validation: Any client can connect, however they will still
## be added to the tlsknownclients file.
##
## Default: "tofu"
tlsservertrust = "tofu"
## TLS known clients file for the server. This contains the fingerprints of
## public keys of other nodes that are allowed to connect to this one.
##
## Default: "tls-known-clients"
tlsknownclients = "tls-known-clients"
## Path to a file containing the client's TLS certificate in Apache format.
## This is used to identify this node to other nodes in the network when it is
## connecting to their public APIs.
##
## This file will be auto-generated if it doesn't exist.
##
## Default: "tls-client-cert.pem"
tlsclientcert = "tls-client-cert.pem"
## List of files that constitute the CA trust chain for the client certificate.
## This can be empty for auto-generated/non-PKI-based certificates.
##
## Default: []
tlsclientchain = []
## The private key file for the client TLS certificate.
##
## This file will be auto-generated if it doesn't exist.
##
## Default: "tls-client-key.pem"
tlsclientkey = "tls-client-key.pem"
## TLS trust mode for the client. This decides which servers it will connect to.
## Options:
##
## - whitelist: This node will only connect to servers it has previously seen
## and added to the tlsknownclients file below. This mode will not add
## any new servers to the tlsknownservers file.
##
## - tofu: (Trust-on-first-use) This node will only connect to the same
## server for any given host. (Similar to how OpenSSH works.)
##
## - ca: The node will only connect to servers with a valid certificate and
## chain of trust to one of the system root certificates. The folder
## containing trusted root certificates can be overriden with the
## SYSTEM_CERTIFICATE_PATH environment variable.
##
## - ca-or-tofu: A combination of ca and tofu: If a certificate is valid,
## it is always allowed and added to the tlsknownservers list. If it is
## self-signed, it will be allowed only if it's the first certificate this
## node has seen for that host.
##
## - insecure-no-validation: This node will connect to any server, regardless
## of certificate, however it will still be added to the tlsknownservers
## file.
##
## Default: "ca-or-tofu"
tlsclienttrust = "ca-or-tofu"
## TLS known servers file for the client. This contains the fingerprints of
## public keys of other nodes that this node has encountered.
##
## Default: "tls-known-servers"
tlsknownservers = "tls-known-servers"
```

View File

@ -0,0 +1,160 @@
```go
package constellation
import (
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"github.com/tv42/httpunix"
"io"
"io/ioutil"
"net/http"
"os"
"os/exec"
"strings"
"time"
)
func launchNode(cfgPath string) (*exec.Cmd, error) {
cmd := exec.Command("constellation-node", cfgPath)
stderr, err := cmd.StderrPipe()
if err != nil {
return nil, err
}
go io.Copy(os.Stderr, stderr)
if err := cmd.Start(); err != nil {
return nil, err
}
time.Sleep(100 * time.Millisecond)
return cmd, nil
}
func unixTransport(socketPath string) *httpunix.Transport {
t := &httpunix.Transport{
DialTimeout: 1 * time.Second,
RequestTimeout: 5 * time.Second,
ResponseHeaderTimeout: 5 * time.Second,
}
t.RegisterLocation("c", socketPath)
return t
}
func unixClient(socketPath string) *http.Client {
return &http.Client{
Transport: unixTransport(socketPath),
}
}
func RunNode(socketPath string) error {
c := unixClient(socketPath)
res, err := c.Get("http+unix://c/upcheck")
if err != nil {
return err
}
if res.StatusCode == 200 {
return nil
}
return errors.New("Constellation Node API did not respond to upcheck request")
}
type Client struct {
httpClient *http.Client
}
func (c *Client) doJson(path string, apiReq interface{}) (*http.Response, error) {
buf := new(bytes.Buffer)
err := json.NewEncoder(buf).Encode(apiReq)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", "http+unix://c/"+path, buf)
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
res, err := c.httpClient.Do(req)
if err == nil && res.StatusCode != 200 {
return nil, fmt.Errorf("Non-200 status code: %+v", res)
}
return res, err
}
func (c *Client) SendPayload(pl []byte, b64From string, b64To []string) ([]byte, error) {
buf := bytes.NewBuffer(pl)
req, err := http.NewRequest("POST", "http+unix://c/sendraw", buf)
if err != nil {
return nil, err
}
if b64From != "" {
req.Header.Set("c11n-from", b64From)
}
req.Header.Set("c11n-to", strings.Join(b64To, ","))
req.Header.Set("Content-Type", "application/octet-stream")
res, err := c.httpClient.Do(req)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("Non-200 status code: %+v", res)
}
return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body))
}
func (c *Client) SendSignedPayload(signedPayload []byte, b64To []string) ([]byte, error) {
buf := bytes.NewBuffer(signedPayload)
req, err := http.NewRequest("POST", "http+unix://c/sendsignedtx", buf)
if err != nil {
return nil, err
}
req.Header.Set("c11n-to", strings.Join(b64To, ","))
req.Header.Set("Content-Type", "application/octet-stream")
res, err := c.httpClient.Do(req)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("Non-200 status code: %+v", res)
}
return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body))
}
func (c *Client) ReceivePayload(key []byte) ([]byte, error) {
req, err := http.NewRequest("GET", "http+unix://c/receiveraw", nil)
if err != nil {
return nil, err
}
req.Header.Set("c11n-key", base64.StdEncoding.EncodeToString(key))
res, err := c.httpClient.Do(req)
if res != nil {
defer res.Body.Close()
}
if err != nil {
return nil, err
}
if res.StatusCode != 200 {
return nil, fmt.Errorf("Non-200 status code: %+v", res)
}
return ioutil.ReadAll(res.Body)
}
func NewClient(socketPath string) (*Client, error) {
return &Client{
httpClient: unixClient(socketPath),
}, nil
}
```

View File

@ -0,0 +1,183 @@
# Configuration file
A `.json` file including required configuration details must be provided using the `-configfile` command-line property when starting Tessera.
Many configuration options can be overridden using the command-line. See the [Using CLI to override config](../Using%20CLI%20to%20override%20config) page for more information.
## Configuration options
The configuration options are explained in more detail in this section. Configuration options that require more than a brief explanation are covered in separate pages.
### Cryptographic Keys
See [Keys page](../Keys).
### Whitelist
If set to true, the `peers` list will be used as the whitelisted urls for the Tessera node:
```
"useWhiteList": true,
```
---
### Database
Tessera's database uses JDBC to connect to an external database. Any valid JDBC URL may be specified, refer to your providers details to construct a valid JDBC URL.
```
"jdbc": {
"url": "[JDBC URL]",
"username": "[JDBC Username]",
"password": "[JDBC Password]"
}
```
---
### Server
> **For Tessera versions prior to 0.8:** See [Legacy Server Settings](../Legacy%20server%20settings).
To allow for a greater level of control, Tessera's API has been separated into distinct groups. Each group is only accessible over a specific server type. Tessera can be started with different combinations of these servers depending on the functionality required. This is defined in the configuration and determines the APIs that are available and how they are accessed.
The possible server types are:
- `P2P` - Tessera uses this server to communicate with other Transaction Managers (the URI for this server can be shared with other nodes to be used in their `peer` list - see below)
- `Q2T` - This server is used for communications between Tessera and its corresponding Quorum node
- `ENCLAVE` - If using a remote enclave, this defines the connection details for the remote enclave server (see the [Enclave docs](../../Tessera%20Services/Enclave#types-of-enclave) for more info)
- `ThirdParty` - This server is used to expose certain Transaction Manager functionality to external services such as Quorum.js
- `ADMIN` - This server is used for configuration management. It is intended for use by the administrator of the Tessera node and is not recommended to be advertised publicly
The servers to be started are provided as a list:
```
"serverConfigs": [
...<server settings>...
]
```
Each server is individually configurable and can advertise over HTTP, HTTPS or a Unix Socket. The format of an individual server config is slightly different between Tessera v0.9 and v0.8:
#### Server configuration (v0.9)
HTTP:
```
{
"app": "<app type>",
"enabled": <boolean>,
"serverUri":"http://[host]:[port]/[path]
"communicationType" : <enum>, // "REST" or "GRPC"
}
```
HTTPS:
```
{
"app": "<app type>",
"enabled": <boolean>,
"serverUri":"https://[host]:[port]/[path]
"communicationType" : <enum>, // "REST" or "GRPC"
"sslConfig": {
...<SSL settings, see below>...
}
}
```
Unix Socket:
```
{
"app": "<app type>",
"enabled": <boolean>,
"serverUri":"unix://[path],
"communicationType" : "REST"
}
```
#### Server configuration (v0.8)
HTTP:
```
{
"app": "<app type>",
"enabled": <boolean>,
"serverSocket":{
"type": "INET",
"port": <int>, //The port to advertise and bind on (if binding address not set)
"hostName": <string> // The hostname to advertise and bind on (if binding address not set)
},
"communicationType" : <enum>, // "REST" or "GRPC"
"bindingAddress": <string> //An address to bind the server to that overrides the one defined above
}
```
HTTPS:
```
{
"app": "<app type>",
"enabled": <boolean>,
"serverSocket":{
"type": "INET",
"port": <int>, //The port to advertise and bind on (if binding address not set)
"hostName": <string> // The hostname to advertise and bind on (if binding address not set)
},
"communicationType" : <enum>, // "REST" or "GRPC"
"bindingAddress": <string>, //An address to bind the server to that overrides the one defined above
"sslConfig": {
...<SSL settings, see below>...
}
}
```
Unix Socket:
```
{
"app": "<app type>",
"enabled": <boolean>,
"serverSocket":{
"type":"UNIX",
"path": <string> //the path of the unix socket to create
},
"communicationType" : "UNIX_SOCKET"
}
```
### TLS/SSL: server sub-config
See [TLS/SSL](../TLS) page.
### InfluxDB Config: server sub-config
Configuration details to allow Tessera to record monitoring data to a running InfluxDB instance.
```
"influxConfig": {
"hostName": "[Hostname of Influx instance]",
"port": "[Port of Influx instance]",
"pushIntervalInSecs": "[How often to push data to InfluxDB]",
"dbName": "[Name of InfluxDB]"
}
```
---
### Peers
A list of URLs used by Tessera to communicate with other nodes. Peer info is shared between nodes during runtime (however, please note the section on `Peer Discovery` below).
```
"peer": [
{
"url": "http://myhost.com:9000"
},
{
"url": "http://myhost.com:9001"
},
{
"url": "http://myhost.com:9002"
}
]
```
### Disabling peer discovery
If peer discovery is disabled, then **only** peers defined in the configuration file will be communicated with; any peers notified by other nodes will be ignored. This allows nodes to be 'locked down' if desired.
```
"disablePeerDiscovery": true
```
---
### Always-send-to
It is possible to configure a node that will be sent a copy of every transaction, even if it is not specified as a party to the transaction. This could be used, for example, to send a copy of every transaction to a node for audit purposes. Specify the public keys to forward transactions onto, and these will be included as if you had specified them on the `privateFor` field to start with.
```
"alwaysSendTo":["<public key 1>", "<public key 2>"]
```
---

View File

@ -0,0 +1,185 @@
Tessera uses cryptographic keys to provide transaction privacy.
You can use existing private/public key pairs as well as use Tessera to generate new key pairs for you. See [Generating & securing keys](../../Tessera%20Services/Keys/Keys) for more info.
```
"keys": {
"passwords": [],
"passwordFile": "Path",
"azureKeyVaultConfig": {
"url": "Url"
},
"hashicorpKeyVaultConfig": {
"url": "Url",
"approlePath": "String",
"tlsKeyStorePath": "Path",
"tlsTrustStorePath": "Path"
},
"keyData": [
{
//The data for a private/public key pair
}
]
}
```
## KeyData
Key pairs can be provided in several ways:
#### 1. Direct key pairs
Direct key pairs are convenient but are the least secure configuration option available, as you expose your private key in the configuration file. More secure options are available and preferable for production environments.
The key pair data is provided in plain text in the configfile:
```
"keys": {
"keyData": [
{
"privateKey": "yAWAJjwPqUtNVlqGjSrBmr1/iIkghuOh1803Yzx9jLM=",
"publicKey": "/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="
}
]
}
```
#### 2. Inline key pairs
The public key is provided in plain text. The private key is provided through additional config:
```
"keys": {
"keyData": [
{
"config": {
"data": {
"bytes": "yAWAJjwPqUtNVlqGjSrBmr1/iIkghuOh1803Yzx9jLM="
},
"type": "unlocked"
},
"publicKey": "/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="
}
]
}
```
This allows for the use of Argon2 password-secured private keys by including the corresponding Argon2 settings in the additional config:
```
"keys": {
"passwords": ["password"],
"keyData": [
{
"config": {
"data": {
"aopts": {
"variant": "id",
"memory": 1048576,
"iterations": 10,
"parallelism": 4,
},
"snonce": "x3HUNXH6LQldKtEv3q0h0hR4S12Ur9pC",
"asalt": "7Sem2tc6fjEfW3yYUDN/kSslKEW0e1zqKnBCWbZu2Zw=",
"sbox": "d0CmRus0rP0bdc7P7d/wnOyEW14pwFJmcLbdu2W3HmDNRWVJtoNpHrauA/Sr5Vxc"
},
"type": "argon2sbox"
},
"publicKey": "/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="
}
]
}
```
#### 3. Azure Key Vault key pairs
The keys in the pair are stored as secrets in an Azure Key Vault. This requires providing the vault url and the secret IDs for both keys:
```
"keys": {
"azureKeyVaultConfig": {
"url": "https://my-vault.vault.azure.net"
},
"keyData": [
{
"azureVaultPrivateKeyId": "Key",
"azureVaultPublicKeyId": "Pub",
"azureVaultPublicKeyVersion": "bvfw05z4cbu11ra2g94e43v9xxewqdq7",
"azureVaultPrivateKeyVersion": "0my1ora2dciijx5jq9gv07sauzs5wjo2"
}
]
}
```
This example configuration will retrieve the specified versions of the secrets `Key` and `Pub` from the key vault with DNS name `https://my-vault.vault.azure.net`. If no version is specified then the latest version of the secret is retrieved.
> Environment variables must be set if using an Azure Key Vault, for more information see [Setting up an Azure Key Vault](../../Tessera%20Services/Keys/Setting%20up%20an%20Azure%20Key%20Vault)
#### 4. Hashicorp Vault key pairs
The keys in the pair are stored as a secret in a Hashicorp Vault. Additional configuration can also be provided if the Vault is configured to use TLS and if the AppRole auth method is being used at a different path to the default (`approle`):
```
"hashicorpKeyVaultConfig": {
"url": "https://localhost:8200",
"tlsKeyStorePath": "/path/to/keystore.jks",
"tlsTrustStorePath": "/path/to/truststore.jks",
"approlePath": "not-default",
},
"keyData": [
{
"hashicorpVaultSecretEngineName": "engine",
"hashicorpVaultSecretName": "secret",
"hashicorpVaultSecretVersion": 1,
"hashicorpVaultPrivateKeyId": "privateKey",
"hashicorpVaultPublicKeyId": "publicKey",
}
]
```
This example configuration will retrieve version 1 of the secret `engine/secret` from Vault and its corresponding values for `privateKey` and `publicKey`.
If no `hashicorpVaultSecretVersion` is provided then the latest version for the secret will be retrieved by default.
Tessera requires TLS certificates and keys to be stored in `.jks` Java keystore format. If the `.jks` files are password protected then the following environment variables must be set:
* `HASHICORP_CLIENT_KEYSTORE_PWD`
* `HASHICORP_CLIENT_TRUSTSTORE_PWD`
> If using a Hashicorp Vault additional environment variables must be set and a version 2 K/V secret engine must be enabled. For more information see [Setting up a Hashicorp Vault](../../Tessera%20Services/Keys/Setting%20up%20a%20Hashicorp%20Vault).
#### 5. Filesystem key pairs
The keys in the pair are stored in files:
```
"keys": {
"passwordFile": "/path/to/passwords",
"keyData": [
{
"privateKeyPath": "/path/to/privateKey.key",
"publicKeyPath": "/path/to/publicKey.pub"
}
]
}
```
The contents of the public key file must contain the public key only, e.g.:
```
/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc=
```
The contents of the private key file must contain the private key in the config format, e.g.:
```
{
"type" : "unlocked",
"data" : {
"bytes" : "DK0HDgMWJKtZVaP31mPhk6TJNACfVzz7VZv2PsQZeKM="
}
}
```
## Multiple Keys
If wished, multiple key pairs can be specified for a Tessera node. In this case, any one of the public keys can be used to address a private transaction to that node. Tessera will sequentially try each key to find one that can decrypt the payload. This can be used, for example, to simplify key rotation.
Note that multiple key pairs can only be set up within the configuration file, not via separate filesystem key files.
## Viewing the keys registered for a node
An ADMIN API endpoint `/config/keypairs` exists to allow you to view the public keys of the key pairs currently in use by your Tessera node. This requires configuring an ADMIN server in the node's configuration file, as described in [Configuration Overview](../Configuration%20Overview).
A sample response for the request `adminhost:port/config/keypairs` is:
```json
[
{
"publicKey" : "oNspPPgszVUFw0qmGFfWwh1uxVUXgvBxleXORHj07g8="
},
{
"publicKey" : "ABn6zhBth2qpdrJXp98IvjExV212ALl3j4U//nj4FAI="
}
]
```

View File

@ -0,0 +1,50 @@
**Important**
Legacy server settings were part of Tessera v0.5, v0.6 and v0.7. They have deprecated for, but still work with, v0.8 and v0.9 and may be removed for any future versions.
---
The server settings are defined as following:
```
"server": {
"hostName": "<Hostname to advertise. Includes url scheme but not port e.g. http://myhost.com>",
"port": "<Port that will be used to expose Tessera services>",
"bindingAddress": "<Full interface to bind. Includes URL scheme and port. e.g. http://0.0.0.0:9999>",
"sslConfig": <...>,
"influxConfig": <...>
}
```
<br>
<br>
If the address to advertise keys on is the same as the address you wish to bind to, then the `bindingAddress` may be omitted, for example:
```
"server": {
"hostName": "http://myhost.com",
"port": 9999,
"sslConfig": <...>,
"influxConfig": <...>
}
```
---
**InfluxDB Config**
Configuration details to allow Tessera to record monitoring data to a running InfluxDB instance.
```
"influxConfig": {
"hostName": "[Hostname of Influx instance]",
"port": "[Port of Influx instance]",
"pushIntervalInSecs": "[How often to push data to InfluxDB]",
"dbName": "[Name of InfluxDB]"
}
```
### Unix socket file
Path to the Unix domain socket file used to communicate between Quorum and Tessera.
```
"unixSocketFile" : "/path/to/socketfile.ipc"
```

View File

@ -0,0 +1,9 @@
Tessera configuration varies by version as new features are added or changed. Below is a list of sample configurations that show a possible structure. There may be more features that are not included in the sample; a full list of features can be found [here](../Configuration%20Overview).
## Samples
| Version |
| ------------- |
| [0.9 - latest release](../Tessera%20v0.9%20sample%20settings) |
| [0.8](../Tessera%20v0.8%20sample%20settings) |
| [0.7.3](../Tessera%20v0.7.3%20sample%20settings) |

View File

@ -0,0 +1,168 @@
### Usage
Communications via TLS/SSL can be enabled by setting `"tls": "STRICT"`.
If the value is set to `"OFF"`, the rest of the SSL configuration will not be considered.
!!! warning
If using TLS make sure to update the hostname of the node to use `https` instead of `http`
```json
{
"sslConfig": {
"tls": "[Authentication mode : OFF,STRICT]",
"serverTrustMode": "[Possible values: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE]",
"serverKeyStore": "[Path to server keystore]",
"serverKeyStorePassword": "[Password required for server KeyStore]",
"serverTrustStore": "[Server trust store path]",
"serverTrustStorePassword": "[Password required for server trust store]",
"serverTlsKeyPath": "[Path to server TLS key path]",
"serverTlsCertificatePath": "[Path to server TLS cert path]",
"serverTrustCertificates": [
"[Array of truststore certificates if no truststore is defined.]"
],
"clientTrustMode": "[Possible values: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE]",
"clientKeyStore": "[Path to client keystore. The keystore that is used when communicating to other nodes.]",
"clientKeyStorePassword": "[Password required for client KeyStore]",
"clientTrustStore": "[Path to client TrustStore]",
"clientTrustStorePassword": "[Password required for client trust store]",
"clientTlsKeyPath": "[Path to client TLS Key]",
"clientTlsCertificatePath": "[Path to client TLS cert]",
"clientTrustCertificates": [
"[Array of truststore certificates if no truststore is defined.]"
],
"knownClientsFile": "[TLS known clients file for the server. This contains the fingerprints of public keys of other nodes that are allowed to connect to this one.]",
"knownServersFile": "[TLS known servers file for the client. This contains the fingerprints of public keys of other nodes that this node has encountered.]",
"generateKeyStoreIfNotExisted": "[boolean]",
"environmentVariablePrefix": "[Prefix to uniquely identify environment variables for this particular server ssl config]"
}
}
```
When SSL is enabled, each node will need to have certificates and keys defined for both client-side and server-side. These can be defined in multiple ways:
1. Secured & unsecured `.jks` (Java keystore) format files
* `serverKeyStore`, `serverKeyStorePassword`, `serverTrustStore`, `serverTrustStorePassword`
* `clientKeyStore`, `clientKeyStorePassword`, `clientTrustStore`, `clientTrustStorePassword`
2. `.pem` format certificate and key files
* `serverTlsKeyPath`, `serverTlsCertificatePath`, `serverTrustCertificates`
* `clientTlsKeyPath`, `clientTlsCertificatePath`, `clientTrustCertificates`
`.jks` files take precedence over `.pem` files if both are provided for client-side or server-side.
#### Keystores
##### Passwords
Passwords for secured `.jks` keystores can be provided in multiple ways, and in the following order of precedence:
1. *Prefixed* environment variables
* `<PREFIX>_TESSERA_SERVER_KEYSTORE_PWD`, `<PREFIX>_TESSERA_SERVER_TRUSTSTORE_PWD`
* `<PREFIX>_TESSERA_CLIENT_KEYSTORE_PWD`, `<PREFIX>_TESSERA_CLIENT_TRUSTSTORE_PWD`
2. Config file
* `serverKeyStorePassword`, `serverTrustStorePassword`
* `clientKeyStorePassword`, `clientTrustStorePassword`
3. *Global* environment variables
* `TESSERA_SERVER_KEYSTORE_PWD`, `TESSERA_SERVER_TRUSTSTORE_PWD`
* `TESSERA_CLIENT_KEYSTORE_PWD`, `TESSERA_CLIENT_TRUSTSTORE_PWD`
The *global* environment variables, if set, are applied to all server configs defined in the configfile (i.e. if a P2P and ADMIN server are both configured with TLS then the values set for the global environment variables will be used for both). These values are ignored if the passwords are also provided in the configfile or as prefixed environment variables.
The *prefixed* environment variables are only applied to the servers with that `environmentVariablePrefix` value defined in their config. This allows, for example, a P2P and ADMIN server to be configured with different prefixes, `P2P` and `ADMIN`. Different keystores can then be used for each server and the individual passwords provided with `P2P_<...>` and `ADMIN_<...>`.
##### Generating keystores
If keystores do not already exist, Tessera can generate `.jks` (Java keystore) files for use with non-CA Trust Modes (see Trust Modes).
By setting `"generateKeyStoreIfNotExisted": "true"`, Tessera will check whether files already exist at the paths provided in the `serverKeyStore` and `clientKeyStore` config values. If the files do not exist:
1. New keystores will be generated and saved at the `serverKeyStore` and `clientKeyStore` paths
2. The keystores will be secured using the corresponding passwords if they are provided (see Passwords)
#### PEM files
Below is a config sample for using the `.pem` file format:
```json
"sslConfig" : {
"tls" : "STRICT",
"generateKeyStoreIfNotExisted" : "false",
"serverTlsKeyPath" : "server-key.pem",
"serverTlsCertificatePath" : "server-cert.pem",
"serverTrustCertificates" : ["server-trust.pem"]
"serverTrustMode" : "CA",
"clientTlsKeyPath" : "client-key.pem",
"clientTlsCertificatePath" : "client-cert.pem",
"clientTrustCertificates" : ["client-trust.pem"]
"clientTrustMode" : "TOFU",
"knownClientsFile" : "knownClients",
"knownServersFile" : "knownServers"
}
```
#### Trust Modes
The Trust Mode for both client and server must also be specified. Multiple trust modes are supported: `TOFU`, `WHITELIST`, `CA`, `CA_OR_TOFU`, and `NONE`.
* `TOFU` (Trust-on-first-use)
Only the first node that connects identifying as a certain host will be allowed to connect as the same host in the future. When connecting for the first time, the host and its certificate will be added to `knownClientsFile` (for server), or `knownServersFile` (for client). These files will be generated if not already existed, using the values specified in `knownClientsFile` and `knownServersFile`.
A config sample for `TOFU` trust mode is:
```json
"sslConfig" : {
"tls" : "STRICT",
"generateKeyStoreIfNotExisted" : "true",
"serverKeyStore" : "server-keystore",
"serverKeyStorePassword" : "tessera",
"serverTrustMode" : "TOFU",
"clientKeyStore" : "client-keystore",
"clientKeyStorePassword" : "tessera",
"clientTrustMode" : "TOFU",
"knownClientsFile" : "knownClients",
"knownServersFile" : "knownServers"
}
```
* `WHITELIST`
Only nodes that have previously connected to this node and have been added to the `knownClients` file will be allowed to connect. Similarly, this node will only be allowed to make connections to nodes that have been added to the `knownServers` file. This trust mode will not add new entries to the `knownClients` or `knownServers` files.
With this trust mode, the whitelist files (`knownClientsFile` and `knownServersFile`) must be provided.
A config sample for `WHITELIST` trust mode is:
```json
"sslConfig" : {
"tls" : "STRICT",
"generateKeyStoreIfNotExisted" : "true",
"serverKeyStore" : "server-keystore",
"serverKeyStorePassword" : "tessera",
"serverTrustMode" : "WHITELIST",
"clientKeyStore" : "client-keystore",
"clientKeyStorePassword" : "tessera",
"clientTrustMode" : "WHITELIST",
"knownClientsFile" : "knownClients",
"knownServersFile" : "knownServers"
}
```
* `CA`
Only nodes with a valid certificate and chain of trust are allowed to connect. For this trust mode, trust stores must be provided and must contain a list of trust certificates.
A config sample for `CA` trust mode is:
```json
"sslConfig" : {
"tls" : "STRICT",
"generateKeyStoreIfNotExisted" : "false", //You can't generate trust stores when using CA
"serverKeyStore" : "server-keystore",
"serverKeyStorePassword" : "tessera",
"serverTrustStore" : "server-truststore",
"serverTrustStorePassword" : "tessera",
"serverTrustMode" : "CA",
"clientKeyStore" : "client-keystore",
"clientKeyStorePassword" : "tessera",
"clientTrustStore" : "client-truststore",
"clientTrustStorePassword" : "tessera",
"clientTrustMode" : "CA",
"knownClientsFile" : "knownClients",
"knownServersFile" : "knownServers"
}
```

View File

@ -0,0 +1,115 @@
```json
{
"useWhiteList": "boolean",
"jdbc": {
"url": "String",
"username": "String",
"password": "String"
},
"server": {
"hostName": "String - url e.g. http://127.0.0.1",
"port": "int",
"grpcPort": "int",
"bindingAddress": "String - url with port e.g. http://127.0.0.1:9001",
"communicationType": "enum REST,GRPC",
"sslConfig": {
"tls": "enum STRICT,OFF",
"generateKeyStoreIfNotExisted": "boolean",
"serverKeyStore": "Path",
"serverTlsKeyPath": "Path",
"serverTlsCertificatePath": "Path",
"serverKeyStorePassword": "String",
"serverTrustStore": "Path",
"serverTrustCertificates": [
"Path..."
],
"serverTrustStorePassword": "String",
"serverTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"clientKeyStore": "Path",
"clientTlsKeyPath": "Path",
"clientTlsCertificatePath": "Path",
"clientKeyStorePassword": "String",
"clientTrustStore": "Path",
"clientTrustCertificates": [
"Path..."
],
"clientTrustStorePassword": "String",
"clientTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"knownClientsFile": "Path",
"knownServersFile": "Path"
},
"influxConfig": {
"hostName": "String - url e.g. http://hostname",
"port": "int",
"pushIntervalInSecs": "int",
"dbName": "String"
}
},
"peer": [
{
"url": "String - url e.g. http://127.0.0.1:9000/"
}
],
"keys": {
"passwords": [
"String..."
],
"passwordFile": "Path",
"azureKeyVaultConfig": {
"url": "Azure Key Vault url"
},
"hashicorpKeyVaultConfig": {
"url": "Hashicorp Vault url",
"approlePath": "String (defaults to 'approle' if not set)",
"tlsKeyStorePath": "Path to jks key store",
"tlsTrustStorePath": "Path to jks trust store"
},
"keyData": [
{
"config": {
"data": {
"aopts": {
"variant": "Enum : id,d or i",
"memory": "int",
"iterations": "int",
"parallelism": "int"
},
"bytes": "String",
"snonce": "String",
"asalt": "String",
"sbox": "String",
"password": "String"
},
"type": "Enum: argon2sbox or unlocked. If unlocked is defined then config data is required. "
},
"privateKey": "String",
"privateKeyPath": "Path",
"azureVaultPrivateKeyId": "String",
"azureVaultPrivateKeyVersion": "String",
"publicKey": "String",
"publicKeyPath": "Path",
"azureVaultPublicKeyId": "String",
"azureVaultPublicKeyVersion": "String",
"hashicorpVaultSecretEngineName": "String",
"hashicorpVaultSecretName": "String",
"hashicorpVaultSecretVersion": "Integer (defaults to 0 (latest) if not set)",
"hashicorpVaultPrivateKeyId": "String",
"hashicorpVaultPublicKeyId": "String"
}
]
},
"alwaysSendTo": [
"String..."
],
"unixSocketFile": "Path"
}
```

View File

@ -0,0 +1,142 @@
**Changes:**
- added modular server configurations
---
**Sample**
```json
{
"useWhiteList": "boolean",
"jdbc": {
"url": "String",
"username": "String",
"password": "String"
},
"serverConfigs": [
{
"app": "ThirdParty",
"enabled": true,
"serverSocket": {
"type": "INET",
"port": 9081,
"hostName": "http://localhost"
},
"bindingAddress": "String - url with port e.g. http://127.0.0.1:9081",
"communicationType": "REST"
},
{
"app": "Q2T",
"enabled": true,
"serverSocket": {
"type": "UNIX",
"path": "/tmp/tm.ipc"
},
"communicationType": "UNIX_SOCKET"
},
{
"app": "P2P",
"enabled": true,
"serverSocket": {
"type": "INET",
"port": 9001,
"hostName": "http://localhost"
},
"bindingAddress": "String - url with port e.g. http://127.0.0.1:9001",
"sslConfig": {
"tls": "enum STRICT,OFF",
"generateKeyStoreIfNotExisted": "boolean",
"serverKeyStore": "Path",
"serverTlsKeyPath": "Path",
"serverTlsCertificatePath": "Path",
"serverKeyStorePassword": "String",
"serverTrustStore": "Path",
"serverTrustCertificates": [
"Path..."
],
"serverTrustStorePassword": "String",
"serverTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"clientKeyStore": "Path",
"clientTlsKeyPath": "Path",
"clientTlsCertificatePath": "Path",
"clientKeyStorePassword": "String",
"clientTrustStore": "Path",
"clientTrustCertificates": [
"Path..."
],
"clientTrustStorePassword": "String",
"clientTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"knownClientsFile": "Path",
"knownServersFile": "Path"
},
"communicationType": "REST"
}
],
"peer": [
{
"url": "url e.g. http://127.0.0.1:9000/"
}
],
"keys": {
"passwords": [
"String..."
],
"passwordFile": "Path",
"azureKeyVaultConfig": {
"url": "Azure Key Vault url"
},
"hashicorpKeyVaultConfig": {
"url": "Hashicorp Vault url",
"approlePath": "String (defaults to 'approle' if not set)",
"tlsKeyStorePath": "Path to jks key store",
"tlsTrustStorePath": "Path to jks trust store"
},
"keyData": [
{
"config": {
"data": {
"aopts": {
"variant": "Enum : id,d or i",
"memory": "int",
"iterations": "int",
"parallelism": "int"
},
"bytes": "String",
"snonce": "String",
"asalt": "String",
"sbox": "String",
"password": "String"
},
"type": "Enum: argon2sbox or unlocked. If unlocked is defined then config data is required. "
},
"privateKey": "String",
"privateKeyPath": "Path",
"azureVaultPrivateKeyId": "String",
"azureVaultPrivateKeyVersion": "String",
"publicKey": "String",
"publicKeyPath": "Path",
"azureVaultPublicKeyId": "String",
"azureVaultPublicKeyVersion": "String",
"hashicorpVaultSecretEngineName": "String",
"hashicorpVaultSecretName": "String",
"hashicorpVaultSecretVersion": "Integer (defaults to 0 (latest) if not set)",
"hashicorpVaultPrivateKeyId": "String",
"hashicorpVaultPublicKeyId": "String"
}
]
},
"alwaysSendTo": [
"String..."
],
"unixSocketFile": "Path"
}
```

View File

@ -0,0 +1,249 @@
**Changes:**
- collapsed server socket definitions into a single property `serverAddress`
e.g.
```json
"serverSocket": {
"type":"INET",
"port": 9001,
"hostName": "http://localhost"
},
```
becomes
```
"serverAddress": "http://localhost:9001",
```
---
**Sample**
```json
{
"useWhiteList": "boolean",
"jdbc": {
"url": "String",
"username": "String",
"password": "String"
},
"serverConfigs": [
{
"app": "ENCLAVE", // Defines us using a remote enclave, leave out if using built-in enclave
"enabled": true,
"serverAddress": "http://localhost:9081", //Where to find the remote enclave
"communicationType": "REST"
},
{
"app": "ThirdParty",
"enabled": true,
"serverAddress": "http://localhost:9081",
"bindingAddress": "String - url with port e.g. http://127.0.0.1:9081",
"communicationType": "REST"
},
{
"app": "Q2T",
"enabled": true,
"serverAddress": "unix:/tmp/tm.ipc",
"communicationType": "REST"
},
{
"app": "P2P",
"enabled": true,
"serverAddress": "http://localhost:9001",
"bindingAddress": "String - url with port e.g. http://127.0.0.1:9001",
"sslConfig": {
"tls": "enum STRICT,OFF",
"generateKeyStoreIfNotExisted": "boolean",
"serverKeyStore": "Path",
"serverTlsKeyPath": "Path",
"serverTlsCertificatePath": "Path",
"serverKeyStorePassword": "String",
"serverTrustStore": "Path",
"serverTrustCertificates": [
"Path..."
],
"serverTrustStorePassword": "String",
"serverTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"clientKeyStore": "Path",
"clientTlsKeyPath": "Path",
"clientTlsCertificatePath": "Path",
"clientKeyStorePassword": "String",
"clientTrustStore": "Path",
"clientTrustCertificates": [
"Path..."
],
"clientTrustStorePassword": "String",
"clientTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"knownClientsFile": "Path",
"knownServersFile": "Path"
},
"communicationType": "REST"
}
],
"peer": [
{
"url": "url e.g. http://127.0.0.1:9000/"
}
],
"keys": {
"passwords": [
"String..."
],
"passwordFile": "Path",
"azureKeyVaultConfig": {
"url": "Azure Key Vault url"
},
"hashicorpKeyVaultConfig": {
"url": "Hashicorp Vault url",
"approlePath": "String (defaults to 'approle' if not set)",
"tlsKeyStorePath": "Path to jks key store",
"tlsTrustStorePath": "Path to jks trust store"
},
"keyData": [
{
"config": {
"data": {
"aopts": {
"variant": "Enum : id,d or i",
"memory": "int",
"iterations": "int",
"parallelism": "int"
},
"bytes": "String",
"snonce": "String",
"asalt": "String",
"sbox": "String",
"password": "String"
},
"type": "Enum: argon2sbox or unlocked. If unlocked is defined then config data is required. "
},
"privateKey": "String",
"privateKeyPath": "Path",
"azureVaultPrivateKeyId": "String",
"azureVaultPrivateKeyVersion": "String",
"publicKey": "String",
"publicKeyPath": "Path",
"azureVaultPublicKeyId": "String",
"azureVaultPublicKeyVersion": "String",
"hashicorpVaultSecretEngineName": "String",
"hashicorpVaultSecretName": "String",
"hashicorpVaultSecretVersion": "Integer (defaults to 0 (latest) if not set)",
"hashicorpVaultPrivateKeyId": "String",
"hashicorpVaultPublicKeyId": "String"
}
]
},
"alwaysSendTo": [
"String..."
],
"unixSocketFile": "Path"
}
```
---
**Sample enclave settings**
```json
{
"serverConfigs": [
{
"app": "ENCLAVE",
"enabled": true,
"serverAddress": "http://localhost:9001",
"bindingAddress": "String - url with port e.g. http://127.0.0.1:9001",
"sslConfig": {
"tls": "enum STRICT,OFF",
"generateKeyStoreIfNotExisted": "boolean",
"serverKeyStore": "Path",
"serverTlsKeyPath": "Path",
"serverTlsCertificatePath": "Path",
"serverKeyStorePassword": "String",
"serverTrustStore": "Path",
"serverTrustCertificates": [
"Path..."
],
"serverTrustStorePassword": "String",
"serverTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"clientKeyStore": "Path",
"clientTlsKeyPath": "Path",
"clientTlsCertificatePath": "Path",
"clientKeyStorePassword": "String",
"clientTrustStore": "Path",
"clientTrustCertificates": [
"Path..."
],
"clientTrustStorePassword": "String",
"clientTrustMode": "Enumeration: CA, TOFU, WHITELIST, CA_OR_TOFU, NONE",
"knownClientsFile": "Path",
"knownServersFile": "Path"
},
"communicationType": "REST"
}
],
"keys": {
"passwords": [
"String..."
],
"passwordFile": "Path",
"azureKeyVaultConfig": {
"url": "Azure Key Vault url"
},
"hashicorpKeyVaultConfig": {
"url": "Hashicorp Vault url",
"approlePath": "String (defaults to 'approle' if not set)",
"tlsKeyStorePath": "Path to jks key store",
"tlsTrustStorePath": "Path to jks trust store"
},
"keyData": [
{
"config": {
"data": {
"aopts": {
"variant": "Enum : id,d or i",
"memory": "int",
"iterations": "int",
"parallelism": "int"
},
"bytes": "String",
"snonce": "String",
"asalt": "String",
"sbox": "String",
"password": "String"
},
"type": "Enum: argon2sbox or unlocked. If unlocked is defined then config data is required. "
},
"privateKey": "String",
"privateKeyPath": "Path",
"azureVaultPrivateKeyId": "String",
"azureVaultPrivateKeyVersion": "String",
"publicKey": "String",
"publicKeyPath": "Path",
"azureVaultPublicKeyId": "String",
"azureVaultPublicKeyVersion": "String",
"hashicorpVaultSecretEngineName": "String",
"hashicorpVaultSecretName": "String",
"hashicorpVaultSecretVersion": "Integer (defaults to 0 (latest) if not set)",
"hashicorpVaultPrivateKeyId": "String",
"hashicorpVaultPublicKeyId": "String"
}
]
},
"alwaysSendTo": [
"String..."
]
}
```

View File

@ -0,0 +1,47 @@
CLI options can be used to add to, or override, configuration defined in a `configfile`.
Standard Tessera CLI options are prefixed with a single hyphen (e.g. `-configfile <PATH>`), whilst the config override options are prefixed with a double hyphen (e.g. `--alwaysSendTo <STRING[]...>`). Use `tessera help` to see a complete list of CLI options.
If a config value is included in both the `configfile` and the CLI, then the CLI value will take precendence. The exceptions to this rule are the `--peer.url <STRING>` and `--alwaysSendTo <STRING[]...>` options. Instead of overriding, these CLI options append to any peer or alwaysSendTo urls in the provided `configfile`. For example, if the following was provided in a `configfile`:
```
{
...
"peer": [
{
"url": "http://localhost:9001"
}
],
alwaysSendTo:[
"giizjhZQM6peq52O7icVFxdTmTYinQSUsvyhXzgZqkE="
],
...
}
```
and Tessera was run with the following overrides:
```
tessera -configfile path/to/file --peer.url http://localhost:9002 --peer.url http://localhost:9003 --alwaysSendTo /+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc= --alwaysSendTo UfNSeSGySeKg11DVNEnqrUtxYRVor4+CvluI8tVv62Y=
```
then Tessera will be started with the following equivalent configuration:
```
{
...
"peer": [
{
"url": "http://localhost:9001"
},
{
"url": "http://localhost:9002"
},
{
"url": "http://localhost:9003"
}
],
alwaysSendTo:[
"giizjhZQM6peq52O7icVFxdTmTYinQSUsvyhXzgZqkE=",
"/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="
"UfNSeSGySeKg11DVNEnqrUtxYRVor4+CvluI8tVv62Y="
],
...
}
```
As demonstrated in this example, in certain cases multiple values can be provided by repeating the CLI option. This is supported for the `peer.url`, `alwaysSendTo`, `server.sslConfig.serverTrustCertificates` and `server.sslConfig.clientTrustCertificates` options.

View File

@ -0,0 +1,28 @@
### Private Transaction Process Flow
Below is a description of how Private Transactions are processed in Quorum:
![Quorum Tessera Privacy Flow](https://github.com/jpmorganchase/tessera/raw/master/Tessera%20Privacy%20flow.jpeg)
In this example, Party A and Party B are party to Transaction AB, whilst Party C is not.
1. Party A sends a Transaction to their Quorum Node, specifying the Transaction payload and setting `privateFor` to be the public keys for Parties A and B
2. Party A's Quorum Node passes the Transaction on to its paired Transaction Manager, requesting for it to store the Transaction payload
3. Party A's Transaction Manager makes a call to its associated Enclave to validate the sender and encrypt the payload
4. Party A's Enclave checks the private key for Party A and, once validated, performs the Transaction conversion. This entails:
1. generating a symmetric key and a random Nonce
1. encrypting the Transaction payload and Nonce with the symmetric key from i.
1. calculating the SHA3-512 hash of the encrypted payload from ii.
1. iterating through the list of Transaction recipients, in this case Parties A and B, and encrypting the symmetric key from i. with the recipient's public key (PGP encryption)
1. returning the encrypted payload from step ii., the hash from step iii. and the encrypted keys (for each recipient) from step iv. to the Transaction Manager
5. Party A's Transaction manager then stores the encrypted payload (encrypted with the symmetric key) and encrypted symmetric key using the hash as the index, and then securely transfers (via HTTPS) the hash, encrypted payload, and encrypted symmetric key that has been encrypted with Party B's public key to Party B's Transaction Manager. Party B's Transaction Manager responds with an Ack/Nack response. Note that if Party A does not receive a response/receives a Nack from Party B then the Transaction will not be propagated to the network. It is a prerequisite for the recipients to store the communicated payload.
6. Once the data transmission to Party B's Transaction Manager has been successful, Party A's Transaction Manager returns the hash to the Quorum Node which then replaces the Transaction's original payload with that hash, and changes the transaction's `V` value to 37 or 38, which will indicate to other nodes that this hash represents a private transaction with an associated encrypted payload as opposed to a public transaction with nonsensical bytecode.
7. The Transaction is then propagated to the rest of the network using the standard Ethereum P2P Protocol.
8. A block containing Transaction AB is created and distributed to each Party on the network.
9. In processing the block, all Parties will attempt to process the Transaction. Each Quorum node will recognise a `V` value of 37 or 38, identifying the Transaction as one whose payload requires decrypting, and make a call to their local Transaction Manager to determine if they hold the Transaction (using the hash as the index to look up).
10. Since Party C does not hold the Transaction, it will receive a `NotARecipient` message and will skip the Transaction - it will not update its Private StateDB. Party A and B will look up the hash in their local Transaction Managers and identify that they do hold the Transaction. Each will then make a call to its Enclave, passing in the Encrypted Payload, Encrypted symmetric key and Signature.
11. The Enclave validates the signature and then decrypts the symmetric key using the Party's private key that is held in The Enclave, decrypts the Transaction Payload using the now-revealed symmetric key and returns the decrypted payload to the Transaction Manager.
12. The Transaction Managers for Parties A and B then send the decrypted payload to the EVM for contract code execution. This execution will update the state in the Quorum Node's Private StateDB only. NOTE: once the code has been executed it is discarded so is never available for reading without going through the above process.

View File

@ -0,0 +1,125 @@
## Migration Utilities
Two utilities are included to help with migrating existing Constellation configurations and datastores for use with Tessera. These utilites are included in the Tessera project and are available for use after building Tessera with Maven.
A full migration workflow would be as follows:
1. Shut down the Constellation/Quorum nodes
2. Perform [database migration](#data-migration)
3. Perform [configuration migration](#configuration-migration)
4. Start Tessera/Quorum nodes
## Data Migration
This utility migrates a Constellation datastore (BerkeleyDB or directory/file storage) to a Tessera compatible one (H2, SQLITE). By default Tessera uses an H2 database, however alternatives can be configured. Refer [DDLs](https://github.com/jpmorganchase/tessera/tree/master/ddls/create-table) for help with defining with other databases.
To make running the utility commands simpler, you can first create an `alias`:
```
alias tessera-data-migration="java -jar /path/to/tessera/data-migration/target/data-migration-${version}-cli.jar"
```
CLI help can be accessed by running:
```
tessera-data-migration help
usage: tessera-data-migration
-exporttype <TYPE> Export DB type i.e. h2, sqlite
-inputpath <PATH> Path to input file or directory
-outputfile <PATH> Path to output file
-storetype <TYPE> Store type i.e. bdb, dir
-dbuser Set a username on the migrated database (only applies to H2)
-dbpass Set a password for the specified user (only applies to H2)
```
#### Migrating BerkeleyDB (bdb)
To migrate a BerkeleyDB (bdb) database for use with Tessera you must first export your existing store using `db_dump`:
```
db_dump -f exported.txt c1/cn§.db/payload.db
```
Then run the following command to perform the migration:
```
tessera-data-migration -storetype bdb -inputpath exported.txt -dbuser <username> -dbpass <password> -outputfile <PATH> -exporttype <TYPE>
```
#### Migrating Directory/File (dir) storage
For dir storage:
```
tessera-data-migration -storetype dir -inputpath /path/to/dir -dbuser <username> -dbpass <password> -outputfile <PATH> -exporttype <TYPE>
```
### Output types
To use H2 as the output storage, specify:
```
-exporttype h2 -outputfile /path/to/h2database
```
To use SQLite as the output storage, specify:
```
-exporttype sqlite -outputfile /path/to/sqlitedb
```
#### Database usernames and passwords
If you want to set a username and password on the migrated database, you must specify this using the following options:
```
-dbuser <username> -dbpass <password>
```
If you do not wish to set a username and password on the migrated database, you must explicitly say so by specifying the arguments without parameters, i.e.
```
-dbuser -dbpass
```
Note also that even though SQLite does not have the concept of usernames and passwords, you must still specify at least the empty configuration.
#### After migration
The output file should then be placed in a location of your choosing that corresponds to the location specified in the configuration file (without any file extension), i.e.
```
"jdbc": {
"url": "jdbc:h2:./c1/migratedfile;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0"
}
```
Note: the migrated database is migrated without user credentials, so if using the file directly then the username and password should not be specified in the configuration.
The Constellation files are no longer used, and can be cleaned up or left alone.
## Configuration Migration
This utility will generate a Tessera compatible `.json` format configuration file from an existing Constellation `.toml` configuration file. The `.json` file will be saved locally to be used when running Tessera. Individual configuration parameters can be overridden during the migration process if required.
To make running the utility commands simpler, you can first create an `alias`:
```
alias tessera-config-migration="java -jar /path/to/tessera/config-migration/target/config-migration-${version}-cli.jar"
```
Most of the Constellation configuration command line parameters are supported. For details of the Constellation configuration see the [Constellation documentation](../../Constellation/Constellation).
To see the CLI help which provides details on overriding specific configuration items from a `.toml` file, run:
```
tessera-config-migration help
```
To migrate a `.toml` file to `.json` with no overrides, run:
```
tessera-config-migration --tomlfile="/path/to/constellation-config.toml"
```
By default, the generated `.json` config will be printed to the console and saved to `./tessera-config.json`. To save to another location/with a different filename use the `--outputfile <PATH>` CLI option.
#### Note about `ipwhitelist`
Unlike Constellation, Tessera does not use a separate `ipwhitelist`. If `useWhiteList` is set to `true` in the `.json` config then the `peers` list will be used as the whitelist.
If `ipwhitelist` is provided in an existing `.toml` config file then this will only be used to set `useWhiteList: true`; any nodes included in this list will not be added by default to the Tessera config. Make sure to add any nodes that were only included in `ipwhitelist` to `peers` after using the utility.
#### Validation
Validation is applied to the generated config. Messages will be printed to the terminal if the validation identifies issues. For example, if a `hostname` is not provided then the following message will be printed:
```
Warning: may not be null on property serverConfig.hostName
```
Any validation violations will have to be addressed before the config can be used with Tessera.

View File

@ -0,0 +1,124 @@
## Enclave
### What is an enclave?
An enclave is a secure processing environment that acts as a black box for processing commands and data. Enclaves come in various forms, some on hardware and others in software. In all scenarios, the purpose is to protect information that exists inside of the enclave from malicious attack.
### What does a Tessera enclave do?
The Tessera enclave is designed to handle all of the encryption/decryption operations required by the Transaction Manager, as well as all forms of key management.
This enables all sensitive operations to be handled in a single place, without any leakage into areas of program memory that don't need access. This also means that a smaller application can be run in a secure environment, where memory constraints are often more stringent, such as hardware enclaves.
The Transaction Manager, which handles peer management and database access, as well as Quorum communication does not perform **any** encryption/decryption, greatly reducing the impact an attack can have.
### What exactly does the enclave handle?
The Tessera enclave **handles** the following data:
- public/private key access
- public keys of extra recipients (** should be moved into Transaction Manager, not sensitive)
- default identity of attached nodes
The enclaves **performs** the following actions on request:
- fetching the default identity for attached nodes (default public key)
- providing forwarding keys for all transactions (** should be moved into Transaction Manager, not sensitive)
- returning all public keys managed by this enclave
- encrypting a payload for given sender and recipients
- encrypting raw payloads for given sender
- decrypting transactions for a given recipient (or sender)
- adding new recipients for existing payloads
### Where does the Enclave sit in the private transaction flow?
The Enclave is the innermost actor of the sequence of events. The below diagram demonstrates where the enclave sits:
![Quorum Tessera Privacy Flow](https://github.com/jpmorganchase/tessera/raw/master/Tessera%20Privacy%20flow.jpeg)
As the diagram shows, each enclave interacts only with it's own transaction manager and no-one else.
Tessera provides different types of Enclaves to suit different needs:
### Types of Enclave
#### Local enclave
The local enclave is the classical option that was included in versions of Tessera prior to v0.9. This includes the enclave inside the same process and the transaction manager. This is still an option, and requires all the enclave configuration to be inside the same configuration file and the Transaction Manager configuration.
##### How to use?
In order to use the local enclave, you simply need to not specify an Enclave server type in the configuration. don't forget to specify the enclave config in the Transaction Manager config file.
#### HTTP Enclave
The HTTP Enclave is a remote enclave that serves RESTful endpoints over HTTP. This allows a clear separation of concerns for between the Enclave process and Transaction Manager (TM) process. The enclave must be present and running at TM startup as it will be called upon for initialisation.
##### How to use?
The HTTP enclave can be started up by specifying an `ENCLAVE` server app type, with REST as the communication type. This same configuration should be put into the TM configuration so it knows where to find the remote enclave. Remember to set TLS settings as appropriate, with the TM being a client of the Enclave.
##### Advantage?
The HTTP enclave could be deployed in a completely secure environment away from local machine where TM process runs and it adds this additional layer of security for private keys which is only accessible from HTTP enclave.
### Setting up an Enclave
### Configuration
The configuration for the enclave is designed to the same as for the Transaction Manager.
#### Local Enclave Setup
The following should be present in the TM configuration:
```json
{
"keys": {
"keyData": [{
"privateKey": "yAWAJjwPqUtNVlqGjSrBmr1/iIkghuOh1803Yzx9jLM=",
"publicKey": "/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="
}]
},
"alwaysSendTo": []
}
```
#### Remote Enclave Setup
The configuration required is minimal, and only requires the following from the main config (as an example):
In the remote enclave config:
```json
{
"serverConfigs": [{
"app": "ENCLAVE",
"enabled": true,
"serverAddress": "http://localhost:8080",
"communicationType": "REST",
"bindingAddress": "http://0.0.0.0:8080"
}],
"keys": {
"keyData": [{
"privateKey": "yAWAJjwPqUtNVlqGjSrBmr1/iIkghuOh1803Yzx9jLM=",
"publicKey": "/+UuD63zItL1EbjxkKUljMgG8Z1w0AJ8pNOR4iq2yQc="
}]
},
"alwaysSendTo": []
}
```
and in the TM configuration:
```json
"serverConfigs": [{
"app": "ENCLAVE",
"enabled": true,
"serverAddress": "http://localhost:8080",
"communicationType": "REST"
}],
```
The keys are the same as the Transaction Manager configuration, and can use all the key types including vaults. When using a vault with the enclave, be sure to include the corresponding jar on the classpath, either:
* `/path/to/azure-key-vault-0.9-SNAPSHOT-all.jar`
* `/path/to/hashicorp-key-vault-0.9-SNAPSHOT-all.jar`
If using the all-in-one Transaction Manager jar, all the relevant files are included, and just the configuration needs to be updated for the TM.
If using the individual "make-your-own" jars, you will need the "core Transaction Manager" jar along with the "Enclave clients" jar, and add them both to the classpath as such: `java -cp /path/to/transactionmanager.jar:/path/to/enclave-client.jar com.quroum.tessera.Launcher -configfile /path/to/config.json`

View File

@ -0,0 +1,114 @@
## Generating keys
Key generation can be used in multiple ways:
1. Generate a key pair and save in new files `.pub` and `.key`:
```
tessera -keygen
```
This command will require interactive input for passwords.
If you wish to generate an unlocked key, `/dev/null` can be used for stdin to tell the application not to expect any input (version 0.8 only):
```
# Version 0.8+
tessera -keygen < /dev/null
# Version 0.7.x or before
printf "\n\n" | tessera -keygen
```
The `-filename` option can be used to specify alternate filepaths. Multiple key pairs can be generated at the same time by providing a comma-separated list of values:
```
tessera -keygen -filename /path/to/key1,/path/to/key2
```
1. Generate a key pair and save to an Azure Key Vault, with DNS name `<url>`, as secrets with IDs `Pub` and `Key`:
```
tessera -keygen -keygenvaulttype AZURE -keygenvaulturl <url>
```
The `-filename` option can be used to specify alternate IDs. Multiple key pairs can be generated at the same time by providing a comma-separated list of values:
```
tessera -keygen -keygenvaulttype AZURE -keygenvaulturl <url> -filename id1,id2
```
**Note: If saving new keys with the same ID as keys that already exist in the vault, the existing keys will be replaced by the newer version.**
> Environment variables must be set if using an Azure Key Vault, for more information see [Setting up an Azure key vault](../Setting%20up%20an%20Azure%20Key%20Vault)
1. Generate a key pair and save to a Hashicorp Vault at the secret path `secretEngine/secretName` with IDs `publicKey` and `privateKey`:
```bash
tessera -keygen -keygenvaulttype HASHICORP -keygenvaulturl <url> \
-keygenvaultsecretengine secretEngine -filename secretName
```
Options exist for configuring TLS and AppRole authentication (by default the AppRole path is set to `approle`):
```bash
tessera -keygen -keygenvaulttype HASHICORP -keygenvaulturl <url> \
-keygenvaultsecretengine <secretEngineName> -filename <secretName> \
-keygenvaultkeystore <JKS file> -keygenvaulttruststore <JKS file> \
-keygenvaultapprole <authpath>
```
The `-filename` option can be used to generate and store multiple key pairs at the same time:
```bash
tessera -keygen -keygenvaulttype HASHICORP -keygenvaulturl <url> \
-keygenvaultsecretengine secretEngine -filename myNode/keypairA,myNode/keypairB
```
**Saving a new key pair to an existing secret will overwrite the values stored at that secret. Previous versions of secrets may be retained and be retrievable by Tessera depending on how the K/V secrets engine is configured. See [Keys](../../../Configuration/Keys) for more information on configuring Tessera for use with Vault.**
> Environment variables must be set if using a Hashicorp Vault, and a version 2 K/V secret engine must be enabled. For more information see [Setting up a Hashicorp Vault](../Setting%20up%20a%20Hashicorp%20Vault).
1. Generate a key pair, save to files and then start Tessera using a provided config
```
tessera -keygen -configfile /path/to/config.json
```
```
tessera -keygen -filename key1 -configfile /path/to/config.json
```
Tessera loads `config.json` as usual and includes the newly generated key data before starting.
An updated `.json` configfile is printed to the terminal (or to a file if using the `-output` CLI option). No changes are made to the `config.json` file itself.
## Securing private keys
Generated private keys can be encrypted with a password. This is prompted for on the console during key generation. After generating password-protected keys, the password must be added to your configuration to ensure Tessera can read the keys. The password is not saved anywhere but must be added to the configuration else the key will not be able to be decrypted.
Passwords can be added to the json config either inline using `"passwords":[]`, or stored in an external file that is referenced by `"passwordFile": "Path"`. Note that the number of arguments/file-lines provided must equal the total number of private keys. For example, if there are 3 total keys and the second is not password secured, the 2nd argument/line must be blank or contain dummy data.
Tessera uses Argon2 in the process of encrypting private keys. By default, Argon2 is configured as follows:
```
{
"variant": "id",
"memory": 1048576,
"iterations": 10,
"parallelism": 4
}
```
The Argon2 configuration can be altered by using the `-keygenconfig` option. Any override file must have the same format as the default configuration above and all options must be provided.
```
tessera -keygen -filename /path/to/key1 -keygenconfig /path/to/argonoptions.json
```
For more information on Argon2 see the [Argon2 Github page](https://github.com/P-H-C/phc-winner-argon2).
### Updating password protected private keys
The password of a private key stored in a file can be updated. Password update uses the `--keys.keyData.privateKeyPath` CLI option to get the path to the file.
Password update can be used in multiple ways. Running any of these commands will start a CLI prompt to allow you to set a new password.
1. Add a password to an unlocked key
```
tessera -updatepassword --keys.keyData.privateKeyPath /path/to/.key
```
1. Change the password of a locked key. This requires providing the current password for the key (either inline or as a file)
```
tessera -updatepassword --keys.keyData.privateKeyPath /path/to/.key --keys.passwords <password>
```
or
```
tessera -updatepassword --keys.keyData.privateKeyPath /path/to/.key --keys.passwordFile /path/to/pwds
```
1. Use different Argon2 options from the defaults when updating the password
```
tessera --keys.keyData.privateKeyPath <path to keyfile> --keys.keyData.config.data.aopts.algorithm <algorithm> --keys.keyData.config.data.aopts.iterations <iterations> --keys.keyData.config.data.aopts.memory <memory> --keys.keyData.config.data.aopts.parallelism <parallelism>
```
All options have been overriden here but only the options you wish to alter from their defaults need to be provided.

View File

@ -0,0 +1,56 @@
The private/public key pairs used by Tessera can be [stored](../Keys) in and [retrieved](../../../Configuration/Keys) from a key vault, preventing the need to store the keys locally.
This page details how to set up and configure a Hashicorp Vault for use with Tessera.
The [Hashicorp Vault Getting Started documentation](https://learn.hashicorp.com/vault/) provides much of the information needed to get started. The following section goes over some additional considerations when running Tessera with Vault.
## Configuring the vault
### TLS
When running in production situations it is advised to configure the Vault server for 2-way (mutual) TLS communication. Tessera also supports 1-way TLS and unsecured (no TLS) communications with a Vault server.
An example configuration for the Vault listener to use 2-way TLS is shown below. This can be included as part of the `.hcl` used when starting the Vault server:
```
listener "tcp" {
tls_min_version = "tls12"
tls_cert_file = "/path/to/server.crt"
tls_key_file = "/path/to/server.key"
tls_require_and_verify_client_cert = "true"
tls_client_ca_file = "/path/to/client-ca.crt"
}
```
### Auth methods
Tessera directly supports the [AppRole](https://www.vaultproject.io/docs/auth/approle.html) auth method. If required, other auth methods can be used by logging in outside of Tessera (e.g. using the HTTP API) and providing the resulting vault token to Tessera. See the *Enabling Tessera to use the vault* section below for more information.
When using AppRole, Tessera assumes the default auth path to be `approle`, however this value can be overwritten. See [Keys](../../../Configuration/Keys) for more information.
### Policies
To be able to carry out all possible interactions with a Vault, Tessera requires the following policy capabilities: `["create", "update", "read"]`. A subset of these capabilities can be configured if not all functionality is required.
### Secret engines
Tessera can read and write keys to the following secret engine type:
- [K/V Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2.html)
The K/V Version 2 secret engine supports versioning of secrets, however only a limited number of versions are retained. This number can be changed as part of the Vault configuration process.
## Enabling Tessera to use the vault
### Environment Variables
If using a Hashicorp Vault, Tessera requires certain environment variables to be set depending on the auth method being used.
- If using the AppRole auth method, set:
1. `HASHICORP_ROLE_ID`
2. `HASHICORP_SECRET_ID`
These credentials are obtained as outlined in the [AppRole documentation](https://www.vaultproject.io/docs/auth/approle.html). Tessera will use these credentials to authenticate with Vault.
- If using the root token or you already have a token due to authorising with an alternative method, set:
1. `HASHICORP_TOKEN`
!!! note
If using TLS additional environment variables must be set. See [Keys](../../../Configuration/Keys) for more information as well as details of the Tessera configuration required to retrieve keys from a Vault.
### Dependencies
The Hashicorp dependencies are included in the `tessera-app-<version>-app.jar`. If using the `tessera-simple-<version>-app.jar` then `hashicorp-key-vault-<version>-all.jar` must be added to the classpath.

View File

@ -0,0 +1,72 @@
The private/public key pairs used by Tessera can be [stored](../Keys) in and [retrieved](../../../Configuration/Keys) from a key vault, preventing the need to store the keys locally.
This page details how to set up and configure an Azure Key Vault for use with Tessera.
The Microsoft Azure documentation provides much of the information needed to get started. The information in this section has been taken from the following pages of the Azure documentation:
* https://docs.microsoft.com/en-us/azure/key-vault/quick-create-node
* https://docs.microsoft.com/en-us/azure/key-vault/key-vault-get-started
## Creating the vault
The Key Vault can be created using either the [Azure Web Portal](https://azure.microsoft.com/en-gb/features/azure-portal/) or the [Azure CLI](https://docs.microsoft.com/en-gb/cli/azure/install-azure-cli?view=azure-cli-latest).
### Using the portal
1. Login to the Azure Portal
1. Select `Create a resource` from the sidebar
1. Search for, and select, `Key Vault`
1. Fill out the necessary fields, including choosing a suitable name and location (the list of possible locations can be found using the Azure CLI, see below), and click `Create`
### Using the CLI
1. Login to Azure using the [Azure CLI](https://docs.microsoft.com/en-gb/cli/azure/install-azure-cli?view=azure-cli-latest)
```
az login
```
1. Create a resource group, choosing a suitable name and location
```
az group create --name <rg-name> --location <location>
```
To view a list of possible locations use the command
```
az account list-locations
```
1. Create the Key Vault, choosing a suitable name and location and referencing the resource group created in the previous step
```
az keyvault create --name <kv-name> --resource-group <rg-name> --location <location>
```
A Key Vault has now been created that can be used to store secrets.
## Configuring the vault to work with Tessera
Azure uses an Active Directory system to grant access to services. We will create an 'application' that we will authorise to use the vault. We will provide the credentials created as a result of this to authenticate our Tessera instance to use the key vault.
In order for the vault to be accessible by Tessera, the following steps must be carried out:
1. Log in to the Azure Portal
1. Select `Azure Active Directory` from the sidebar
1. Select `App registrations`, `New application registration` and complete the registration process. **Make note of the `Application ID`**.
1. Once registered, click `Settings`, `Keys`, and create a new key with a suitable name and expiration rule. **Once the key has been saved make note of the key value - this is the only opportunity to see this value!**
To authorise the newly registered app to use the Key Vault complete the following steps:
1. Select `All services` from the sidebar and select `Key vaults`
1. Select the vault
1. Select `Access policies` and `Add new`
1. Search for and select the newly registered application as the `Principal`
1. Enable the `Get` and `Set` secret permissions
## Enabling Tessera to use the vault
### Environment Variables
If using an Azure Key Vault, Tessera requires two environment variables to be set:
1. `AZURE_CLIENT_ID`: The `Application ID`
1. `AZURE_CLIENT_SECRET`: The application registration `key`
Both of these values can be retrieved during the application registration process as outlined above.
### Dependencies
The Azure dependencies are included in the `tessera-app-<version>-app.jar`. If using the `tessera-simple-<version>-app.jar` then `azure-key-vault-<version>-all.jar` must be added to the classpath.

View File

@ -0,0 +1,74 @@
## Transaction Manager
### What is a transaction manager?
A transaction manager is the central piece in the lifecycle of a private transaction. It interfaces with most other parts of the network/infrastructure and manages the lifecycle of private data.
### What does a transaction manager do?
The transaction manager's duties include:
- forming a P2P network of transaction managers & broadcasting peer/key information
- interfacing with the enclave for encrypting/decrypting private payloads
- storing and retrieving saved data from the database
- providing the gateway for Quorum to distribute private information
The Transaction Manager, which handles peer management and database access, as well as Quorum communication, does not contain access to any private keys and does not perform and encryption/decryption, greatly reducing the impact an attack can have.
### Where does the transaction manager sit in the private transaction flow?
The transaction manager is the touch point for Quorum to distribute it's private payloads. It connects directly to Quorum and interfaces with the attached enclave, as well as with other transaction managers.
![Quorum Tessera Privacy Flow](https://github.com/jpmorganchase/tessera/raw/master/Tessera%20Privacy%20flow.jpeg)
## Setting up a Transaction Manager
### Running Tessera
The only mandatory parameter for running a minimal Transaction Manager is the location of the configuration file to use.
Use the `-configfile <path>` argument to specify the location of the config file.
Other CLI arguments can be passed, and details of these commands can be found in their respective pages - particularly around key vaults and key generation.
### Databases
By default, Tessera uses an H2 file-based database, but any JDBC compatible database can be used.
To do this, add the necessary drivers to the classpath, and run the `com.quorum.tessera.Launcher` class, like the following:
```
java -cp some-jdbc-driver.jar:/path/to/tessera-app.jar:. com.quorum.tessera.Launcher
```
For example, to use Oracle database:
```
java -cp ojdbc7.jar:tessera-app.jar:. com.quorum.tessera.Launcher -configfile config.json
```
Some DDL scripts have been provided for more popular databases, but feel free to adapt these to whichever database you wish to use.
### Configuration
The configuration for the transaction manager is described in the [configuration overview](../../Configuration/Configuration Overview), as well as [sample configurations](../../Configuration/Sample Configuration).
### Flavours of transaction manager
For advanced users, you may decide on certain options for the transaction manager, or to disable other parts.
The default transaction manager comes with the standard options most setups will use, but other versions are as follows:
- GRPC communication (experimental)
- Non-remote only enclaves (named "tessera-simple")
These must be built from source and can be found inside the `tessera-dist` module.
## Data recovery
Tessera contains functionality to request transactions from other nodes in the network; this is useful if the database is lost or corrupted somehow.
However, depending on the size of the network and the number of transactions made between peers, this can put heavy strain on the network resending all the data.
### How to enable
The data recovery mechanism is intended to be a "switch-on" feature as a startup command. The times when you will need this will be known prior to starting the application (usually after a disaster event). When starting Tessera, simply add the following property to the startup command: `-Dspring.profiles.active=enable-sync-poller`. This should go before any jar or class definitions, e.g. `java -Dspring.profiles.active=enable-sync-poller -jar tessera.jar -configfile config.json`.
### How it works
The data recovery procedure works by invoking a "resend request" to each new node it sees in the network. This request will cause the target node to resend each of its transactions to the intended recipient, meaning they will again save the transaction in their database.
The target node will not send back transactions as a response the request in order to ensure that a malicious node cannot get access to the transactions. i.e. anyone can send a request for a particular key, but it will mean that the node that holds that key will receive the transactions, not the node making the request. In normal usage, the node making the request and the node holding the public key are the same.

View File

@ -0,0 +1,20 @@
## Tessera
Tessera is a stateless Java system that is used to enable the encryption, decryption, and distribution of private transactions for [Quorum](/).
Each Tessera node:
* Generates and maintains a number of private/public key pairs
* Self manages and discovers all nodes in the network (i.e. their public keys) by connecting to as few as one other node
* Provides Private and Public API interfaces for communication:
* Private API - This is used for communication with Quorum
* Public API - This is used for communication between Tessera peer nodes
* Provides two way SSL using TLS certificates and various trust models like Trust On First Use (TOFU), whitelist,
certificate authority, etc.
* Supports IP whitelist
* Connects to any SQL DB which supports the JDBC client

View File

@ -0,0 +1,19 @@
Administrators of a Tessera node can use the `admin` CLI command to make changes to the node. These changes are made while the node is running and do not require a node restart.
The `admin` CLI makes use of the [ADMIN server API](../Interface%20%26%20API) and provides some additional features. An ADMIN server must have been configured at startup (see [Configuration Overview](../../Configuration/Configuration%20Overview)).
After starting a node with `tessera -configfile /path/to/node-config.json`, the admin CLI can be used. Currently supported admin commands are:
- `addpeer`: Add a new peer to a running node
### `addpeer`
```
tessera admin -configfile /path/to/node-config.json -addpeer <new-peer-url>
```
The provided configfile is the same configfile used to start the Tessera node.
This will do two things:
1. Add `<new-peer-url>` to the node's list of peers, by using the ADMIN API
1. Update the configfile `/path/to/node-config.json` to include `<new-peer-url>` in the `peer` list. Updating the configfile in this way means that if the node is stopped and started again, the admin changes will still be present.
If the configfile should not be updated, use the ADMIN API directly.

View File

@ -0,0 +1,120 @@
## Interface Details
All interfaces can be set to run over HTTP, GRPC or HTTP-over-Unix-Sockets.
### gRPC (for inter-node communication)
We currently have an implementation of gRPC for peer node communication as experiment API. This is not enabled on Quorum yet, but between Tessera nodes they can be enabled by adding in a couple of properties in the configuration file as child elements of `serverConfig`.
- `grpcPort` - when this value is specified, Tessera node will start a gRPC server listening on this port. The normal `port` value would still be used for starting REST server.
- `communicationType` - possible values are `REST`, `GRPC`. Default value is `REST`.
Please note that communication between Quorum and Tessera are still via unix socket. This communication flag provides additional options for Tessera peer-to-peer communication. If gRPC is the option specified, please ensure the peers urls are provided with the appropriate ports.
---
### Tessera to Tessera - Public API
Tessera nodes communicate with each other for:
- Node/network discovery
- Sending/Receiving encrypted payloads
The following endpoints are advertised on this interface:
* `/version`
* `/upcheck`
* `/push`
* `/resend`
* `/partyinfo`
### Third Party - Public API
Tessera nodes communicate with third parties for:
- storing encrypted payloads for external applications
The following endpoints are advertised on this interface:
* `/version`
* `/upcheck`
* `/storeraw`
### Quorum to Tessera - Private API
Quorum uses this API to:
- Check if the local Tessera node is running
- Send and receive details of private transactions
The following endpoints are advertised on this interface:
- `/version`
- `/upcheck`
- `/sendraw`
- `/send`
- `/receiveraw`
- `/receive`
- `/sendsignedtx`
### Admin API
Admins should use this API to:
- Access information about the Tessera node
- Make changes to the configuration of the Tessera node
The following endpoints are advertised on this API:
- `/peers` - Add to, and retrieve from, the Tessera node's peers list
- `/keypairs` - Retrieve all public keys or search for a particular public key in use by the Tessera node
## API Details
**`version`** - _Get Tessera version_
- Returns the version of Tessera that is running.
**`upcheck`** - _Check Tessera node is running_
- Returns the text "I'm up!"
**`push`** - _Push transactions between nodes_
- Persist encrypted payload received from another node.
**`resend`** - _Resend transaction_
- Resend all transactions for given key or given hash/recipient.
**`partyinfo`** - _Retrieve details of known nodes_
- GET: Request public keys/url of all known peer nodes.
- POST: accepts a stream that contains the caller node's network information, and returns a merged copy with the callee node's network information
**`sendraw`** - _Send transaction bytestring_
- Send transaction payload bytestring from Quorum to Tessera node. Tessera sends the transaction hash in the response back.
**`send`** - _Send transaction bytestring_
- Similar to sendraw however request payload is in json format. Please see our [Swagger documentation](https://jpmorganchase.github.io/tessera-swagger/index.html) for object model.
**`storeraw`** - _Store transaction bytestring_
- Store transaction bytestring from a third party to Tessera node. Tessera sends the transaction hash in the response back.
**`sendsignedtx`** - _Distribute signed transaction payload_
- Send transaction payload identified by hash (returned by storeraw) from Quorum to Tessera node. Tessera sends the transaction hash in the response back.
**`receiveraw`** - _Receive transaction bytestring_
- Receive decrypted bytestring of the transaction payload from Tessera to Quorum for transactions it is party to.
**`receive`** - _Receive transaction bytestring_
- Similar to receiveraw however request payload is in json format. Please see our [Swagger documentation](https://jpmorganchase.github.io/tessera-swagger/index.html) for object model.
**`delete`** - _Delete a transaction_
- Delete hashed encrypted payload stored in Tessera nodes.
For more interactions with the API see the [Swagger documentation](https://jpmorganchase.github.io/tessera-swagger/index.html).

View File

@ -0,0 +1,82 @@
## Using Splunk
Tessera logs can be interpreted by Splunk to allow for monitoring and analysis. The general steps to set up Splunk monitoring for a network of Tessera nodes are:
1. If one does not already exist, set up a central Splunk instance (a Receiver) on a separate host.
1. Configure the Tessera hosts to forward their logging info to the Receiver by:
1. Providing Logback configuration to Tessera as a CLI arg on start-up to specify the format of the logging output (e.g. save to a file).
This is achieved by providing an XML/Groovy config file defining the logging-level and Logback Appenders to use, for example:
``` xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/path/to/file.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<logger name="org.glassfish.jersey.internal.inject.Providers" level="ERROR" />
<logger name="org.hibernate.validator.internal.util.Version" level="ERROR" />
<logger name="org.hibernate.validator.internal.engine.ConfigurationImpl" level="ERROR" />
<root level="INFO">
<appender-ref ref="FILE"/>
</root>
</configuration>
```
Further information can be found in the [Logback documentation](https://logback.qos.ch/manual/configuration.html#syntax).
1. Set up Splunk Universal Forwarders on each Tessera host. These are lightweight Splunk clients that will be used to collect and pass logging data to the central Splunk instance for analysis.
1. Set up the central Splunk instance to listen and receive logging data from the Universal Forwarders
Further information about setting up Splunk and Universal Forwarders can be found in the Splunk documentation. The following pages are a good starting point:
* [Consolidate data from multiple hosts](http://docs.splunk.com/Documentation/Forwarder/7.1.2/Forwarder/Consolidatedatafrommultiplehosts)
* [Set up the Universal Forwarder](http://docs.splunk.com/Documentation/Splunk/7.1.2/Forwarding/EnableforwardingonaSplunkEnterpriseinstance#Set_up_the_universal_forwarder)
* [Configure the Universal Forwarder](http://docs.splunk.com/Documentation/Forwarder/7.1.2/Forwarder/Configuretheuniversalforwarder)
* [Enable a receiver](http://docs.splunk.com/Documentation/Forwarder/7.1.2/Forwarder/Enableareceiver)
## Jersey Web Server Metrics
Simple Jersey web server metrics for a Tessera node can be monitored if desired. Tessera can store this performance data in a time-series database. Two open-source database options are available for use, depending on your particular use-case:
* [InfluxDB](https://www.influxdata.com/time-series-platform/influxdb/): For 'push'-style data transmission
* [Prometheus](https://prometheus.io/): For 'pull'-style data transmission
To set up monitoring requires the installation and configuration of one of these database offerings. Both databases integrate well with the open source metrics dashboard editor [Grafana](https://grafana.com/) to allow for easy creation of dashboards to visualise the data being captured from Tessera.
### Using InfluxDB
The [InfluxDB documentation](https://docs.influxdata.com/influxdb) provides all the information needed to get InfluxDB setup and ready to integrate with Tessera. A summary of the steps is as follows:
1. Download and install InfluxDB
1. Create an InfluxDB database
1. Add configuration details to the `server` section of your Tessera config file to allow Tessera to post metrics data to the InfluxDB host. An example configuration using InfluxDB's default hostName and port is (truncated for clarity):
```json
"server": {
"influxConfig": {
"port": 8086,
"hostName": "http://localhost",
"dbName": "tessera_demo",
"pushIntervalInSecs": 60
}
}
```
With `influxConfig` provided, Tessera will collect metrics data and push it to the InfluxDB service periodically based on the value set for `pushIntervalInSecs`
1. You can use the `influx` CLI to query the database and view the data that is being stored
### Using Prometheus
The [Prometheus documentation](https://prometheus.io/docs/) provides all the information needed to get Prometheus setup and ready to integrate with Tessera. A summary of the steps is as follows:
1. Download and install Prometheus
1. Configure `prometheus.yml` to give the Prometheus instance the necessary information to pull metrics from each of the Tessera nodes. As Prometheus is pull-based, no additional config needs to be added to Tessera
1. Go to `localhost:9090` (or whatever host and port have been defined for the Prometheus instance) to see the Prometheus UI and view the data that is being stored
### Creating dashboards with Grafana
Once Tessera usage data is being stored in either InfluxDB or Prometheus, Grafana can be used to easily create dashboards to visualise that data. The [Grafana documentation](http://docs.grafana.org/) provides all the information needed to set up a Grafana instance and integrate it with both of these time-series databases. A summary of the steps is as follows:
1. Download and install Grafana
1. Create a new Data Source to connect Grafana with your database of choice
1. Create a new Dashboard
1. Create charts and other elements for the dashboard

BIN
docs/Quorum Design.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 KiB

View File

@ -1,10 +1,4 @@
# Quorum documentation
* [Whitepaper](./Quorum%20Whitepaper%20v0.2.pdf) (PDF) - Quorum Whitepaper [demo video](https://vimeo.com/user5833792/review/210456842/a42d0fcb87)
* [Design](./design.md) - Quorum design overview
* [Raft Specific Documentation](./raft.md) - Overview of raft implementation
* [Istanbul RPC API](./istanbul-rpc-api.md) - Overview of Istanbul BFT APIs
* [Privacy](./privacy.md) - Sending private transactions [privacy video](https://vimeo.com/user5833792/review/210456729/8f70cfaaa5)
* [Running](./running.md) - Detailed instructions for running Quorum nodes (see also [Constellation](https://github.com/jpmorganchase/constellation), [Tessera](https://github.com/jpmorganchase/tessera))
* [API](./api.md) - new privacy API
New Quorum documentation is now published on https://goquorum.readthedocs.io/

View File

@ -0,0 +1,42 @@
## Network Permissioning
Network Permissioning is a feature that controls which nodes can connect to a given node and also to which nodes the given node can dial out to. Currently, it is managed at the individual node level by the `--permissioned` command line flag when starting the node.
If the `--permissioned` flag is set, the node looks for a file named `<data-dir>/permissioned-nodes.json` . This file contains the whitelist of enodes that this node can connect to and accept connections from. Therefore, with permissioning enabled, only the nodes that are listed in the `permissioned-nodes.json` file become part of the network. If the `--permissioned` flag is specified but no nodes are added to the `permissioned-nodes.json` file then this node can neither connect to any node nor accept any incoming connections.
The `permissioned-nodes.json` file follows the below pattern, which is similar to the `<data-dir>/static-nodes.json` file that is used to specify the list of static nodes a given node always connects to:
``` json
[
"enode://remoteky1@ip1:port1",
"enode://remoteky1@ip2:port2",
"enode://remoteky1@ip3:port3",
]
```
Sample file: (node id truncated for clarity)
``` json
[
"enode://6598638ac5b15ee386210156a43f565fa8c485924894e2f3a967207c047470@127.0.0.1:30300",
]
```
!!! Note
In the current implementation, every node has its own copy of the `permissioned-nodes.json` file. In this case, if different nodes have a different list of remote keys then each node may have a different list of permissioned nodes - which may have an adverse effect. In a future release, the permissioned nodes list will be moved from the `permissioned-nodes.json` file to a Smart Contract, thereby ensuring that all nodes will use one global on-chain list to verify network connections.
## Enclave Encryption Technique
The Enclave encrypts payloads sent to it by the Transaction Manager using xsalsa20poly1305 (payload container) and curve25519xsalsa20poly1305 (recipient box). Each payload encryption produces a payload container, as well as N recipient boxes, where N is the number of recipients specified in the `privateFor` param of the Transaction.
* A payload container contains the payload encrypted with a symmetric key and a random nonce
* A recipient box is the Master Key for the payload container encrypted for the public key of a recipient using a random nonce. (Note that this is basically how PGP works, but using the [NaCl](https://nacl.cr.yp.to/) cryptographic primitives.)
We currently manually define all public key whitelists, and dont do automatic rotation of keys, however the system was built to support rotation trivially, by allowing counterparties to advertise multiple keys at once. The tooling to make it seamless and automatic is on the our Roadmap.
We also do not currently have a PKI system, but simply randomly generate keys that are manually added to whitelists (e.g. a registry of authorized counterparties on the blockchain.) The process is currently for operators to generate a keypair and then add the public keys to the whitelists manually.
## Private Key Storage Algorithm
The following steps detail the technique used to manage the private keys:
1. Given a password P
2. Generate random Argon2i nonce
3. Generate random NaCl secretbox nonce
4. Stretch P using Argon2i (and the Argon2i nonce) into a 32-byte master key (MK)
5. Encrypt Private key in secretbox using secretbox nonce and Argon2i-stretched MK

View File

@ -0,0 +1,31 @@
# Transaction Processing
One of the key features of Quorum is that of Transaction Privacy. To that end, we introduce the notion of 'Public Transactions' and 'Private Transactions'. Note that this is a notional concept only and Quorum does not introduce new Transaction Types, but rather, the Ethereum Transaction Model has been extended to include an optional `privateFor` parameter (the population of which results in a Transaction being treated as private by Quorum) and the Transaction Type has a new `IsPrivate` method to identify such Transactions.
[Constellation](../../Privacy/Constellation/Constellation) / [Tessera](../../Privacy/Tessera/Tessera) are used by Quorum to transfer private payloads to their intended recipients, performing encryption and related operations in the process.
## Public Transactions
So called 'Public Transactions' are those Transactions whose payload is visible to all participants of the same Quorum network. These are [created as standard Ethereum Transactions in the usual way](https://github.com/ethereum/wiki/wiki/JavaScript-API#web3ethsendtransaction).
Examples of Public Transactions may include Market Data updates from some service provider, or some reference data update such as a correction to a Bond Security definition.
!!! Note
'Public' Transactions are not Transactions from the public Ethereum network. Perhaps a more appropriate term would be 'common' or 'global' Transactions, but 'Public' is used to contrast with 'Private' Transactions.
## Private Transactions
So called 'Private Transactions' are those Transactions whose payload is only visible to the network participants whose public keys are specified in the `privateFor` parameter of the Transaction . `privateFor` can take multiple addresses in a comma separated list. (See Creating Private Transactions under the [Running Quorum](../../Getting Started/running) section).
When the Quorum Node encounters a Transaction with a non-null `privateFor` value, it sets the `V` value of the Transaction Signature to be either `37` or `38` (as opposed to `27` or `28` which are the values used to indicate a Transaction is 'public' as per standard Ethereum as specified in the Ethereum yellow paper).
## Processing Transactions
### Public vs Private Transaction handling
Public Transactions are executed in the standard Ethereum way, and so if a Public Transaction is sent to an Account that holds Contract code, each participant will execute the same code and their underlying StateDBs will be updated accordingly.
Private Transactions, however, are not executed per standard Ethereum: prior to the sender's Quorum Node propagating the Transaction to the rest of the network, it replaces the original Transaction Payload with a hash of the encrypted Payload that it receives from Constellation/Tessera. Participants that are party to the Transaction will be able to replace the hash with the actual payload via their Constellation/Tessera instance, whilst those Participants that are not party will only see the hash.
The result is that if a Private Transaction is sent to an Account that holds Contract code, those participants who are not party to the Transaction will simply end up skipping the Transaction, and therefore not execute the Contract code. However those participants that are party to the Transaction will replace the hash with the original Payload before calling the EVM for execution, and their StateDB will be updated accordingly. In absence of making corresponding changes to the geth client, these two sets of participants would therefore end up with different StateDBs and not be able to reach consensus. So in order to support this bifurcation of contract state, Quorum stores the state of Public contracts in a Public State Trie that is globally synchronised, and it stores the state of Private contracts in a Private State Trie that is not synchronised globally. For details on how Consensus is achieved in light of this, please refer to [Quorum Consensus](../../Consensus/Consensus).
### Private Transaction Process Flow
Please refer [Private Transaction Flow](../../Privacy/Tessera/How%20Tessera%20Works) section under Tessera

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

97
docs/ZSL.md Normal file
View File

@ -0,0 +1,97 @@
# ZSL Proof of Concept
!!! caution
The POC discussed in this section should not be considered production-ready
## Overview
Quorum supports both Public Contracts (which are executed in the standard Ethereum way, and are visible to all participants in the distributed ledger) and Private Contracts (which are shared between the parties to the private contract using Tessera, but can not be read by other participants). This approach preserves the privacy of the parties to the private contract, and the confidentiality of the private contracts business logic. However, a key limitation is that it does not support prevention of double-spending for digital assets that are exchanged within private contracts.
ZSL (zero-knowledge security layer) is a protocol designed by the team behind Zcash, that leverages zk-SNARKS to enable the transfer of digital assets on a distributed ledger, without revealing any information about the Sender, Recipient, or the quantity of assets that are being transferred.
J.P. Morgan and the Zcash team partnered to create a proof of concept (POC) implementation of ZSL for Quorum, which enables the issuance of digital assets using ZSL-enabled public smart contracts (z-contracts). We refer to such digital assets as “z-tokens”. Z-tokens can be shielded from public view and transacted privately. Proof that a shielded transaction has been executed can be presented to a private contract, thereby allowing the private contract to update its state in response to shielded transactions that are executed using public z-contracts.
This combination of Tesseras private contracts with ZSLs z-contracts, allows obligations that arise from a private contract, to be settled using shielded transfers of z-tokens, while maintaining full privacy and confidentiality.
For more background, please read the [POC Technical Design Document](https://github.com/jpmorganchase/zsl-q/blob/master/docs/ZSL-Quorum-POC_TDD_v1.3pub.pdf).
## Implementation
The ZSL proof of concept has been implemented as follows:
* ZSL-specific code resides in the [zsl-q](https://github.com/jpmorganchase/zsl-q) repo
* The Quorum integration is implemented as a separate branch of the Quorum repo - [zsl_geth1.5](https://github.com/jpmorganchase/quorum/tree/zsl_geth1.5)
* There is also a ZSL-specific branch of the quorum-examples repo - [zsl_geth1.5](https://github.com/jpmorganchase/quorum-examples/tree/zsl_geth1.5)
* The [zsl-q-params](https://github.com/jpmorganchase/zsl-q-params) repo contains the shared parameters required for generating and verifying the zk-SNARK proofs.
Full instructions on how to install Quorum with ZSL can be found in the [zsl-q README](https://github.com/jpmorganchase/zsl-q/blob/master/README.md).
Please note that this POC is intended to demonstrate how ZSL can complement Quorum, and provide a platform for experimentation and exploration of different use cases. It implements a simplified, stripped-down version of the Zerocash protocol to enable rapid prototyping. There is no formal security proof for the protocol, exception-handling has not been implemented for proof verification, the software has not been subjected to rigorous testing, and **it should not be considered “production-ready”**.
Broadly speaking, Quorum ZSL supplies a contract within which virtual funds can be "bundled" into cryptographically obfuscated "notes". Each note represents a store of value, and can be unlocked, or "redeemed", only using a secret spending key. To effect a private transfer, Alice may bundle value into a note, and then transmit the note's secret key to Bob through a private, off-chain channel. Bob may then redeem this note on-chain, revealing, in the process, no public link between Alice and himself. Note that in a previous version, a failure to link _Ethereum_ and _note_ signatures made possible a sort of "front-running" attack; this has been fixed by PR [#587](https://github.com/jpmorganchase/quorum/pull/587).
## Equity Trade use case example
The following example illustrates a specific use case for Quorum with ZSL - a simple equity trade where Alice is buying ACME shares from Bob. The POC includes a demonstration that implements this example; instructions on how to run it can be found [here](https://github.com/jpmorganchase/zsl-q/blob/master/README.md#example-2---private-contract-trade).
![Quorum Equity Trade Use Case diagram](Quorum_Equity_Use_Case.png)
### Beginning State:
* Z-contracts have been created for US dollars (the USD z-contract) and ACME shares (the ACME z-contract),
* Z-tokens have been issued into both contracts by the relevant issuer, then shielded and transferred to Alice and Bob.
* Alice owns some USD z-tokens, and Bob owns some ACME z-tokens. Both their holdings are shielded (i.e. a third-party observer cannot tell who owns what).
### User Story:
1. **A Private Contract is established between Alice and Bob using Tessera.**
1. The Private Contract specifies an equity trade of a specific quantity of ACME shares at a specific price in USD, between two specific parties: Alice (who is buying the ACME shares) and Bob (who is selling ACME shares).
1. The Private Contract references the USD and ACME z-contracts, and the relevant public keys and payment addresses of the parties.
1. One party initialises the contract (this is the equivalent of bidding/offering). It doesn't matter which party does this - in this example, it's Alice.
1. After being initialised, the contract state is "Bid" (it would be "Offer" if Bob had initialised it).
2. **The other party sends the Private Contract a transaction indicating acceptance of the terms.**
1. In this example, it is Bob who accepts Alices bid.
1. At this point, the trade is "done" (i.e. the terms are agreed and both parties have committed to the trade) and all that remains is for Settlement to take place. Assume that the USD must be paid first.
1. Contract state: Done.
3. **The Private Contract instructs Payment.**
1. When the contract's status updates to Done, it issues an instruction to the Buyer's (i.e. Alices) client to pay the relevant amount of USD to the Seller (Bob).
1. Alice's client receives and queues that instruction, and instructs a shielded payment.
4. **The Buyers pays USD to the Seller.**
1. Alice pays the relevant amount of USD z-tokens to Bob's USD payment address by generating the necessary zk-SNARK proof and sending it to the USD z-contract.
1. A shielded transaction takes place, creating a note within the z-contract which only Bob can spend (i.e. Bobs USD z-token balance is increased).
1. Alices balance of USD z-tokens is reduced accordingly.
5. **The Buyer provides evidence of payment to the Private Contract.**
1. Alice sends the Private Contract a transaction with the output note of the USD payment.
1. This also transmits the note to Bob so he can spend it.
6. **The Private Contract verifies the payment.**
1. The Private Contract calls a constant function on the USD z-contract, using the note supplied by Alice, to verify that the payment is valid.
1. The z-contract responds in a binary fashion to indicate whether the note commitment is in the z-contracts note accumulator (in which case the shielded payment is valid) or not.
1. If it is valid, the contract's status updates to Payment Received, and...
7. **..the Private Contract instructs Delivery.**
1. The Private Contract issues an instruction to the Seller's (i.e. Bobs) client to transfer the relevant amount of ACME shares to the Buyer
1. Bob's client receives and queues that instruction, and prompts him to make the payment.
8. **The Seller delivers ACME shares to the Buyer.**
1. Bob transfers the relevant amount of ACME z-tokens to Alice's ACME payment address by generating the necessary zk-SNARK proof and sending it to the ACME z-contract.
1. A shielded transaction takes place, creating a note output that only Alice can spend (i.e. Alices ACME z-token balance is increased).
1. Bobs balance of ACME z-tokens is reduced accordingly.
9. **The Seller provides evidence of delivery to the Private Contract**
1. Bob sends the Private Contract a transaction with the output note of the ACME delivery.
1. This also transmits the note to Alice so she can “spend” the note (i.e. transfer those tokens to someone else).
10. **The Private Contract verifies delivery.**
1. The Private Contract calls the ACME z-contract (using a constant function), using the note supplied by Bob, to verify that the transfer is valid.
1. If it is valid, the contract's status updates to Settled.
After Alice has delivered the USD z-tokens to Bob in step 5, he can send them to a third party (e.g. Carol).
* Carol will not be able to ascertain the source of the tokens (i.e. that Bob obtained them from Alice).
* Alice will not be able to ascertain when Bob transfers the tokens to someone else (or who the recipient is). She will be able to see that a transaction has occurred (because the transaction is written to the z-contract on the main Quorum chain which she has access to) but she will not be able to ascertain the Sender, Recipient, nor the quantity of tokens being transferred.
* The same holds true for the ACME z-tokens Alice has obtained from Bob.
### Protocol
The diagram below illustrates how the cryptographic protocol supports steps 1 thru 6 from the example above.
![ZSL/Quorum Proof of Concept Protocol (v0.4)](ZSL-Quorum-POC_Protocol_v0_4.png)

2
docs/conf.py Normal file
View File

@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
exclude_patterns = ['/tests']

View File

@ -1,54 +0,0 @@
# Design
## Public/Private State
Quorum supports dual state:
- Public state: accessible by all nodes within the network
- Private state: only accessible by nodes with the correct permissions
The difference is made through the use of transactions with encrypted (private) and non-encrypted payloads (public).
Nodes can determine if a transaction is private by looking at the `v` value of the signature.
Public transactions have a `v` value of `27` or `28`, private transactions have a value of `37` or `38`.
If the transaction is private, the node can only execute the transaction if it has the ability to decrypt the payload.
Nodes who are not involved in the transaction cannot decrypt the payload and are therefore unable to process the transaction.
As a result all nodes share a common public state which is created through public transactions and have a local unique private state.
This model imposes a restriction in the ability to modify state in private transactions.
Since it's a common use case for a (private) contract to read data from a public contract the virtual machine has the ability to jump into read only mode.
For each call from a private contract to a public contract the virtual machine will change to read only mode.
If the virtual machine is in read only mode and the code tries to make a state change the virtual machine stops execution and throws an exception.
The following transactions are allowed:
```
1. S -> A -> B
2. S -> (A) -> (B)
3. S -> (A) -> [B -> C]
```
and the following transaction are unsupported:
```
1. (S) -> A
2. (S) -> (A)
```
where:
- `S` = sender
- `(X)` = private
- `X` = public
- `->` = direction
- `[]` = read only mode
### State verification
To determine if nodes are in sync the public state root hash is included in the block.
Since private transactions can only be processed by nodes that are involved its impossible to get global consensus on the private state.
To overcome this issue the RPC method `eth_storageRoot(address[, blockNumber]) -> hash` can be used.
It returns the storage root for the given address at an (optional) block number.
If the optional block number is not given the latest block number is used.
The storage root hash can be on or off chain compared by the parties involved.

BIN
docs/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

115
docs/index.md Normal file
View File

@ -0,0 +1,115 @@
# Quorum - Enterprise Ethereum Client
## What is Quorum?
Quorum is an Ethereum-based distributed ledger protocol that has been developed to provide industries such as finance, supply chain, retail, real estate, etc. with a permissioned implementation of Ethereum that supports transaction and contract privacy.
Quorum includes a minimalistic fork of the [Go Ethereum client](https://github.com/ethereum/go-ethereum) (a.k.a geth), and as such, leverages the work that the Ethereum developer community has undertaken.
The primary features of Quorum, and therefore extensions over public Ethereum, are:
* Transaction and contract privacy
* Multiple voting-based consensus mechanisms
* Network/Peer permissions management
* Higher performance
Quorum currently includes the following components:
* Quorum Node (modified Geth Client)
* Privacy Manager (Constellation/Tessera)
* Transaction Manager
* Enclave
!!! info "Background Reading"
For more information on the design rationale and background to Quorum, please read the [**Quorum Whitepaper**](https://github.com/jpmorganchase/quorum/blob/master/docs/Quorum%20Whitepaper%20v0.2.pdf), view the [Hyperledger deck](https://drive.google.com/open?id=0B8rVouOzG7cOeHo0M2ZBejZTdGs) or watch the [presentation](https://drive.google.com/open?id=0B8rVouOzG7cOcDg4UkxqdTBacm8) given to the Hyperledger Project Technical Steering Committee meeting on 22-Sept-16. Also see quick overview of sending private transactions [here](https://vimeo.com/user5833792/review/210456729/8f70cfaaa5)
## Logical Architecture Diagram
![](Quorum%20Design.png)
## Design
### Public/Private State
Quorum supports dual state:
- Public state: accessible by all nodes within the network
- Private state: only accessible by nodes with the correct permissions
The difference is made through the use of transactions with encrypted (private) and non-encrypted payloads (public).
Nodes can determine if a transaction is private by looking at the `v` value of the signature.
Public transactions have a `v` value of `27` or `28`, private transactions have a value of `37` or `38`.
If the transaction is private, the node can only execute the transaction if it has the ability to access and decrypt the payload. Nodes who are not involved in the transaction do not have the private payload at all. As a result all nodes share a common public state which is created through public transactions and have a local unique private state.
This model imposes a restriction in the ability to modify state in private transactions.
Since it's a common use case for a (private) contract to read data from a public contract the virtual machine has the ability to jump into read only mode.
For each call from a private contract to a public contract the virtual machine will change to read only mode.
If the virtual machine is in read only mode and the code tries to make a state change the virtual machine stops execution and throws an exception.
The following transactions are allowed:
```
1. S -> A -> B
2. S -> (A) -> (B)
3. S -> (A) -> [B -> C]
```
and the following transaction are unsupported:
```
1. (S) -> A
2. (S) -> (A)
```
where:
- `S` = sender
- `(X)` = private
- `X` = public
- `->` = direction
- `[]` = read only mode
### State verification
To determine if nodes are in sync the public state root hash is included in the block.
Since private transactions can only be processed by nodes that are involved its impossible to get global consensus on the private state.
To overcome this issue the RPC method `eth_storageRoot(address[, blockNumber]) -> hash` can be used.
It returns the storage root for the given address at an (optional) block number.
If the optional block number is not given the latest block number is used.
The storage root hash can be on or off chain compared by the parties involved.
## Component Overview
### Quorum Node
The Quorum Node is intentionally designed to be a lightweight fork of geth in order that it can continue to take advantage of the R&D that is taking place within the ever growing Ethereum community. To that end, Quorum will be updated in-line with future geth releases.
The Quorum Node includes the following modifications to geth:
* Consensus is achieved with the Raft or Istanbul BFT consensus algorithms instead of using Proof-of-Work.
* The P2P layer has been modified to only allow connections to/from permissioned nodes.
* The block generation logic has been modified to replace the global state root check with a new global public state root.
* The block validation logic has been modified to replace the global state root in the block header with the global public state root
* The State Patricia trie has been split into two: a public state trie and a private state trie.
* Block validation logic has been modified to handle Private Transactions
* Transaction creation has been modified to allow for Transaction data to be replaced by encrypted hashes in order to preserve private data where required
* The pricing of Gas has been removed, although Gas itself remains
### Constellation & Tessera
[Constellation](Privacy/Constellation/Constellation) and [Tessera](Privacy/Tessera/Tessera) are Haskell and Java implementations of a general-purpose system for submitting information in a secure way. They are comparable to a network of MTA (Message Transfer Agents) where messages are encrypted with PGP. It is not blockchain-specific, and are potentially applicable in many other types of applications where you want individually-sealed message exchange within a network of counterparties. The Constellation and Tessera modules consist of two sub-modules:
* The Node (which is used for Quorum's default implementation of a `PrivateTransactionManager`)
* The Enclave
#### Transaction Manager
Quorums Transaction Manager is responsible for Transaction privacy. It stores and allows access to encrypted transaction data, exchanges encrypted payloads with other participant's Transaction Managers but does not have access to any sensitive private keys. It utilizes the Enclave for cryptographic functionality (although the Enclave can optionally be hosted by the Transaction Manager itself.)
The Transaction Manager is restful/stateless and can be load balanced easily.
For further details on how the Transaction Manager interacts with the Enclave, please refer [here](Privacy/Tessera/Tessera%20Services/Transaction%20Manager)
#### The Enclave
Distributed Ledger protocols typically leverage cryptographic techniques for transaction authenticity, participant authentication, and historical data preservation (i.e. through a chain of cryptographically hashed data.) In order to achieve a separation of concerns, as well as to provide performance improvements through parallelization of certain crypto-operations, much of the cryptographic work including symmetric key generation and data encryption/decryption is delegated to the Enclave.
The Enclave works hand in hand with the Transaction Manager to strengthen privacy by managing the encryption/decryption in an isolated way. It holds private keys and is essentially a “virtual HSM” isolated from other components.
For further details on the Enclave, please refer [here](Privacy/Tessera/Tessera%20Services/Enclave).

View File

@ -1,19 +0,0 @@
# Privacy
## Sending Private Transactions
To send a private transaction, a private Transaction Manager must be configured. This is the
service which transfers private payloads to their intended recipients, performing
encryption and related operations in the process.
[Constellation](https://github.com/jpmorganchase/constellation) / [Tessera](https://github.com/jpmorganchase/tessera) is used to provide the private Transaction Manager for a Quorum node. Once a Constellation/Tessera node is running, the `PRIVATE_CONFIG` environment variable is used to point the Quorum node to the transaction manager instance. Examples of this can be seen in the [quorum-examples 7nodes](https://github.com/jpmorganchase/quorum-examples) source files.
Once Constellation/Tessera is launched and `PRIVATE_CONFIG` points to a valid configuration file,
a `SendTransaction` call can be made private by specifying the `privateFor` argument.
`privateFor` is a list of public keys of the intended recipients (these public keys are distinct from Ethereum account keys). When a transaction
is private, the transaction contents will be sent to the `PrivateTransactionManager` and the
identifier returned will be placed in the transaction instead. When other Quorum nodes
receive a private transaction, they will query their `PrivateTransactionManager` for the
identifier and replace the transaction contents with the result. Nodes which are
not party to a transaction will not be able to retrieve the original contents.

7
docs/requirements.txt Normal file
View File

@ -0,0 +1,7 @@
mkdocs>=1.0
pymdown-extensions==6.0
mkdocs-material>=4.1
Markdown==3.0.1
markdown-fenced-code-tabs==1.0.5
markdown-include==0.5.1
MarkupSafe==1.1.0

View File

@ -1,107 +0,0 @@
# Running Quorum
Quorum introduces the `--permissioned` CLI argument:
```
QUORUM OPTIONS:
--permissioned If enabled, the node will allow only a defined list of nodes to connect
```
The full list of arguments can be viewed by running `geth --help`.
### Initialize chain
The first step is to generate the genesis block.
The `7nodes` directory in the `quorum-examples` repository contains several keys (using an empty password) that are used in the example genesis file:
```
key1 vote key 1
key2 vote key 2
key3 vote key 3
key4 block maker 1
key5 block maker 2
```
Example genesis file (copy to `genesis.json`):
```json
{
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
"config": {
"homesteadBlock": 0
},
"difficulty": "0x0",
"extraData": "0x",
"gasLimit": "0x2FEFD800",
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
"nonce": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
```
Now we can initialize geth:
```
geth init genesis.json
```
### Setup Bootnode
Optionally you can set up a bootnode that all the other nodes will first connect to in order to find other peers in the network. You will first need to generate a bootnode key:
1. To generate the key for the first time:
`bootnode -genkey tmp_file.txt // this will start a bootnode with an enode address and generate a key inside a “tmp_file.txt” file`
2. To later restart the bootnode using the same key (and hence use the same enode url):
`bootnode -nodekey tmp_file.txt`
or
`bootnode -nodekeyhex 77bd02ffa26e3fb8f324bda24ae588066f1873d95680104de5bc2db9e7b2e510 // Key from tmp_file.txt`
### Start node
Starting a node is as simple as `geth`. This will start the node without any of the roles and makes the node a spectator. If you have setup a bootnode then be sure to add the `--bootnodes` param to your startup command:
`geth --bootnodes $BOOTNODE_ENODE`
## Setup multi-node network
The [quorum-examples 7nodes](https://github.com/jpmorganchase/quorum-examples) source files contain several scripts demonstrating how to set up a private test network made up of 7 nodes.
## Permissioned Network
Node Permissioning is a feature of Quorum that is used to define:
1. The nodes that a particular Quorum node is able to connect to
2. The nodes that a particular Quorum node is able to receive connections from
Permissioning is managed at the individual node level by using the `--permissioned` command line flag when starting the node.
If a node is started with `--permissioned` set, the node will look for a `<data-dir>/permissioned-nodes.json` file. This file contains the list of enodes that this node can connect to and accept connections from. In other words, if permissioning is enabled, only the nodes that are listed in the `permissioned-nodes.json` file become part of the network.
If `--permissioned` is set, a `permissioned-nodes.json` file must be provided. If the flag is set but no nodes are present in this file, then the node will be unable to make any outward connections or accept any incoming connections.
The format of `permissioned-nodes.json` is similar to `static-nodes.json`:
```json
[
"enode://enodehash1@ip1:port1",
"enode://enodehash2@ip2:port2",
"enode://enodehash3@ip3:port3"
]
```
For example, including the hash, a sample file might look like:
```json
[
"enode://6598638ac5b15ee386210156a43f565fa8c48592489d3e66ac774eac759db9eb52866898cf0c5e597a1595d9e60e1a19c84f77df489324e2f3a967207c047470@127.0.0.1:30300"
]
```
In the current release, every node has its own copy of `permissioned-nodes.json`. In a future release, the permissioned nodes list will be moved to a smart contract, thereby keeping the list on-chain and requiring just one global list of nodes that connect to the network.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

15
docs/theme/assets/stylesheets/extra.css vendored Normal file
View File

@ -0,0 +1,15 @@
.help-sidetab {
position: fixed;
top: 70%;
right: -71px;
-webkit-transform: translateY(-50%);
-ms-transform: translateY(-50%);
transform: translateY(-50%);
color: white;
background-color: #00e290;
transform: rotate(270deg);
padding: 5px 20px 5px 20px;
border-radius : 10px 10px 0px 0px;
cursor: pointer;
font-size: medium;
}

220
docs/theme/base.html vendored Normal file
View File

@ -0,0 +1,220 @@
{% import "partials/language.html" as lang with context %}
{% set feature = config.theme.feature %}
{% set palette = config.theme.palette %}
{% set font = config.theme.font %}
<!doctype html>
<html lang="{{ lang.t('language') }}" class="no-js">
<head>
{% block site_meta %}
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta http-equiv="x-ua-compatible" content="ie=edge">
{% if page and page.meta and page.meta.description %}
<meta name="description" content="{{ page.meta.description }}">
{% elif config.site_description %}
<meta name="description" content="{{ config.site_description }}">
{% endif %}
{% if page and page.meta and page.meta.redirect %}
<script>var anchor=window.location.hash.substr(1);location.href="{{ page.meta.redirect }}"+(anchor?"#"+anchor:"")</script>
<meta http-equiv="refresh" content="0; url={{ page.meta.redirect }}">
<meta name="robots" content="noindex">
<link rel="canonical" href="{{ page.meta.redirect }}">
{% elif page.canonical_url %}
<link rel="canonical" href="{{ page.canonical_url }}">
{% endif %}
{% if page and page.meta and page.meta.author %}
<meta name="author" content="{{ page.meta.author | first }}">
{% elif config.site_author %}
<meta name="author" content="{{ config.site_author }}">
{% endif %}
{% for key in [
"clipboard.copy",
"clipboard.copied",
"search.language",
"search.pipeline.stopwords",
"search.pipeline.trimmer",
"search.result.none",
"search.result.one",
"search.result.other",
"search.tokenizer"
] %}
<meta name="lang:{{ key }}" content="{{ lang.t(key) }}">
{% endfor %}
<link rel="shortcut icon" href="{{ config.theme.favicon | url }}">
<meta name="generator" content="mkdocs-{{ mkdocs_version }}, mkdocs-material-4.2.0">
{% endblock %}
{% block htmltitle %}
{% if page and page.meta and page.meta.title %}
<title>{{ page.meta.title }}</title>
{% elif page and page.title and not page.is_homepage %}
<title>{{ page.title }} - {{ config.site_name }}</title>
{% else %}
<title>{{ config.site_name }}</title>
{% endif %}
{% endblock %}
{% block styles %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/application.668e8dde.css' | url }}">
{% if palette.primary or palette.accent %}
<link rel="stylesheet" href="{{ 'assets/stylesheets/application-palette.224b79ff.css' | url }}">
{% endif %}
{% if palette.primary %}
{% import "partials/palette.html" as map %}
{% set primary = map.primary(
palette.primary | replace(" ", "-") | lower
) %}
<meta name="theme-color" content="{{ primary }}">
{% endif %}
{% endblock %}
{% block libs %}
<script src="{{ 'assets/javascripts/modernizr.74668098.js' | url }}"></script>
{% endblock %}
{% block fonts %}
{% if font != false %}
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family={{
font.text | replace(' ', '+') + ':300,400,400i,700|' +
font.code | replace(' ', '+')
}}">
<style>body,input{font-family:"{{ font.text }}","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"{{ font.code }}","Courier New",Courier,monospace}</style>
{% endif %}
{% endblock %}
<link rel="stylesheet" href="{{ 'assets/fonts/material-icons.css' | url }}">
{% if config.extra.manifest %}
<link rel="manifest" href="{{ config.extra.manifest | url }}">
{% endif %}
{% for path in config["extra_css"] %}
<link rel="stylesheet" href="{{ path | url }}">
{% endfor %}
{% block analytics %}
{% if config.google_analytics %}
{% include "partials/integrations/analytics.html" %}
{% endif %}
{% endblock %}
{% block extrahead %}{% endblock %}
</head>
{% if palette.primary or palette.accent %}
{% set primary = palette.primary | replace(" ", "-") | lower %}
{% set accent = palette.accent | replace(" ", "-") | lower %}
<body dir="{{ lang.t('direction') }}" data-md-color-primary="{{ primary }}" data-md-color-accent="{{ accent }}">
{% else %}
<body dir="{{ lang.t('direction') }}">
{% endif %}
<svg class="md-svg">
<defs>
{% set platform = config.extra.repo_icon or config.repo_url %}
{% if "github" in platform %}
{% include "assets/images/icons/github.f0b8504a.svg" %}
{% elif "gitlab" in platform %}
{% include "assets/images/icons/gitlab.6dd19c00.svg" %}
{% elif "bitbucket" in platform %}
{% include "assets/images/icons/bitbucket.1b09e088.svg" %}
{% endif %}
</defs>
</svg>
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
<label class="md-overlay" data-md-component="overlay" for="__drawer"></label>
{% if page.toc | first is defined %}
<a href="{{ (page.toc | first).url }}" tabindex="1" class="md-skip">
{{ lang.t('skip.link.title') }}
</a>
{% endif %}
{% block header %}
{% include "partials/header.html" %}
{% endblock %}
<div class="md-container">
{% block hero %}
{% if page and page.meta and page.meta.hero %}
{% include "partials/hero.html" with context %}
{% endif %}
{% endblock %}
{% if feature.tabs %}
{% include "partials/tabs.html" %}
{% endif %}
<main class="md-main">
<div class="md-main__inner md-grid" data-md-component="container">
{% block site_nav %}
{% if nav %}
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/nav.html" %}
</div>
</div>
</div>
{% endif %}
{% if page.toc %}
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc">
<div class="md-sidebar__scrollwrap">
<div class="md-sidebar__inner">
{% include "partials/toc.html" %}
</div>
</div>
</div>
{% endif %}
{% endblock %}
<div class="md-content">
<article class="md-content__inner md-typeset">
{% block content %}
{% if page.edit_url %}
<a href="{{ page.edit_url }}" title="{{ lang.t('edit.link.title') }}" class="md-icon md-content__icon">&#xE3C9;</a>
{% endif %}
{% if not "\x3ch1" in page.content %}
<h1>{{ page.title | default(config.site_name, true)}}</h1>
{% endif %}
{{ page.content }}
{% block source %}
{% if page and page.meta and page.meta.source %}
<h2 id="__source">{{ lang.t("meta.source") }}</h2>
{% set repo = config.repo_url %}
{% if repo | last == "/" %}
{% set repo = repo[:-1] %}
{% endif %}
{% set path = page.meta.path | default([""]) %}
{% set file = page.meta.source %}
<a href="{{ [repo, path, file] | join('/') }}" title="{{ file }}" class="md-source-file">
{{ file }}
</a>
{% endif %}
{% endblock %}
{% endblock %}
{% block disqus %}
{% include "partials/integrations/disqus.html" %}
{% endblock %}
</article>
</div>
</div>
</main>
{% block footer %}
{% include "partials/footer.html" %}
{% endblock %}
</div>
{% block scripts %}
<script src="{{ 'assets/javascripts/application.81068b3a.js' | url }}"></script>
{% if lang.t("search.language") != "en" %}
{% set languages = lang.t("search.language").split(",") %}
{% if languages | length and languages[0] != "" %}
{% set path = "assets/javascripts/lunr/" %}
<script src="{{ (path ~ 'lunr.stemmer.support.js') | url }}"></script>
{% for language in languages | map("trim") %}
{% if language != "en" %}
{% if language == "ja" %}
<script src="{{ (path ~ 'tinyseg.js') | url }}"></script>
{% endif %}
{% if language in ("da", "de", "es", "fi", "fr", "hu", "it", "ja", "nl", "no", "pt", "ro", "ru", "sv", "th", "tr") %}
<script src="{{ (path ~ 'lunr.' ~ language ~ '.js') | url }}"></script>
{% endif %}
{% endif %}
{% endfor %}
{% if languages | length > 1 %}
<script src="{{ (path ~ 'lunr.multi.js') | url }}"></script>
{% endif %}
{% endif %}
{% endif %}
<script>app.initialize({version:"{{ mkdocs_version }}",url:{base:"{{ base_url }}"}})</script>
{% for path in config["extra_javascript"] %}
<script src="{{ path | url }}"></script>
{% endfor %}
{% endblock %}
</body>
</html>

80
docs/theme/partials/footer.html vendored Normal file
View File

@ -0,0 +1,80 @@
{% import "partials/language.html" as lang with context %}
<footer class="md-footer">
<div class="help-sidetab">
<div class="md-footer-meta__help">
<a href="https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/" target="_blank" rel="noopener">
Need further help?
</a>
</div>
</div>
<!-- Link to previous and/or next page -->
{% if page.previous_page or page.next_page %}
<div class="md-footer-nav">
<nav class="md-footer-nav__inner md-grid">
<!-- Link to previous page -->
{% if page.previous_page %}
<a href="{{ page.previous_page.url | url }}"
title="{{ page.previous_page.title }}"
class="md-flex md-footer-nav__link md-footer-nav__link--prev"
rel="prev">
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-back
md-footer-nav__button"></i>
</div>
<div class="md-flex__cell md-flex__cell--stretch
md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
{{ lang.t("footer.previous") }}
</span>
{{ page.previous_page.title }}
</span>
</div>
</a>
{% endif %}
<!-- Link to next page -->
{% if page.next_page %}
<a href="{{ page.next_page.url | url }}"
title="{{ page.next_page.title }}"
class="md-flex md-footer-nav__link md-footer-nav__link--next"
rel="next">
<div class="md-flex__cell md-flex__cell--stretch
md-footer-nav__title">
<span class="md-flex__ellipsis">
<span class="md-footer-nav__direction">
{{ lang.t("footer.next") }}
</span>
{{ page.next_page.title }}
</span>
</div>
<div class="md-flex__cell md-flex__cell--shrink">
<i class="md-icon md-icon--arrow-forward
md-footer-nav__button"></i>
</div>
</a>
{% endif %}
</nav>
</div>
{% endif %}
</footer>
<footer>
<div class="md-footer-meta md-typeset">
<div class="md-footer-meta__inner md-grid">
<div class="md-footer-copyright">
{% if config.copyright %}
<div class="md-footer-copyright__highlight">
{{ config.copyright }}
</div>
{% endif %}
powered by
<a href="https://www.mkdocs.org">MkDocs</a>
and
<a href="https://squidfunk.github.io/mkdocs-material/">
Material for MkDocs</a>
</div>
{% include "partials/social.html" %}
</div>
</div>
</footer>

87
docs/theme/partials/header.html vendored Normal file
View File

@ -0,0 +1,87 @@
<!--
Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
-->
<!-- Application header -->
<header class="md-header" data-md-component="header">
<!-- Top-level navigation -->
<nav class="md-header-nav md-grid">
<div class="md-flex">
<!-- Link to home -->
<div class="md-flex__cell md-flex__cell--shrink">
<a href="{{ config.site_url | default(nav.homepage.url, true) | url }}"
title="{{ config.site_name }}"
class="md-header-nav__button md-logo">
{% if config.theme.logo.icon %}
<i class="md-icon">{{ config.theme.logo.icon }}</i>
{% else %}
<img src="{{ config.theme.logo | url }}" height="24" />
{% endif %}
</a>
</div>
<!-- Button to toggle drawer -->
<div class="md-flex__cell md-flex__cell--shrink">
<label class="md-icon md-icon--menu md-header-nav__button"
for="__drawer"></label>
</div>
<!-- Header title -->
<div class="md-flex__cell md-flex__cell--stretch">
<div class="md-flex__ellipsis md-header-nav__title"
data-md-component="title">
{% if config.site_name == page.title %}
{{ config.site_name }}
{% else %}
<span class="md-header-nav__topic">
{{ config.site_name }}
</span>
<span class="md-header-nav__topic">
{{ page.title }}
</span>
{% endif %}
</div>
</div>
<!-- Button to open search dialogue -->
<div class="md-flex__cell md-flex__cell--shrink">
{% if "search" in config["plugins"] %}
<label class="md-icon md-icon--search md-header-nav__button"
for="__search"></label>
<!-- Search interface -->
{% include "partials/search.html" %}
{% endif %}
</div>
<!-- Repository containing source -->
{% if config.repo_url %}
<div class="md-flex__cell md-flex__cell--shrink">
<div class="md-header-nav__source">
{% include "partials/source.html" %}
</div>
</div>
{% endif %}
</div>
</nav>
</header>

46
docs/theme/partials/nav.html vendored Normal file
View File

@ -0,0 +1,46 @@
<!--
Copyright (c) 2016-2019 Martin Donath <martin.donath@squidfunk.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN THE SOFTWARE.
-->
<!-- Main navigation -->
<nav class="md-nav md-nav--primary" data-md-level="0">
<!-- Site title -->
<label class="md-nav__title md-nav__title--site" for="__drawer">
<a href="{{config.site_url}}">{{config.site_name}}</a>
</label>
<!-- Repository containing source -->
{% if config.repo_url %}
<div class="md-nav__source">
{% include "partials/source.html" %}
</div>
{% endif %}
<!-- Render item list -->
<ul class="md-nav__list" data-md-scrollfix>
{% for nav_item in nav %}
{% set path = "nav-" + loop.index | string %}
{% set level = 1 %}
{% include "partials/nav-item.html" %}
{% endfor %}
</ul>
</nav>

94
mkdocs.yml Normal file
View File

@ -0,0 +1,94 @@
# Project information
site_name: Quorum
site_url: https://goquorum.readthedocs.io/
site_description: A permissioned implementation of Ethereum supporting data privacy
site_author: Quorum
copyright: Quorum 2019
# Repository
repo_name: quorum
repo_url: https://github.com/jpmorganchase/quorum
nav:
- Getting Started:
- Setup Overview & Quickstart: Getting Started/Setup Overview & Quickstart.md
- Getting Started From Scratch: Getting Started/Getting-Started-From-Scratch.md
- Quorum Examples:
- Overview: Getting Started/Quorum-Examples.md
- 7 Nodes Example: Getting Started/7Nodes.md
- Running Quorum: Getting Started/running.md
- Consensus:
- Consensus: Consensus/Consensus.md
- Raft: Consensus/raft.md
- Istanbul: Consensus/istanbul-rpc-api.md
- Transaction Processing: Transaction Processing/Transaction Processing.md
- Security:
- Security & Permissioning: Security/Security & Permissioning.md
- Privacy:
- Tessera:
- What is Tessera: Privacy/Tessera/Tessera.md
- How Tessera works: Privacy/Tessera/How Tessera Works.md
- Configuration:
- Overview: Privacy/Tessera/Configuration/Configuration Overview.md
- Keys Config: Privacy/Tessera/Configuration/Keys.md
- TLS Config: Privacy/Tessera/Configuration/TLS.md
- Using CLI to override config: Privacy/Tessera/Configuration/Using CLI to override config.md
- Sample Configuration: Privacy/Tessera/Configuration/Sample Configuration.md
- Tessera Services:
- Transaction Manager: Privacy/Tessera/Tessera Services/Transaction Manager.md
- Enclave: Privacy/Tessera/Tessera Services/Enclave.md
- Keys:
- Key Generation: Privacy/Tessera/Tessera Services/Keys/Keys.md
- Setting up Hashicorp Vault: Privacy/Tessera/Tessera Services/Keys/Setting up a Hashicorp Vault.md
- Setting up Azure Key Vault: Privacy/Tessera/Tessera Services/Keys/Setting up an Azure Key Vault.md
- Usage:
- Interfaces & API: Privacy/Tessera/Usage/Interface & API.md
- Admin Usage: Privacy/Tessera/Usage/Admin Usage.md
- Monitoring: Privacy/Tessera/Usage/Monitoring.md
- Migration from Constellation: Privacy/Tessera/Migration from Constellation.md
- Constellation:
- What is Constellation: Privacy/Constellation/Constellation.md
- How it works: Privacy/Constellation/How constellation works.md
- Sample Configuration: Privacy/Constellation/Sample Configuration.md
- Running Constellation: Privacy/Constellation/Installation & Running.md
theme:
name: 'material'
custom_dir: 'docs/theme'
font:
text: 'Roboto'
code: 'Roboto Mono'
language: 'en'
logo: 'images/logo.png'
extra_css:
- 'theme/assets/stylesheets/extra.css'
markdown_extensions:
- toc:
permalink: true
toc_depth: 3
- codehilite
- admonition
- footnotes
- def_list
- abbr
- pymdownx.arithmatex
- pymdownx.betterem:
smart_enable: all
- pymdownx.keys
- pymdownx.details
- pymdownx.emoji
- pymdownx.magiclink
- pymdownx.mark
- pymdownx.smartsymbols
- pymdownx.superfences
- pymdownx.tasklist:
custom_checkbox: true
- pymdownx.tilde
- meta
- smarty
plugins:
- search