Merge remote-tracking branch 'origin/master' into feature/cliqueConsensus

This commit is contained in:
apratt3377 2018-09-10 16:01:31 -04:00
commit 0d0e7550b5
19 changed files with 205 additions and 224 deletions

119
README.md
View File

@ -1,4 +1,4 @@
# Quorum
# <img src="logo.png" width="100" height="100"/>
<a href="https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/" target="_blank" rel="noopener"><img title="Quorum Slack" src="https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/badge.svg" alt="Quorum Slack" /></a>
@ -8,128 +8,39 @@ Quorum is a fork of [go-ethereum](https://github.com/ethereum/go-ethereum) and i
Key enhancements over go-ethereum:
* __Privacy__ - Quorum supports private transactions and private contracts through public/private state separation and utilising [Constellation](https://github.com/jpmorganchase/constellation), a peer-to-peer encrypted message exchange for directed transfer of private data to network participants
* __Alternative Consensus Mechanisms__ - with no need for POW/POS in a permissioned network, Quorum instead offers multiple consensus mechanisms that are more appropriate for consortium chains:
* __Privacy__ - Quorum supports private transactions and private contracts through public/private state separation, and utilises peer-to-peer encrypted message exchanges (see [Constellation](https://github.com/jpmorganchase/constellation) and [Tessera](https://github.com/jpmorganchase/tessera)) for directed transfer of private data to network participants
* __Alternative Consensus Mechanisms__ - 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
* __Istanbul BFT__ - a PBFT-inspired consensus algorithm with transaction finality, by AMIS.
* __Peer Permissioning__ - node/peer permissioning using smart contracts, ensuring only known parties can join the network
* __Higher Performance__ - Quorum offers significantly higher performance than public geth
Note: The QuorumChain consensus algorithm is not yet supported by this release.
* __Peer Permissioning__ - node/peer permissioning using smart contracts, ensuring only known parties can join the network
* __Higher Performance__ - Quorum offers significantly higher performance than public geth
## Architecture
<a href="https://github.com/jpmorganchase/quorum/wiki/Transaction-Processing#private-transaction-process-flow">![Quorum privacy architecture](https://github.com/jpmorganchase/quorum-docs/raw/master/images/QuorumTransactionProcessing.JPG)</a>
![Quorum Tessera Privacy Flow](https://raw.githubusercontent.com/jpmorganchase/quorum-docs/master/images/QuorumTransactionProcessing.JPG)
The above diagram is a high-level overview of the privacy architecture used by Quorum. For more in-depth discussion of the components, refer to the [wiki](https://github.com/jpmorganchase/quorum/wiki/) pages.
The above diagram is a high-level overview of the privacy architecture used by Quorum. For more in-depth discussion of the components, refer to the [wiki](https://github.com/jpmorganchase/quorum/wiki) pages.
## Quickstart
The quickest way to get started with Quorum is using [VirtualBox](https://www.virtualbox.org/wiki/Downloads) and [Vagrant](https://www.vagrantup.com/downloads.html):
```sh
git clone https://github.com/jpmorganchase/quorum-examples
cd quorum-examples
vagrant up
# (should take 5 or so minutes)
vagrant ssh
```
Now that you have a fully-functioning Quorum environment set up, let's run the 7-node cluster example. This will spin up several nodes with a mix of voters, block makers, and unprivileged nodes.
```sh
# (from within vagrant env, use `vagrant ssh` to enter)
ubuntu@ubuntu-xenial:~$ cd quorum-examples/7nodes
$ ./raft-init.sh
# (output condensed for clarity)
[*] Cleaning up temporary data directories
[*] Configuring node 1
[*] Configuring node 2 as block maker and voter
[*] Configuring node 3
[*] Configuring node 4 as voter
[*] Configuring node 5 as voter
[*] Configuring node 6
[*] Configuring node 7
$ ./raft-start.sh
[*] Starting Constellation nodes
[*] Starting bootnode... waiting... done
[*] Starting node 1
[*] Starting node 2
[*] Starting node 3
[*] Starting node 4
[*] Starting node 5
[*] Starting node 6
[*] Starting node 7
[*] Unlocking account and sending first transaction
Contract transaction send: TransactionHash: 0xbfb7bfb97ba9bacbf768e67ac8ef05e4ac6960fc1eeb6ab38247db91448b8ec6 waiting to be mined...
true
```
We now have a 7-node Quorum cluster with a [private smart contract](https://github.com/jpmorganchase/quorum-examples/blob/master/examples/7nodes/script1.js) (SimpleStorage) sent from `node 1` "for" `node 7` (denoted by the public key passed via `privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]` in the `sendTransaction` call).
Connect to any of the nodes and inspect them using the following commands:
```sh
$ geth attach ipc:qdata/dd1/geth.ipc
$ geth attach ipc:qdata/dd2/geth.ipc
...
$ geth attach ipc:qdata/dd7/geth.ipc
# e.g.
$ geth attach ipc:qdata/dd2/geth.ipc
Welcome to the Geth JavaScript console!
instance: Geth/v1.5.0-unstable/linux/go1.7.3
coinbase: 0xca843569e3427144cead5e4d5999a3d0ccf92b8e
at block: 679 (Tue, 15 Nov 2016 00:01:05 UTC)
datadir: /home/ubuntu/quorum-examples/7nodes/qdata/dd2
modules: admin:1.0 debug:1.0 eth:1.0 net:1.0 personal:1.0 quorum:1.0 rpc:1.0 txpool:1.0 web3:1.0
# let's look at the private txn created earlier:
> eth.getTransaction("0xbfb7bfb97ba9bacbf768e67ac8ef05e4ac6960fc1eeb6ab38247db91448b8ec6")
{
blockHash: "0xb6aec633ef1f79daddc071bec8a56b7099ab08ac9ff2dc2764ffb34d5a8d15f8",
blockNumber: 1,
from: "0xed9d02e382b34818e88b88a309c7fe71e65f419d",
gas: 300000,
gasPrice: 0,
hash: "0xbfb7bfb97ba9bacbf768e67ac8ef05e4ac6960fc1eeb6ab38247db91448b8ec6",
input: "0x9820c1a5869713757565daede6fcec57f3a6b45d659e59e72c98c531dcba9ed206fd0012c75ce72dc8b48cd079ac08536d3214b1a4043da8cea85be858b39c1d",
nonce: 0,
r: "0x226615349dc143a26852d91d2dff1e57b4259b576f675b06173e9972850089e7",
s: "0x45d74765c5400c5c280dd6285a84032bdcb1de85a846e87b57e9e0cedad6c427",
to: null,
transactionIndex: 1,
v: "0x25",
value: 0
}
```
Note in particular the `v` field value of "0x25" or "0x26" (37 or 38 in decimal) which marks this transaction as having a private payload (input).
## Demonstrating Privacy
Documentation detailing steps to demonstrate the privacy features of Quorum can be found in [quorum-examples/7nodes/README](https://github.com/jpmorganchase/quorum-examples/tree/master/examples/7nodes/README.md).
The quickest way to get started with Quorum is by following instructions in the [Quorum Examples](https://github.com/jpmorganchase/quorum-examples) repository. This allows you to quickly create a network of Quorum nodes, and includes a step-by-step demonstration of the privacy features of Quorum.
## Further Reading
Further documentation can be found in the [docs](docs/) folder and on the [wiki](https://github.com/jpmorganchase/quorum/wiki/).
Further documentation can be found in the [docs](docs/) folder and on the [wiki](https://github.com/jpmorganchase/quorum/wiki).
## See also
* [Quorum](https://github.com/jpmorganchase/quorum): this repository
* [Constellation](https://github.com/jpmorganchase/constellation): peer-to-peer encrypted message exchange for transaction privacy
* [Quorum Wiki](https://github.com/jpmorganchase/quorum/wiki)
* [quorum-examples](https://github.com/jpmorganchase/quorum-examples): Quorum demonstration examples
* [Quorum Community Slack Inviter](https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/): Quorum Slack community entry point
* [Constellation](https://github.com/jpmorganchase/constellation): Haskell implementation of peer-to-peer encrypted message exchange for transaction privacy
* [Tessera](https://github.com/jpmorganchase/tessera): Java implementation of peer-to-peer encrypted message exchange for transaction privacy
* [Raft Consensus Documentation](raft/doc.md)
* [Istanbul BFT Consensus Documentation](https://github.com/ethereum/EIPs/issues/650): [RPC API](https://github.com/getamis/go-ethereum/wiki/RPC-API) and [technical article](https://medium.com/getamis/istanbul-bft-ibft-c2758b7fe6ff)
* [Clique POA Consensus Documentation](https://github.com/ethereum/EIPs/issues/225)
* [ZSL](https://github.com/jpmorganchase/quorum/wiki/ZSL) wiki page and [documentation](https://github.com/jpmorganchase/zsl-q/blob/master/README.md)
* [quorum-examples](https://github.com/jpmorganchase/quorum-examples): example quorum clusters
* [quorum-tools](https://github.com/jpmorganchase/quorum-tools): local cluster orchestration, and integration testing tool
* [Quorum Wiki](https://github.com/jpmorganchase/quorum/wiki)
* [Quorum Community Slack Inviter](https://clh7rniov2.execute-api.us-east-1.amazonaws.com/Express/): Quorum Slack community entry point
## Third Party Tools/Libraries
@ -142,6 +53,8 @@ The following Quorum-related libraries/applications have been created by Third P
* [ERC20 REST service](https://github.com/blk-io/erc20-rest-service) - a Quorum-supported RESTful service for creating and managing ERC-20 tokens
* [Nethereum Quorum](https://github.com/Nethereum/Nethereum/tree/master/src/Nethereum.Quorum) - a .NET Quorum adapter
* [web3j-quorum](https://github.com/web3j/quorum) - an extension to the web3j Java library providing support for the Quorum API
* [Apache Camel](http://github.com/apache/camel) - an Apache Camel component providing support for the Quorum API using web3j library. Here is the artcile describing how to use Apache Camel with Ethereum and Quorum https://medium.com/@bibryam/enterprise-integration-for-ethereum-fa67a1577d43
## Contributing

View File

@ -40,6 +40,7 @@ var customGenesisTests = []struct {
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00"
"config" : {"isQuorum":false}
}`,
query: "eth.getBlock(0).nonce",
result: "0x0000000000000042",
@ -56,7 +57,7 @@ var customGenesisTests = []struct {
"mixhash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp" : "0x00",
"config" : {}
"config" : {"isQuorum":false }
}`,
query: "eth.getBlock(0).nonce",
result: "0x0000000000000042",
@ -76,7 +77,9 @@ var customGenesisTests = []struct {
"config" : {
"homesteadBlock" : 314,
"daoForkBlock" : 141,
"daoForkSupport" : true
"daoForkSupport" : true,
"isQuorum" : false
},
}`,
query: "eth.getBlock(0).nonce",

View File

@ -77,6 +77,8 @@ var (
privateblockReceiptsPrefix = []byte("Pr") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
privateReceiptPrefix = []byte("Prs")
privateBloomPrefix = []byte("Pb")
quorumEIP155ActivatedPrefix = []byte("quorum155active")
)
// txLookupEntry is a positional metadata to help looking up the data content of
@ -117,6 +119,13 @@ func GetBlockNumber(db DatabaseReader, hash common.Hash) uint64 {
return binary.BigEndian.Uint64(data)
}
//returns whether we have a chain configuration that can't be updated
//after the EIP155 HF has happened
func GetIsQuorumEIP155Activated(db DatabaseReader) bool {
data, _ := db.Get(quorumEIP155ActivatedPrefix)
return len(data) == 1
}
// GetHeadHeaderHash retrieves the hash of the current canonical head block's
// header. The difference between this and GetHeadBlockHash is that whereas the
// last block hash is only updated upon a full block import, the last header
@ -595,6 +604,11 @@ func WriteChainConfig(db ethdb.Putter, hash common.Hash, cfg *params.ChainConfig
return db.Put(append(configPrefix, hash[:]...), jsonChainConfig)
}
// WriteQuorumEIP155Activation writes a flag to the database saying EIP155 HF is enforced
func WriteQuorumEIP155Activation(db ethdb.Putter) error {
return db.Put(quorumEIP155ActivatedPrefix, []byte{1})
}
// GetChainConfig will fetch the network settings based on the given hash.
func GetChainConfig(db DatabaseReader, hash common.Hash) (*params.ChainConfig, error) {
jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...))

View File

@ -386,3 +386,22 @@ func TestBlockReceiptStorage(t *testing.T) {
t.Fatalf("deleted receipts returned: %v", rs)
}
}
// Tests that setting the flag for Quorum EIP155 activation read values correctly
func TestIsQuorumEIP155Active(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
isQuorumEIP155Active := GetIsQuorumEIP155Activated(db)
if isQuorumEIP155Active {
t.Fatal("Quorum EIP155 active read to be set, but wasn't set beforehand")
}
dbSet, _ := ethdb.NewMemDatabase()
WriteQuorumEIP155Activation(dbSet)
isQuorumEIP155ActiveAfterSetting := GetIsQuorumEIP155Activated(dbSet)
if !isQuorumEIP155ActiveAfterSetting {
t.Fatal("Quorum EIP155 active read to be unset, but was set beforehand")
}
}

View File

@ -200,7 +200,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
if height == missingNumber {
return newcfg, stored, fmt.Errorf("missing block number for head header hash")
}
compatErr := storedcfg.CheckCompatible(newcfg, height)
compatErr := storedcfg.CheckCompatible(newcfg, height, GetIsQuorumEIP155Activated(db))
if compatErr != nil && height != 0 && compatErr.RewindTo != 0 {
return newcfg, stored, compatErr
}

View File

@ -23,8 +23,8 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/vm"
// "github.com/ethereum/go-ethereum/consensus/ethash"
// "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
@ -42,7 +42,7 @@ func TestDefaultGenesisBlock(t *testing.T) {
func TestSetupGenesis(t *testing.T) {
var (
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
// customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
customg = Genesis{
Config: &params.ChainConfig{HomesteadBlock: big.NewInt(3)},
Alloc: GenesisAlloc{
@ -84,57 +84,57 @@ func TestSetupGenesis(t *testing.T) {
wantHash: params.MainnetGenesisHash,
wantConfig: params.MainnetChainConfig,
},
{
name: "custom block in DB, genesis == nil",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db)
return SetupGenesisBlock(db, nil)
},
wantHash: customghash,
wantConfig: &params.ChainConfig{HomesteadBlock: big.NewInt(3), IsQuorum: true},
},
{
name: "custom block in DB, genesis == testnet",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
customg.MustCommit(db)
return SetupGenesisBlock(db, DefaultTestnetGenesisBlock())
},
wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestnetGenesisHash},
wantHash: params.TestnetGenesisHash,
wantConfig: params.TestnetChainConfig,
},
{
name: "compatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
oldcustomg.MustCommit(db)
return SetupGenesisBlock(db, &customg)
},
wantHash: customghash,
wantConfig: customg.Config,
},
{
name: "incompatible config in DB",
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// Commit the 'old' genesis block with Homestead transition at #2.
// Advance to block #4, past the homestead transition block of customg.
genesis := oldcustomg.MustCommit(db)
bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
defer bc.Stop()
bc.SetValidator(bproc{})
bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
bc.CurrentBlock()
// This should return a compatibility error.
return SetupGenesisBlock(db, &customg)
},
wantHash: customghash,
wantConfig: customg.Config,
wantErr: &params.ConfigCompatError{
What: "Homestead fork block",
StoredConfig: big.NewInt(2),
NewConfig: big.NewInt(3),
RewindTo: 1,
},
},
// {
// name: "custom block in DB, genesis == nil",
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// customg.MustCommit(db)
// return SetupGenesisBlock(db, nil)
// },
// wantHash: customghash,
// wantConfig: &params.ChainConfig{HomesteadBlock: big.NewInt(3), IsQuorum: true},
// // },
// {
// name: "custom block in DB, genesis == testnet",
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// customg.MustCommit(db)
// return SetupGenesisBlock(db, DefaultTestnetGenesisBlock())
// },
// wantErr: &GenesisMismatchError{Stored: customghash, New: params.TestnetGenesisHash},
// wantHash: params.TestnetGenesisHash,
// wantConfig: params.TestnetChainConfig,
// },
// {
// name: "compatible config in DB",
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// oldcustomg.MustCommit(db)
// return SetupGenesisBlock(db, &customg)
// },
// wantHash: customghash,
// wantConfig: customg.Config,
// },
// {
// name: "incompatible config in DB",
// fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
// // Commit the 'old' genesis block with Homestead transition at #2.
// // Advance to block #4, past the homestead transition block of customg.
// genesis := oldcustomg.MustCommit(db)
// bc, _ := NewBlockChain(db, oldcustomg.Config, ethash.NewFullFaker(), vm.Config{})
// defer bc.Stop()
// bc.SetValidator(bproc{})
// bc.InsertChain(makeBlockChainWithDiff(genesis, []int{2, 3, 4, 5}, 0))
// bc.CurrentBlock()
// // This should return a compatibility error.
// return SetupGenesisBlock(db, &customg)
// },
// wantHash: customghash,
// wantConfig: customg.Config,
// wantErr: &params.ConfigCompatError{
// What: "Homestead fork block",
// StoredConfig: big.NewInt(2),
// NewConfig: big.NewInt(3),
// RewindTo: 1,
// },
// },
}
for _, test := range tests {

Binary file not shown.

View File

@ -1,8 +1,8 @@
# Quorum documentation
* [Whitepaper](https://github.com/jpmorganchase/quorum-docs/raw/master/Quorum%20Whitepaper%20v0.1.pdf) (PDF) - Quorum Whitepaper [demo video](https://vimeo.com/user5833792/review/210456842/a42d0fcb87)
* [Whitepaper](./Quorum%20Whitepaper%20v0.2.pdf) (PDF) - Quorum Whitepaper [demo video](https://vimeo.com/user5833792/review/210456842/a42d0fcb87)
* [Design](./design.md) - Quorum design overview
* [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))
* [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

View File

@ -3,7 +3,7 @@
## Privacy APIs
### `web3.eth.sendTransaction(object)` was modified to support private transactions
__To support private transactions in Quorum, the `web3.eth.sendTransaction(object)` API method has been modified.__
```js
web3.eth.sendTransaction(transactionObject [, callback])
@ -14,22 +14,22 @@ Sends a transaction to the network.
##### Parameters
1. `Object` - The transaction object to send:
- `from`: `String` - The address for the sending account. Uses the [web3.eth.defaultAccount](#web3ethdefaultaccount) property, if not specified.
- `to`: `String` - (optional) The destination address of the message, left undefined for a contract-creation transaction.
- `value`: `Number|String|BigNumber` - (optional) The value transferred for the transaction in Wei, also the endowment if it's a contract-creation transaction.
- `gas`: `Number|String|BigNumber` - (optional, default: To-Be-Determined) The amount of gas to use for the transaction (unused gas is refunded).
- <strike>`gasPrice`: `Number|String|BigNumber` - (optional, default: To-Be-Determined) The price of gas for this transaction in wei, defaults to the mean network gas price.</strike>
- `data`: `String` - (optional) Either a [byte string](https://github.com/ethereum/wiki/wiki/Solidity,-Docs-and-ABI) containing the associated data of the message, or in the case of a contract-creation transaction, the initialisation code.
- `nonce`: `Number` - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.
- `privateFrom`: `String` - (optional) When sending a private transaction, the sending party's base64-encoded public key to use. If not present *and* passing `privateFor`, use the default key as configured in the `TransactionManager`.
- `privateFor`: `List<String>` - (optional) When sending a private transaction, an array of the recipients' base64-encoded public keys.
2. `Function` - (optional) If you pass a callback the HTTP request is made asynchronous. See [this note](#using-callbacks) for details.
- `from`: `String` - The address for the sending account. Uses the `web3.eth.defaultAccount` property, if not specified.
- `to`: `String` - (optional) The destination address of the message, left undefined for a contract-creation transaction.
- `value`: `Number|String|BigNumber` - (optional) The value transferred for the transaction in Wei, also the endowment if it's a contract-creation transaction.
- `gas`: `Number|String|BigNumber` - (optional, default: To-Be-Determined) The amount of gas to use for the transaction (unused gas is refunded).
- <strike>`gasPrice`: `Number|String|BigNumber` - (optional, default: To-Be-Determined) The price of gas for this transaction in wei, defaults to the mean network gas price.</strike>
- `data`: `String` - (optional) Either a [byte string](https://github.com/ethereum/wiki/wiki/Solidity,-Docs-and-ABI) containing the associated data of the message, or in the case of a contract-creation transaction, the initialisation code.
- `nonce`: `Number` - (optional) Integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce.
- `privateFrom`: `String` - (optional) When sending a private transaction, the sending party's base64-encoded public key to use. If not present *and* passing `privateFor`, use the default key as configured in the `TransactionManager`.
- `privateFor`: `List<String>` - (optional) When sending a private transaction, an array of the recipients' base64-encoded public keys.
2. `Function` - (optional) If you pass a callback the HTTP request is made asynchronous.
##### Returns
`String` - The 32 Bytes transaction hash as HEX string.
If the transaction was a contract creation use [web3.eth.getTransactionReceipt()](#web3gettransactionreceipt) to get the contract address, after the transaction was mined.
If the transaction was a contract creation use `web3.eth.getTransactionReceipt()` to get the contract address, after the transaction was mined.
##### Example

View File

@ -5,15 +5,15 @@
Quorum supports dual state:
- public state, accessible by all nodes within the network
- private state, only accessible by nodes with the correct permissions
- 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.
Public transactions have a `v` value of `27` or `28`, private transactions have a value of `37` or `38`.
If the transaction is private and the node has the ability to decrypt the payload it can execute the transaction.
Nodes who are not involved in the transaction cannot decrypt the payload and process the transaction.
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.
@ -23,23 +23,31 @@ If the virtual machine is in read only mode and the code tries to make a state c
The following transactions are allowed:
S: sender, (X): private, X: public, ->: direction, []: read only mode
```
1. S -> A -> B
2. S -> (A) -> (B)
3. S -> (A) -> [B -> C]
```
The following transaction are unsupported:
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.

View File

@ -3,22 +3,17 @@
## Sending Private Transactions
To send a private transaction, a `PrivateTransactionManager` must be configured. This is the
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.
Currently, `constellation` is supported out of the box via the `PRIVATE_CONFIG` environment
variable (please note that this integration method will change in the near future.) See the
`7nodes` folder in the `quorum-examples` repository for a complete example of how to use it.
The transaction sent in `script1.js` is private for node 7's `PrivateTransactionManager`
public key.
[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` is launched and `PRIVATE_CONFIG` points to a valid configuration file,
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. (Note that in the case of
`constellation`, this public key is distinct from Ethereum account keys.) When a transaction
`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 (if any; nodes which are
not party to a transaction will not be able to retrieve the original contents.)
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.

View File

@ -1,7 +1,7 @@
# Running Quorum
A `--permissioned` CLI argument was introduced with Quorum.
Quorum introduces the `--permissioned` CLI argument:
```
QUORUM OPTIONS:
@ -53,15 +53,15 @@ Optionally you can set up a bootnode that all the other nodes will first connect
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`
`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`
`bootnode -nodekey tmp_file.txt`
or
or
`bootnode -nodekeyhex 77bd02ffa26e3fb8f324bda24ae588066f1873d95680104de5bc2db9e7b2e510 // Key from tmp_file.txt`
`bootnode -nodekeyhex 77bd02ffa26e3fb8f324bda24ae588066f1873d95680104de5bc2db9e7b2e510 // Key from tmp_file.txt`
### Start node
@ -72,34 +72,36 @@ Starting a node is as simple as `geth`. This will start the node without any of
## Setup multi-node network
Quorum comes with several scripts to setup a private test network with 7 nodes in the `7nodes` folder in the `quorum-examples` repository.
1. Step 1, run `raft-init.sh` and initialize data directories (change variables accordingly)
2. Step 2, start nodes with `raft-start.sh` (change variables accordingly)
3. Step 3, stop network with `stop.sh`
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 that controls which nodes can connect to a given node and also to which nodes this node can dial out to. Currently, it is managed at individual node level by the command line flag `--permissioned` while starting the node.
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
If the `--permissioned` node is present, the node looks for a file named `<data-dir>/permissioned-nodes.json`. This file contains the list of enodes that this node can connect to and also accepts connections only from those nodes. In other words, if permissioning is enabled, only the nodes that are listed in this file become part of the network. It is an error to enable `--permissioned` but not have the `permissioned-nodes.json` file. If the flag is given, but no nodes are present in this file, then this node can neither connect to any node or accept any incoming connections.
Permissioning is managed at the individual node level by using the `--permissioned` command line flag when starting the node.
The `permissioned-nodes.json` follows following pattern (similar to `static-nodes.json`):
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",
"enode://enodehash3@ip3:port3"
]
```
Sample file:
For example, including the hash, a sample file might look like:
```json
[
"enode://6598638ac5b15ee386210156a43f565fa8c48592489d3e66ac774eac759db9eb52866898cf0c5e597a1595d9e60e1a19c84f77df489324e2f3a967207c047470@127.0.0.1:30300",
"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 one global list of nodes that connect to the network.
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.

View File

@ -125,6 +125,19 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
}
log.Info("Initialised chain configuration", "config", chainConfig)
// changes to manipulate the chain id for migration from 2.0.2 and below version to 2.0.3
// version of Quorum - this is applicable for v2.0.3 onwards
if chainConfig.IsQuorum {
if (chainConfig.ChainId != nil && chainConfig.ChainId.Int64() == 1) || config.NetworkId == 1 {
return nil, errors.New("Cannot have chain id or network id as 1.")
}
}
if !core.GetIsQuorumEIP155Activated(chainDb) && chainConfig.ChainId != nil {
//Upon starting the node, write the flag to disallow changing ChainID/EIP155 block after HF
core.WriteQuorumEIP155Activation(chainDb)
}
eth := &Ethereum{
config: config,
chainDb: chainDb,

View File

@ -40,7 +40,7 @@ var DefaultConfig = Config{
EthashCachesOnDisk: 3,
EthashDatasetsInMem: 1,
EthashDatasetsOnDisk: 2,
NetworkId: 1,
NetworkId: 1337,
LightPeers: 20,
DatabaseCache: 128,
GasPrice: big.NewInt(18 * params.Shannon),

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -248,13 +248,13 @@ func (c *ChainConfig) GasTable(num *big.Int) GasTable {
// CheckCompatible checks whether scheduled fork transitions have been imported
// with a mismatching chain configuration.
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *ConfigCompatError {
func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64, isQuorumEIP155Activated bool) *ConfigCompatError {
bhead := new(big.Int).SetUint64(height)
// Iterate checkCompatible to find the lowest conflict.
var lasterr *ConfigCompatError
for {
err := c.checkCompatible(newcfg, bhead)
err := c.checkCompatible(newcfg, bhead, isQuorumEIP155Activated)
if err == nil || (lasterr != nil && err.RewindTo == lasterr.RewindTo) {
break
}
@ -264,7 +264,7 @@ func (c *ChainConfig) CheckCompatible(newcfg *ChainConfig, height uint64) *Confi
return lasterr
}
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *ConfigCompatError {
func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int, isQuorumEIP155Activated bool) *ConfigCompatError {
if isForkIncompatible(c.HomesteadBlock, newcfg.HomesteadBlock, head) {
return newCompatError("Homestead fork block", c.HomesteadBlock, newcfg.HomesteadBlock)
}
@ -277,15 +277,15 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
if isForkIncompatible(c.EIP150Block, newcfg.EIP150Block, head) {
return newCompatError("EIP150 fork block", c.EIP150Block, newcfg.EIP150Block)
}
if isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) {
if isQuorumEIP155Activated && c.ChainId!=nil && isForkIncompatible(c.EIP155Block, newcfg.EIP155Block, head) {
return newCompatError("EIP155 fork block", c.EIP155Block, newcfg.EIP155Block)
}
if isQuorumEIP155Activated && c.ChainId!=nil && c.IsEIP155(head) && !configNumEqual(c.ChainId, newcfg.ChainId) {
return newCompatError("EIP155 chain ID", c.ChainId, newcfg.ChainId)
}
if isForkIncompatible(c.EIP158Block, newcfg.EIP158Block, head) {
return newCompatError("EIP158 fork block", c.EIP158Block, newcfg.EIP158Block)
}
if c.IsEIP158(head) && !configNumEqual(c.ChainId, newcfg.ChainId) {
return newCompatError("EIP158 chain ID", c.EIP158Block, newcfg.EIP158Block)
}
if isForkIncompatible(c.ByzantiumBlock, newcfg.ByzantiumBlock, head) {
return newCompatError("Byzantium fork block", c.ByzantiumBlock, newcfg.ByzantiumBlock)
}

View File

@ -73,7 +73,7 @@ func TestCheckCompatible(t *testing.T) {
}
for _, test := range tests {
err := test.stored.CheckCompatible(test.new, test.head)
err := test.stored.CheckCompatible(test.new, test.head, false)
if !reflect.DeepEqual(err, test.wantErr) {
t.Errorf("error mismatch:\nstored: %v\nnew: %v\nhead: %v\nerr: %v\nwant: %v", test.stored, test.new, test.head, err, test.wantErr)
}

View File

@ -27,8 +27,8 @@ const (
VersionMeta = "stable" // Version metadata to append to the version string
QuorumVersionMajor = 2
QuorumVersionMinor = 0
QuorumVersionPatch = 2
QuorumVersionMinor = 1
QuorumVersionPatch = 0
)
// Version holds the textual version string.

View File

@ -92,10 +92,17 @@ func (c *Client) SendPayload(pl []byte, b64From string, b64To []string) ([]byte,
req.Header.Set("c11n-to", strings.Join(b64To, ","))
req.Header.Set("Content-Type", "application/octet-stream")
res, err := c.httpClient.Do(req)
if err == nil && res.StatusCode != 200 {
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)
}
defer res.Body.Close()
return ioutil.ReadAll(base64.NewDecoder(base64.StdEncoding, res.Body))
}
@ -106,10 +113,17 @@ func (c *Client) ReceivePayload(key []byte) ([]byte, error) {
}
req.Header.Set("c11n-key", base64.StdEncoding.EncodeToString(key))
res, err := c.httpClient.Do(req)
if err == nil && res.StatusCode != 200 {
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)
}
defer res.Body.Close()
return ioutil.ReadAll(res.Body)
}