mirror of https://github.com/poanetwork/quorum.git
Merge remote-tracking branch 'remotes/origin/master' into geth-upgrade-1.8.18
# Conflicts: # consensus/ethash/consensus.go # eth/bloombits.go # params/protocol_params.go
This commit is contained in:
commit
2914b3c99d
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"package": {
|
||||
"name": "geth",
|
||||
"repo": "quorum",
|
||||
"subject": "_ORGANIZATION_",
|
||||
"vcs_url": "https://github.com/jpmorganchase/quorum",
|
||||
"licenses": [
|
||||
"LGPL-3.0"
|
||||
]
|
||||
},
|
||||
"version": {
|
||||
"name": "_TRAVIS_TAG_",
|
||||
"desc": "Quorum: _TRAVIS_TAG_, Geth: _GETH_VERSION_, Commit: _TRAVIS_COMMIT_, Build Number: _TRAVIS_BUILD_NUMBER_",
|
||||
"released": "_RELEASED_DATE_",
|
||||
"vcs_tag": "_TRAVIS_TAG_",
|
||||
"gpgSign": true,
|
||||
"attributes": [
|
||||
{
|
||||
"name": "Travis",
|
||||
"values": [
|
||||
"_TRAVIS_JOB_WEB_URL_"
|
||||
],
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
{
|
||||
"includePattern": "/dist/(.*.tar.gz)",
|
||||
"uploadPattern": "_TRAVIS_TAG_/$1",
|
||||
"matrixParams": {
|
||||
"override": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"publish": true
|
||||
}
|
85
.travis.yml
85
.travis.yml
|
@ -2,27 +2,94 @@
|
|||
|
||||
language: go
|
||||
go_import_path: github.com/ethereum/go-ethereum
|
||||
sudo: false
|
||||
go: 1.9.x
|
||||
sudo: true
|
||||
branches:
|
||||
only:
|
||||
- /.*/ # everything including tags
|
||||
env:
|
||||
global:
|
||||
- BINTRAY_ORGANIZATION=quorumengineering
|
||||
- BINTRAY_USER=quorumbot
|
||||
# Bintray API Key
|
||||
- secure: "QHiPcd3zQoJEsT3VSpxoLVTYwbiYzS8H18EpY7Tk0EqCIfswS2AvRlyRXUxNvCf9ktzpaeXV4b5cPYJ67dwdp5V/O/ARaK5AL6ZjjrTPR1avPnmz/X2VeQEP0aWk1UGMs1nBUj5rzMbIIxlVhpbiITTLAI4Ao0+xRcBi215mDbv271Z7mACEZfXxjaoJA0/3IkbKz9pu1nC7bTjaaExCDAeLp2p8fHi2YQPnBll/7dkn/m1rnsIY9M3KWNCx6xBmQOr1hulrrB6tZoHwFBoDsVTFJFLckPfrWUZsYUgtfWJMQWc6ntv1gFl0f9x6s5fYEphCU2m1JYjEczlQ03B5ro9EyPGKjO7vQxAaFd5nVd2Xf34ZbssEIyXxlSnP/4Gv1GXl9L9aU1Hth9ckYvT5gYP5t/Nw3CDbKD0HelPBvkf8jZwfdlotzFPS2bOZNdl/rJLWgQrX18a/mC3BH9l4TSRz13tbRfo6YcC3Y/uOvG1n4GxzcVaWojAxn86SkknOczPTf2pk9F3JOcGVSYA2R4kGQAe+ErJH2X5g2sh1D5cCYDjQyl5rzWg6P3eK//HYW+mg2+TQ8k2iQVVSwFwrR0Yn4P+5cRDCW9mjtktgq1rTtslj41gSH49Avqr9oXGM2rqdcJPdN8dnmLMrAtmeSUNMMoexiRMmlF2OQKLrW3k="
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
go: 1.9.x
|
||||
- if: tag IS blank
|
||||
os: linux
|
||||
dist: xenial
|
||||
script:
|
||||
- sudo modprobe fuse
|
||||
- sudo chmod 666 /dev/fuse
|
||||
- sudo chown root:$USER /etc/fuse.conf
|
||||
- go run build/ci.go install
|
||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||
|
||||
- os: osx
|
||||
- if: tag IS blank
|
||||
os: osx
|
||||
osx_image: xcode9.2 # so we don't have to deal with Kernel Extension Consent UI which is never possible in CI
|
||||
go: 1.9.x
|
||||
sudo: required
|
||||
script:
|
||||
- brew update
|
||||
- brew install caskroom/cask/brew-cask
|
||||
- brew cask install osxfuse
|
||||
- go run build/ci.go install
|
||||
- go run build/ci.go test -coverage $TEST_PACKAGES
|
||||
|
||||
- if: tag IS present
|
||||
os: linux
|
||||
dist: xenial
|
||||
env: OUTPUT_FILE=geth_${TRAVIS_TAG}_linux_amd64.tar.gz
|
||||
script:
|
||||
- build/env.sh go run build/ci.go install ./cmd/geth
|
||||
- sudo mkdir -p /dist
|
||||
- cd build/bin
|
||||
- sudo tar cfvz /dist/${OUTPUT_FILE} geth
|
||||
- if: tag IS present
|
||||
os: osx
|
||||
osx_image: xcode9.2
|
||||
env: OUTPUT_FILE=geth_${TRAVIS_TAG}_darwin_amd64.tar.gz
|
||||
script:
|
||||
- build/env.sh go run build/ci.go install ./cmd/geth
|
||||
- sudo mkdir -p /dist
|
||||
- cd build/bin
|
||||
- sudo tar cfvz /dist/${OUTPUT_FILE} geth
|
||||
|
||||
before_deploy:
|
||||
- |
|
||||
echo "Prepare Bintray descriptor"
|
||||
export GETH_VERSION=$(cat ${TRAVIS_BUILD_DIR}/VERSION)
|
||||
export RELEASED_DATE=$(date +'%Y-%m-%d')
|
||||
sed -e "s/_TRAVIS_TAG_/${TRAVIS_TAG}/g" \
|
||||
-e "s/_TRAVIS_BUILD_NUMBER_/${TRAVIS_BUILD_NUMBER}/g" \
|
||||
-e "s/_GETH_VERSION_/${GETH_VERSION}/g" \
|
||||
-e "s/_RELEASED_DATE_/${RELEASED_DATE}/g" \
|
||||
-e "s/_TRAVIS_COMMIT_/${TRAVIS_COMMIT}/g" \
|
||||
-e "s/_TRAVIS_JOB_WEB_URL_/${TRAVIS_JOB_WEB_URL//\//\\/}/g" \
|
||||
-e "s/_ORGANIZATION_/${BINTRAY_ORGANIZATION}/g" \
|
||||
${TRAVIS_BUILD_DIR}/.bintray.json > /tmp/bintray.json
|
||||
after_deploy:
|
||||
- |
|
||||
published=""
|
||||
while [ "$published" == "" ]; do
|
||||
echo "Sleep 5s to wait until ${OUTPUT_FILE} is published"
|
||||
sleep 5
|
||||
result=$(curl -u ${BINTRAY_USER}:${BINTRAY_API_KEY} "https://api.bintray.com/packages/${BINTRAY_ORGANIZATION}/quorum/geth/versions/${TRAVIS_TAG}/files")
|
||||
echo "$result"
|
||||
if [[ "$result" == *"${OUTPUT_FILE}"* ]]; then
|
||||
published="done"
|
||||
fi
|
||||
done
|
||||
- |
|
||||
echo "Add ${OUTPUT_FILE} to Download List"
|
||||
curl -u ${BINTRAY_USER}:${BINTRAY_API_KEY} \
|
||||
-H "Content-type: application/json" \
|
||||
-X PUT \
|
||||
--data "{\"list_in_downloads\": true}" \
|
||||
https://api.bintray.com/file_metadata/${BINTRAY_ORGANIZATION}/quorum/${TRAVIS_TAG}/${OUTPUT_FILE}
|
||||
deploy:
|
||||
provider: bintray
|
||||
file: /tmp/bintray.json
|
||||
user: ${BINTRAY_USER}
|
||||
key: ${BINTRAY_API_KEY}
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
|
@ -1,6 +1,8 @@
|
|||
|
||||
# Building Quorum
|
||||
|
||||
Note: Building Quorum requires both a Go (version 1.9 or later) and a C compiler. You can install them using your favourite package manager.
|
||||
|
||||
Clone the repository and build the source:
|
||||
|
||||
```
|
||||
|
|
|
@ -2,7 +2,57 @@
|
|||
|
||||
## How does private state work?
|
||||
|
||||
Let's look at the EVM structure:
|
||||
Original commit from Jeff explains the dual public and private state with INITIAL restrictions:
|
||||
```
|
||||
commit 763f939f4725daa136161868d3b01fa7a84eb71e
|
||||
Author: Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||
Date: Mon Oct 31 12:46:40 2016 +0100
|
||||
|
||||
core, core/vm: dual state & read only EVM
|
||||
|
||||
This commit implements a dual state approach. The dual state approach
|
||||
separates public and private state by making the core vm environment
|
||||
context aware.
|
||||
|
||||
Although not currently implemented it will need to prohibit value
|
||||
transfers and it must initialise all transactions from accounts on the
|
||||
public state. This means that sending transactions increments the
|
||||
account nonce on the public state and contract addresses are derived
|
||||
from the public state when initialised by a transaction. For obvious
|
||||
reasons, contract created by private contracts are still derived from
|
||||
public state.
|
||||
|
||||
This is required in order to have consensus over the public state at all
|
||||
times as non-private participants would still process the transaction on
|
||||
the public state even though private payload can not be decrypted. This
|
||||
means that participants of a private group must do the same in order to
|
||||
have public consensus. However the creation of the contract and
|
||||
interaction still occurs on the private state.
|
||||
|
||||
It implements support for the following calling model:
|
||||
|
||||
S: sender, (X): private, X: public, ->: direction, [ ]: read only mode
|
||||
|
||||
1. S -> A -> B
|
||||
2. S -> (A) -> (B)
|
||||
3. S -> (A) -> [ B -> C ]
|
||||
|
||||
It does not support
|
||||
|
||||
1. (S) -> A
|
||||
2. (S) -> (A)
|
||||
3. S -> (A) -> B
|
||||
|
||||
Implemented "read only" mode for the EVM. Read only mode is checked
|
||||
during any opcode that could potentially modify the state. If such an
|
||||
opcode is encountered during "read only", it throws an exception.
|
||||
|
||||
The EVM is flagged "read only" when a private contract calls in to
|
||||
public state.
|
||||
```
|
||||
|
||||
|
||||
Some things have changed since, let's look at the EVM structure in some more detail:
|
||||
|
||||
```go
|
||||
type EVM struct {
|
||||
|
@ -60,53 +110,3 @@ func (env *EVM) Pop() {
|
|||
Note the invariant that `StateDB` always points to the current state db.
|
||||
|
||||
The other interesting note is read only mode. Any time we call from the private state into the public state (`env.privateState != statedb`), we require anything deeper to be *read only*. Private state transactions can't affect public state, so we throw an EVM exception on any mutating operation (`SELFDESTRUCT, CREATE, SSTORE, LOG0, LOG1, LOG2, LOG3, LOG4`). Question: have any more mutating operations been added? Question: could we not mutate deeper private state?
|
||||
|
||||
|
||||
Original commit explanation:
|
||||
```
|
||||
commit 763f939f4725daa136161868d3b01fa7a84eb71e
|
||||
Author: Jeffrey Wilcke <jeffrey@ethereum.org>
|
||||
Date: Mon Oct 31 12:46:40 2016 +0100
|
||||
|
||||
core, core/vm: dual state & read only EVM
|
||||
|
||||
This commit implements a dual state approach. The dual state approach
|
||||
separates public and private state by making the core vm environment
|
||||
context aware.
|
||||
|
||||
Although not currently implemented it will need to prohibit value
|
||||
transfers and it must initialise all transactions from accounts on the
|
||||
public state. This means that sending transactions increments the
|
||||
account nonce on the public state and contract addresses are derived
|
||||
from the public state when initialised by a transaction. For obvious
|
||||
reasons, contract created by private contracts are still derived from
|
||||
public state.
|
||||
|
||||
This is required in order to have consensus over the public state at all
|
||||
times as non-private participants would still process the transaction on
|
||||
the public state even though private payload can not be decrypted. This
|
||||
means that participants of a private group must do the same in order to
|
||||
have public consensus. However the creation of the contract and
|
||||
interaction still occurs on the private state.
|
||||
|
||||
It implements support for the following calling model:
|
||||
|
||||
S: sender, (X): private, X: public, ->: direction, [ ]: read only mode
|
||||
|
||||
1. S -> A -> B
|
||||
2. S -> (A) -> (B)
|
||||
3. S -> (A) -> [ B -> C ]
|
||||
|
||||
It does not support
|
||||
|
||||
1. (S) -> A
|
||||
2. (S) -> (A)
|
||||
3. S -> (A) -> B
|
||||
|
||||
Implemented "read only" mode for the EVM. Read only mode is checked
|
||||
during any opcode that could potentially modify the state. If such an
|
||||
opcode is encountered during "read only", it throws an exception.
|
||||
|
||||
The EVM is flagged "read only" when a private contract calls in to
|
||||
public state.
|
||||
```
|
|
@ -1,7 +1,8 @@
|
|||
# <img src="logo.png" width="100" height="100"/>
|
||||
# <img src="https://raw.githubusercontent.com/jpmorganchase/quorum/master/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>
|
||||
[](https://travis-ci.org/jpmorganchase/quorum)
|
||||
[](https://bintray.com/quorumengineering/quorum/geth/_latestVersion)
|
||||
|
||||
Quorum is an Ethereum-based distributed ledger protocol with transaction/contract privacy and new consensus mechanisms.
|
||||
|
||||
|
@ -42,8 +43,8 @@ The official docker containers can be found under https://hub.docker.com/u/quoru
|
|||
* [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
|
||||
* Quorum supported consensuses
|
||||
* [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)
|
||||
* [Raft Consensus Documentation](docs/raft.md)
|
||||
* [Istanbul BFT Consensus Documentation](https://github.com/ethereum/EIPs/issues/650): [RPC API](https://github.com/jpmorganchase/quorum/blob/master/docs/istanbul-rpc-api.md) and [technical article](https://medium.com/getamis/istanbul-bft-ibft-c2758b7fe6ff). <span style="background-color: #ffffbf">Please note</span> that updated istanbul-tools is now hosted in [this](https://github.com/jpmorganchase/istanbul-tools/) repository
|
||||
* [Clique POA Consensus Documentation](https://github.com/ethereum/EIPs/issues/225) and a [guide to setup clique json](https://modalduality.org/posts/puppeth/) with [puppeth](https://blog.ethereum.org/2017/04/14/geth-1-6-puppeth-master/)
|
||||
* [ZSL](https://github.com/jpmorganchase/quorum/wiki/ZSL) wiki page and [documentation](https://github.com/jpmorganchase/zsl-q/blob/master/README.md)
|
||||
* [quorum-tools](https://github.com/jpmorganchase/quorum-tools): local cluster orchestration, and integration testing tool
|
||||
|
|
|
@ -871,17 +871,12 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
|||
// makeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// for Geth and returns half of the allowance to assign to the database.
|
||||
func makeDatabaseHandles() int {
|
||||
limit, err := fdlimit.Current()
|
||||
limit, err := fdlimit.Maximum()
|
||||
if err != nil {
|
||||
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
||||
}
|
||||
if limit < 2048 {
|
||||
if err := fdlimit.Raise(2048); err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
}
|
||||
if limit > 2048 { // cap database file descriptors even if more is available
|
||||
limit = 2048
|
||||
if err := fdlimit.Raise(uint64(limit)); err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
return limit / 2 // Leave half for networking and other stuff
|
||||
}
|
||||
|
|
|
@ -301,9 +301,9 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
|
|||
if diff < 0 {
|
||||
diff *= -1
|
||||
}
|
||||
limit := parent.GasLimit / params.GasLimitBoundDivisor
|
||||
limit := parent.GasLimit / params.OriginalGasLimitBoundDivisor
|
||||
|
||||
if uint64(diff) >= limit || header.GasLimit < params.MinGasLimit {
|
||||
if uint64(diff) >= limit || header.GasLimit < params.OriginnalMinGasLimit {
|
||||
return fmt.Errorf("invalid gas limit: have %d, want %d += %d", header.GasLimit, parent.GasLimit, limit)
|
||||
}
|
||||
// Verify that the block number is parent's +1
|
||||
|
|
|
@ -119,3 +119,95 @@ func TestDualStateReadOnly(t *testing.T) {
|
|||
t.Errorf("expected 0 got %x", value)
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
calleeAddress = common.Address{2}
|
||||
calleeContractCode = "600a6000526001601ff300" // a function that returns 10
|
||||
callerAddress = common.Address{1}
|
||||
// a functionn that calls the callee's function at its address and return the same value
|
||||
//000000: PUSH1 0x01
|
||||
//000002: PUSH1 0x00
|
||||
//000004: PUSH1 0x00
|
||||
//000006: PUSH1 0x00
|
||||
//000008: PUSH20 0x0200000000000000000000000000000000000000
|
||||
//000029: PUSH3 0x0186a0
|
||||
//000033: STATICCALL
|
||||
//000034: PUSH1 0x01
|
||||
//000036: PUSH1 0x00
|
||||
//000038: RETURN
|
||||
//000039: STOP
|
||||
callerContractCode = "6001600060006000730200000000000000000000000000000000000000620186a0fa60016000f300"
|
||||
)
|
||||
|
||||
func verifyStaticCall(t *testing.T, privateState *state.StateDB, publicState *state.StateDB, expectedHash common.Hash) {
|
||||
author := common.Address{}
|
||||
msg := callmsg{
|
||||
addr: author,
|
||||
to: &callerAddress,
|
||||
value: big.NewInt(1),
|
||||
gas: 1000000,
|
||||
gasPrice: new(big.Int),
|
||||
data: nil,
|
||||
}
|
||||
|
||||
ctx := NewEVMContext(msg, &dualStateTestHeader, nil, &author)
|
||||
env := vm.NewEVM(ctx, publicState, privateState, ¶ms.ChainConfig{
|
||||
ByzantiumBlock: new(big.Int),
|
||||
}, vm.Config{})
|
||||
|
||||
ret, _, err := env.Call(vm.AccountRef(author), callerAddress, msg.data, msg.gas, new(big.Int))
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Call error: %s", err)
|
||||
}
|
||||
value := common.Hash{ret[0]}
|
||||
if value != expectedHash {
|
||||
t.Errorf("expected %x got %x", expectedHash, value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStaticCall_whenPublicToPublic(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
publicState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
publicState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode))
|
||||
publicState.SetCode(calleeAddress, common.Hex2Bytes(calleeContractCode))
|
||||
|
||||
verifyStaticCall(t, publicState, publicState, common.Hash{10})
|
||||
}
|
||||
|
||||
func TestStaticCall_whenPublicToPrivateInTheParty(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
privateState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
privateState.SetCode(calleeAddress, common.Hex2Bytes(calleeContractCode))
|
||||
|
||||
publicState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
publicState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode))
|
||||
|
||||
verifyStaticCall(t, privateState, publicState, common.Hash{10})
|
||||
}
|
||||
|
||||
func TestStaticCall_whenPublicToPrivateNotInTheParty(t *testing.T) {
|
||||
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
privateState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
|
||||
publicState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
publicState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode))
|
||||
|
||||
verifyStaticCall(t, privateState, publicState, common.Hash{0})
|
||||
}
|
||||
|
||||
func TestStaticCall_whenPrivateToPublic(t *testing.T) {
|
||||
db := ethdb.NewMemDatabase()
|
||||
|
||||
privateState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
privateState.SetCode(callerAddress, common.Hex2Bytes(callerContractCode))
|
||||
|
||||
publicState, _ := state.New(common.Hash{}, state.NewDatabase(db))
|
||||
publicState.SetCode(calleeAddress, common.Hex2Bytes(calleeContractCode))
|
||||
|
||||
verifyStaticCall(t, privateState, publicState, common.Hash{10})
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ func (st *StateTransition) TransitionDb() (ret []byte, usedGas uint64, failed bo
|
|||
ret, st.gas, vmerr = evm.Call(sender, to, data, st.gas, st.value)
|
||||
}
|
||||
if vmerr != nil {
|
||||
log.Debug("VM returned with error", "err", vmerr)
|
||||
log.Info("VM returned with error", "err", vmerr)
|
||||
// The only possible consensus-error would be if there wasn't
|
||||
// sufficient balance to make the transfer happen. The first
|
||||
// balance transfer may never fail.
|
||||
|
|
|
@ -63,6 +63,14 @@ func (b *Bloom) Add(d *big.Int) {
|
|||
b.SetBytes(bin.Bytes())
|
||||
}
|
||||
|
||||
// OrBloom executes an Or operation on the bloom
|
||||
func (b *Bloom) OrBloom(bl []byte) {
|
||||
bin := new(big.Int).SetBytes(b[:])
|
||||
input := new(big.Int).SetBytes(bl[:])
|
||||
bin.Or(bin, input)
|
||||
b.SetBytes(bin.Bytes())
|
||||
}
|
||||
|
||||
// Big converts b to a big integer.
|
||||
func (b Bloom) Big() *big.Int {
|
||||
return new(big.Int).SetBytes(b[:])
|
||||
|
|
|
@ -489,6 +489,9 @@ func (tx *Transaction) IsPrivate() bool {
|
|||
}
|
||||
|
||||
func (tx *Transaction) SetPrivate() {
|
||||
if tx.IsPrivate() {
|
||||
return
|
||||
}
|
||||
if tx.data.V.Int64() == 28 {
|
||||
tx.data.V.SetUint64(38)
|
||||
} else {
|
||||
|
|
|
@ -374,6 +374,7 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
if evm.vmConfig.NoRecursion && evm.depth > 0 {
|
||||
return nil, gas, nil
|
||||
}
|
||||
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
@ -381,20 +382,21 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
|
|||
|
||||
var (
|
||||
to = AccountRef(addr)
|
||||
snapshot = evm.StateDB.Snapshot()
|
||||
stateDb = getDualState(evm, addr)
|
||||
snapshot = stateDb.Snapshot()
|
||||
)
|
||||
// Initialise a new contract and set the code that is to be used by the
|
||||
// EVM. The contract is a scoped environment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, new(big.Int), gas)
|
||||
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
|
||||
contract.SetCallCode(&addr, stateDb.GetCodeHash(addr), stateDb.GetCode(addr))
|
||||
|
||||
// When an error was returned by the EVM or when setting the creation code
|
||||
// above we revert to the snapshot and consume any gas remaining. Additionally
|
||||
// when we're in Homestead this also counts for code storage gas errors.
|
||||
ret, err = run(evm, contract, input, true)
|
||||
if err != nil {
|
||||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
stateDb.RevertToSnapshot(snapshot)
|
||||
if err != errExecutionReverted {
|
||||
contract.UseGas(contract.Gas)
|
||||
}
|
||||
|
|
|
@ -204,7 +204,6 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
|
||||
// Get the operation from the jump table and validate the stack to ensure there are
|
||||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc)
|
||||
operation := in.cfg.JumpTable[op]
|
||||
if !operation.valid {
|
||||
return nil, fmt.Errorf("invalid opcode 0x%x", int(op))
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
* [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
|
||||
|
|
51
docs/api.md
51
docs/api.md
|
@ -3,6 +3,8 @@
|
|||
|
||||
## Privacy APIs
|
||||
|
||||
#### eth.sendTransaction
|
||||
|
||||
__To support private transactions in Quorum, the `web3.eth.sendTransaction(object)` API method has been modified.__
|
||||
|
||||
```js
|
||||
|
@ -50,6 +52,55 @@ web3.eth.sendTransaction({
|
|||
```
|
||||
***
|
||||
|
||||
#### eth.sendRawPrivateTransaction
|
||||
|
||||
__To support sending raw transactions in Quorum, the `web3.eth.sendRawPrivateTransaction(string, object)` API method has been created.__
|
||||
|
||||
```js
|
||||
web3.eth.sendRawPrivateTransaction(signedTransactionData [, privateData] [, callback])
|
||||
```
|
||||
|
||||
Sends a pre-signed transaction. For example can be signed using: https://github.com/SilentCicero/ethereumjs-accounts
|
||||
|
||||
__Important:__ Please note that before calling this API, a `storeraw` api need to be called first to Quorum's private transaction manager. Instructions on how to do this can be found [here](https://github.com/jpmorganchase/tessera/wiki/Interface-&-API).
|
||||
|
||||
##### Parameters
|
||||
1. `String` - Signed transaction data in HEX format
|
||||
2. `Object` - Private data to send
|
||||
- `privateFor`: `List<String>` - When sending a private transaction, an array of the recipients' base64-encoded public keys.
|
||||
3. `Function` - (optional) If you pass a callback the HTTP request is made asynchronous. See [this note](#using-callbacks) for details.
|
||||
|
||||
##### Returns
|
||||
`String` - The 32 Bytes transaction hash as HEX string.
|
||||
If the transaction was a contract creation use [web3.eth.getTransactionReceipt()](#web3ethgettransactionreceipt) to get the contract address, after the transaction was mined.
|
||||
|
||||
|
||||
##### Example
|
||||
```js
|
||||
var Tx = require('ethereumjs-tx');
|
||||
var privateKey = new Buffer('e331b6d69882b4cb4ea581d88e0b604039a3de5967688d3dcffdd2270c0fd109', 'hex')
|
||||
var rawTx = {
|
||||
nonce: '0x00',
|
||||
gasPrice: '0x09184e72a000',
|
||||
gasLimit: '0x2710',
|
||||
to: '0x0000000000000000000000000000000000000000',
|
||||
value: '0x00',
|
||||
// This data should be the hex value of the hash returned by Quorum's privacy transaction manager after invoking storeraw api
|
||||
data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057'
|
||||
}
|
||||
var tx = new Tx(rawTx);
|
||||
tx.sign(privateKey);
|
||||
var serializedTx = tx.serialize();
|
||||
//console.log(serializedTx.toString('hex'));
|
||||
//f889808609184e72a00082271094000000000000000000000000000000000000000080a47f74657374320000000000000000000000000000000000000000000000000000006000571ca08a8bbf888cfa37bbf0bb965423625641fc956967b81d12e23709cead01446075a01ce999b56a8a88504be365442ea61239198e23d1fce7d00fcfc5cd3b44b7215f
|
||||
web3.eth.sendRawPrivateTransaction('0x' + serializedTx.toString('hex'), {privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}, function(err, hash) {
|
||||
if (!err)
|
||||
console.log(hash); // "0x7f9fade1c0d57a7af66ab4ead79fade1c0d57a7af66ab4ead7c2c2eb7b11a91385"
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
|
||||
## JSON RPC Privacy API Reference
|
||||
|
||||
__In addition to the JSON-RPC provided by Ethereum, Quorum exposes below two API calls.__
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
# Istanbul RPC API
|
||||
This is an up to date copy of original wiki entry located here https://github.com/getamis/go-ethereum/wiki/RPC-API
|
||||
|
||||
|
||||
# Getting Started
|
||||
1. Run Istanbul geth with `--rpcapi "istanbul"`
|
||||
2. `geth attach`
|
||||
|
||||
## API Reference
|
||||
|
||||
### istanbul.candidates
|
||||
Candidates returns the current candidates which the node tries to vote in or out.
|
||||
```
|
||||
istanbul.candidates
|
||||
```
|
||||
|
||||
#### Returns
|
||||
`map[string] boolean` - returns the current candidates map.
|
||||
|
||||
### istanbul.discard
|
||||
Discard drops a currently running candidate, stopping the validator from casting further votes (either for or against).
|
||||
```
|
||||
istanbul.discard(address)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
`string` - the address of the candidate
|
||||
|
||||
### istanbul.getSnapshot
|
||||
GetSnapshot retrieves the state snapshot at a given block.
|
||||
```
|
||||
istanbul.getSnapshot(blockHashOrBlockNumber)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
`String|Number` - The block number, the string "latest" or nil. nil is the same with string "latest" and means the latest block
|
||||
|
||||
#### Returns
|
||||
`Object` - The snapshot object
|
||||
|
||||
### istanbul.getSnapshotAtHash
|
||||
GetSnapshotAtHash retrieves the state snapshot at a given block.
|
||||
```
|
||||
istanbul.getSnapshotAtHash(blockHash)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
`String` - The block hash
|
||||
|
||||
#### Returns
|
||||
`Object` - The snapshot object
|
||||
|
||||
### istanbul.getValidators
|
||||
GetValidators retrieves the list of authorized validators at the specified block.
|
||||
```
|
||||
istanbul.getValidators(blockHashOrBlockNumber)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
`String|Number` - The block number, the string "latest" or nil. nil is the same with string "latest" and means the latest block
|
||||
|
||||
#### Returns
|
||||
`[]string` - The validator address array
|
||||
|
||||
### istanbul.getValidatorsAtHash
|
||||
GetValidatorsAtHash retrieves the list of authorized validators at the specified block.
|
||||
```
|
||||
istanbul.getValidatorsAtHash(blockHash)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
`String` - The block hash
|
||||
|
||||
#### Returns
|
||||
`[]string` - The validator address array
|
||||
|
||||
### istanbul.propose
|
||||
Propose injects a new authorization candidate that the validator will attempt to push through. If the number of vote is larger than 1/2 of validators to vote in/out, the candidate will be added/removed in validator set.
|
||||
|
||||
```
|
||||
istanbul.propose(address, auth)
|
||||
```
|
||||
|
||||
#### Parameters
|
||||
`String` - The address of candidate
|
||||
`bool` - `true` votes in and `false` votes out
|
|
@ -153,7 +153,19 @@ func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state vm.M
|
|||
vmError := func() error { return nil }
|
||||
|
||||
context := core.NewEVMContext(msg, header, b.eth.BlockChain(), nil)
|
||||
return vm.NewEVM(context, statedb.state, statedb.privateState, b.eth.chainConfig, vmCfg), vmError, nil
|
||||
|
||||
// Set the private state to public state if contract address is not present in the private state
|
||||
to := common.Address{}
|
||||
if msg.To() != nil {
|
||||
to = *msg.To()
|
||||
}
|
||||
|
||||
privateState := statedb.privateState
|
||||
if !privateState.Exist(to) {
|
||||
privateState = statedb.state
|
||||
}
|
||||
|
||||
return vm.NewEVM(context, statedb.state, privateState, b.eth.chainConfig, vmCfg), vmError, nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
|
||||
|
|
|
@ -421,12 +421,13 @@ func (s *Ethereum) shouldPreserve(block *types.Block) bool {
|
|||
// SetEtherbase sets the mining reward address.
|
||||
func (s *Ethereum) SetEtherbase(etherbase common.Address) {
|
||||
s.lock.Lock()
|
||||
defer s.lock.Unlock()
|
||||
if _, ok := s.engine.(consensus.Istanbul); ok {
|
||||
log.Error("Cannot set etherbase in Istanbul consensus")
|
||||
return
|
||||
}
|
||||
s.etherbase = etherbase
|
||||
s.lock.Unlock()
|
||||
|
||||
|
||||
s.miner.SetEtherbase(etherbase)
|
||||
}
|
||||
|
|
|
@ -115,10 +115,14 @@ func (b *BloomIndexer) Reset(ctx context.Context, section uint64, lastSectionHea
|
|||
return err
|
||||
}
|
||||
|
||||
// Process implements core.ChainIndexerBackend, adding a new header's bloom into
|
||||
// the index.
|
||||
// Process implements core.ChainIndexerBackend, executes an Or operation on header.bloom and private bloom
|
||||
// (header.bloom | private bloom) and adds to index
|
||||
func (b *BloomIndexer) Process(ctx context.Context, header *types.Header) error {
|
||||
b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), header.Bloom)
|
||||
publicBloom := header.Bloom
|
||||
privateBloom := core.GetPrivateBlockBloom(b.db, header.Number.Uint64())
|
||||
publicBloom.OrBloom(privateBloom.Bytes())
|
||||
|
||||
b.gen.AddBloom(uint(header.Number.Uint64()-b.section*b.size), publicBloom)
|
||||
b.head = header.Hash()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1174,6 +1174,11 @@ type SendTxArgs struct {
|
|||
//End-Quorum
|
||||
}
|
||||
|
||||
// SendRawTxArgs represents the arguments to submit a new signed private transaction into the transaction pool.
|
||||
type SendRawTxArgs struct {
|
||||
PrivateFor []string `json:"privateFor"`
|
||||
}
|
||||
|
||||
// setDefaults is a helper function that fills in default values for unspecified tx fields.
|
||||
func (args *SendTxArgs) setDefaults(ctx context.Context, b Backend) error {
|
||||
if args.Gas == nil {
|
||||
|
@ -1315,6 +1320,35 @@ func (s *PublicTransactionPoolAPI) SendRawTransaction(ctx context.Context, encod
|
|||
return submitTransaction(ctx, s.b, tx, tx.IsPrivate())
|
||||
}
|
||||
|
||||
// SendRawPrivateTransaction will add the signed transaction to the transaction pool.
|
||||
// The sender is responsible for signing the transaction and using the correct nonce.
|
||||
func (s *PublicTransactionPoolAPI) SendRawPrivateTransaction(ctx context.Context, encodedTx hexutil.Bytes, args SendRawTxArgs) (common.Hash, error) {
|
||||
|
||||
tx := new(types.Transaction)
|
||||
if err := rlp.DecodeBytes(encodedTx, tx); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
txHash := []byte(tx.Data())
|
||||
isPrivate := args.PrivateFor != nil
|
||||
|
||||
if isPrivate {
|
||||
if len(txHash) > 0 {
|
||||
//Send private transaction to privacy manager
|
||||
log.Info("sending private tx", "data", fmt.Sprintf("%x", txHash), "privatefor", args.PrivateFor)
|
||||
result, err := private.P.SendSignedTx(txHash, args.PrivateFor)
|
||||
log.Info("sent private tx", "result", fmt.Sprintf("%x", result), "privatefor", args.PrivateFor)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return common.Hash{}, fmt.Errorf("transaction is not private")
|
||||
}
|
||||
|
||||
return submitTransaction(ctx, s.b, tx, isPrivate)
|
||||
}
|
||||
|
||||
// Sign calculates an ECDSA signature for:
|
||||
// keccack256("\x19Ethereum Signed Message:\n" + len(message) + message).
|
||||
//
|
||||
|
|
|
@ -435,6 +435,12 @@ const Eth_JS = `
|
|||
web3._extend({
|
||||
property: 'eth',
|
||||
methods: [
|
||||
new web3._extend.Method({
|
||||
name: 'sendRawPrivateTransaction',
|
||||
call: 'eth_sendRawPrivateTransaction',
|
||||
params: 2,
|
||||
inputFormatter: [null, null]
|
||||
}),
|
||||
new web3._extend.Method({
|
||||
name: 'sign',
|
||||
call: 'eth_sign',
|
||||
|
|
|
@ -18,7 +18,17 @@ package params
|
|||
|
||||
import "math/big"
|
||||
|
||||
var (
|
||||
TargetGasLimit = GenesisGasLimit // The artificial target
|
||||
|
||||
)
|
||||
|
||||
const (
|
||||
// these are original values from upstream Geth, used in ethash consensus
|
||||
OriginnalMinGasLimit uint64 = 5000 // The bound divisor of the gas limit, used in update calculations.
|
||||
OriginalGasLimitBoundDivisor uint64 = 1024 // Minimum the gas limit may ever be.
|
||||
|
||||
// modified values for Quorum
|
||||
GasLimitBoundDivisor uint64 = 4096 // The bound divisor of the gas limit, used in update calculations.
|
||||
MinGasLimit uint64 = 700000000 // Minimum the gas limit may ever be.
|
||||
GenesisGasLimit uint64 = 800000000 // Gas limit of the Genesis block.
|
||||
|
|
|
@ -27,7 +27,7 @@ const (
|
|||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
|
||||
QuorumVersionMajor = 2
|
||||
QuorumVersionMinor = 1
|
||||
QuorumVersionMinor = 2
|
||||
QuorumVersionPatch = 1
|
||||
)
|
||||
|
||||
|
|
|
@ -33,6 +33,18 @@ func (g *Constellation) Send(data []byte, from string, to []string) (out []byte,
|
|||
return out, nil
|
||||
}
|
||||
|
||||
func (g *Constellation) SendSignedTx(data []byte, to []string) (out []byte, err error) {
|
||||
if g.isConstellationNotInUse {
|
||||
return nil, ErrConstellationIsntInit
|
||||
}
|
||||
out, err = g.node.SendSignedPayload(data, to)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
|
||||
func (g *Constellation) Receive(data []byte) ([]byte, error) {
|
||||
if g.isConstellationNotInUse {
|
||||
return nil, nil
|
||||
|
|
|
@ -106,6 +106,30 @@ func (c *Client) SendPayload(pl []byte, b64From string, b64To []string) ([]byte,
|
|||
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 {
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
type PrivateTransactionManager interface {
|
||||
Send(data []byte, from string, to []string) ([]byte, error)
|
||||
SendSignedTx(data []byte, to []string) ([]byte, error)
|
||||
Receive(data []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue