Merge remote-tracking branch 'quorum/master' into geth-upgrade-1.8.16

This commit is contained in:
amalraj.manigmail.com 2018-11-29 16:57:27 +08:00
commit 519337a9d0
29 changed files with 969 additions and 326 deletions

View File

@ -1,7 +1,3 @@
**/.git
.git
!.git/HEAD
!.git/refs/heads
**/*_test.go
build/_workspace

View File

@ -10,226 +10,19 @@ matrix:
sudo: required
go: 1.9.x
script:
- sudo -E apt-add-repository -y "ppa:chris-lea/libsodium"
- sudo apt-get update
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse libdb-dev libleveldb-dev zlib1g-dev libtinfo-dev libsodium-dev
- sudo cp /usr/lib/x86_64-linux-gnu/libsodium.so.13 /usr/lib/x86_64-linux-gnu/libsodium.so.18
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- ./build/install-constellation-linux.sh
- export PATH=$PATH:$(pwd)/constellation-0.3.2-ubuntu1604
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: linux
dist: trusty
sudo: required
go: 1.10.x
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
# These are the latest Go versions.
- os: linux
dist: trusty
sudo: required
go: 1.11.x
script:
- sudo -E apt-add-repository -y "ppa:chris-lea/libsodium"
- sudo apt-get update
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install fuse libdb-dev libleveldb-dev zlib1g-dev libtinfo-dev libsodium-dev
- sudo cp /usr/lib/x86_64-linux-gnu/libsodium.so.13 /usr/lib/x86_64-linux-gnu/libsodium.so.18
- sudo modprobe fuse
- sudo chmod 666 /dev/fuse
- sudo chown root:$USER /etc/fuse.conf
- ./build/install-constellation-linux.sh
- export PATH=$PATH:$(pwd)/constellation-0.3.2-ubuntu1604
- go run build/ci.go install
- go run build/ci.go test -coverage
- os: osx
go: 1.11.x
script:
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- brew update
- brew cask install osxfuse
- brew install berkeley-db leveldb libsodium haskell-stack
- ./build/install-constellation-mac.sh
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
# This builder only tests code linters on latest version of Go
- os: linux
dist: trusty
go: 1.11.x
env:
- lint
git:
submodules: false # avoid cloning ethereum/tests
script:
- go run build/ci.go lint
# This builder does the Ubuntu PPA upload
- os: linux
dist: trusty
go: 1.11.x
env:
- ubuntu-ppa
git:
submodules: false # avoid cloning ethereum/tests
addons:
apt:
packages:
- devscripts
- debhelper
- dput
- fakeroot
script:
- go run build/ci.go debsrc -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>" -upload ppa:ethereum/ethereum
# This builder does the Linux Azure uploads
- os: linux
dist: trusty
sudo: required
go: 1.11.x
env:
- azure-linux
git:
submodules: false # avoid cloning ethereum/tests
addons:
apt:
packages:
- gcc-multilib
script:
# Build for the primary platforms that Trusty can manage
- go run build/ci.go install
- go run build/ci.go archive -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go install -arch 386
- go run build/ci.go archive -arch 386 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
# Switch over GCC to cross compilation (breaks 386, hence why do it here only)
- sudo -E apt-get -yq --no-install-suggests --no-install-recommends --force-yes install gcc-arm-linux-gnueabi libc6-dev-armel-cross gcc-arm-linux-gnueabihf libc6-dev-armhf-cross gcc-aarch64-linux-gnu libc6-dev-arm64-cross
- sudo ln -s /usr/include/asm-generic /usr/include/asm
- GOARM=5 go run build/ci.go install -arch arm -cc arm-linux-gnueabi-gcc
- GOARM=5 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- GOARM=6 go run build/ci.go install -arch arm -cc arm-linux-gnueabi-gcc
- GOARM=6 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- GOARM=7 go run build/ci.go install -arch arm -cc arm-linux-gnueabihf-gcc
- GOARM=7 go run build/ci.go archive -arch arm -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go install -arch arm64 -cc aarch64-linux-gnu-gcc
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
# This builder does the Linux Azure MIPS xgo uploads
- os: linux
dist: trusty
services:
- docker
go: 1.11.x
env:
- azure-linux-mips
git:
submodules: false # avoid cloning ethereum/tests
script:
- go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
- go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done
- go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done
- go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
- go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v
- for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done
- go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -upload gethstore/builds
# This builder does the Android Maven and Azure uploads
- os: linux
dist: trusty
addons:
apt:
packages:
- oracle-java8-installer
- oracle-java8-set-default
language: android
android:
components:
- platform-tools
- tools
- android-15
- android-19
- android-24
env:
- azure-android
- maven-android
git:
submodules: false # avoid cloning ethereum/tests
before_install:
- curl https://storage.googleapis.com/golang/go1.11.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go
- export GOPATH=$HOME/go
script:
# Build the Android archive and upload it to Maven Central and Azure
- curl https://dl.google.com/android/repository/android-ndk-r17b-linux-x86_64.zip -o android-ndk-r17b.zip
- unzip -q android-ndk-r17b.zip && rm android-ndk-r17b.zip
- mv android-ndk-r17b $HOME
- export ANDROID_NDK=$HOME/android-ndk-r17b
- mkdir -p $GOPATH/src/github.com/ethereum
- ln -s `pwd` $GOPATH/src/github.com/ethereum
- go run build/ci.go aar -signer ANDROID_SIGNING_KEY -deploy https://oss.sonatype.org -upload gethstore/builds
# This builder does the OSX Azure, iOS CocoaPods and iOS Azure uploads
- os: osx
go: 1.11.x
env:
- azure-osx
- azure-ios
- cocoapods-ios
git:
submodules: false # avoid cloning ethereum/tests
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 archive -type tar -signer OSX_SIGNING_KEY -upload gethstore/builds
# Build the iOS framework and upload it to CocoaPods and Azure
- gem uninstall cocoapods -a -x
- gem install cocoapods
- mv ~/.cocoapods/repos/master ~/.cocoapods/repos/master.bak
- sed -i '.bak' 's/repo.join/!repo.join/g' $(dirname `gem which cocoapods`)/cocoapods/sources_manager.rb
- if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then git clone --depth=1 https://github.com/CocoaPods/Specs.git ~/.cocoapods/repos/master && pod setup --verbose; fi
- xctool -version
- xcrun simctl list
# Workaround for https://github.com/golang/go/issues/23749
- export CGO_CFLAGS_ALLOW='-fmodules|-fblocks|-fobjc-arc'
- go run build/ci.go xcode -signer IOS_SIGNING_KEY -deploy trunk -upload gethstore/builds
# This builder does the Azure archive purges to avoid accumulating junk
- os: linux
dist: trusty
go: 1.11.x
env:
- azure-purge
git:
submodules: false # avoid cloning ethereum/tests
script:
- go run build/ci.go purge -store gethstore/builds -days 14
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/e09ccdce1048c5e03445
on_success: change
on_failure: always
- go run build/ci.go test -coverage $TEST_PACKAGES

View File

@ -1,7 +1,7 @@
# Build Geth in a stock Go builder container
FROM golang:1.11-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers
RUN apk add --no-cache make gcc musl-dev linux-headers git
ADD . /go-ethereum
RUN cd /go-ethereum && make geth bootnode

View File

@ -60,3 +60,53 @@ 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.
```

View File

@ -26,9 +26,12 @@ The above diagram is a high-level overview of the privacy architecture used by Q
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).
## Official Docker Containers
The official docker containers can be found under https://hub.docker.com/u/quorumengineering/
## See also
* [Quorum](https://github.com/jpmorganchase/quorum): this repository

View File

@ -258,3 +258,17 @@ func RegisterRaftService(stack *node.Node, ctx *cli.Context, cfg gethConfig, eth
}
}
// quorumValidateConsensus checks if a consensus was used. The node is killed if consensus was not used
func quorumValidateConsensus(stack *node.Node, isRaft bool) {
var ethereum *eth.Ethereum
err := stack.Service(&ethereum)
if err != nil {
utils.Fatalf("Error retrieving Ethereum service: %v", err)
}
if !isRaft && ethereum.ChainConfig().Istanbul == nil && ethereum.ChainConfig().Clique == nil {
utils.Fatalf("Consensus not specified. Exiting!!")
}
}

View File

@ -18,6 +18,7 @@ package main
import (
"crypto/rand"
"io/ioutil"
"math/big"
"os"
"path/filepath"
@ -31,18 +32,52 @@ import (
)
const (
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 istanbul:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
nodeKey = "b68c0338aa4b266bf38ebe84c6199ae9fac8b29f32998b3ed2fbeafebe8d65c9"
)
var genesis = `{
"config": {
"chainId": 2017,
"homesteadBlock": 1,
"eip150Block": 2,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 3,
"eip158Block": 3,
"istanbul": {
"epoch": 30000,
"policy": 0
}
},
"nonce": "0x0",
"timestamp": "0x0",
"gasLimit": "0x47b760",
"difficulty": "0x1",
"mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"491937757d1b26e29c507b8d4c0b233c2747e68d": {
"balance": "0x446c3b15f9926687d2c40534fdb564000000000000"
}
},
"number": "0x0",
"gasUsed": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
`
// Tests that a node embedded within a console can be started up properly and
// then terminated by closing the input stream.
func TestConsoleWelcome(t *testing.T) {
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
coinbase := "0x491937757d1b26e29c507b8d4c0b233c2747e68d"
datadir := setupIstanbul(t)
defer os.RemoveAll(datadir)
// Start a geth console, make sure it's cleaned up and terminate the console
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--datadir", datadir, "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--shh",
"console")
@ -72,19 +107,22 @@ at block: 0 ({{niltime}})
// Tests that a console can be attached to a running node via various means.
func TestIPCAttachWelcome(t *testing.T) {
// Configure the instance for IPC attachement
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
coinbase := "0x491937757d1b26e29c507b8d4c0b233c2747e68d"
var ipc string
datadir := setupIstanbul(t)
defer os.RemoveAll(datadir)
if runtime.GOOS == "windows" {
ipc = `\\.\pipe\geth` + strconv.Itoa(trulyRandInt(100000, 999999))
} else {
ws := tmpdir(t)
defer os.RemoveAll(ws)
ipc = filepath.Join(ws, "geth.ipc")
ipc = filepath.Join(datadir, "geth.ipc")
}
// Note: we need --shh because testAttachWelcome checks for default
// list of ipc modules and shh is included there.
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--datadir", datadir, "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
@ -95,10 +133,14 @@ func TestIPCAttachWelcome(t *testing.T) {
}
func TestHTTPAttachWelcome(t *testing.T) {
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
coinbase := "0x491937757d1b26e29c507b8d4c0b233c2747e68d"
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
datadir := setupIstanbul(t)
defer os.RemoveAll(datadir)
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--datadir", datadir, "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--rpc", "--rpcport", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
@ -109,11 +151,14 @@ func TestHTTPAttachWelcome(t *testing.T) {
}
func TestWSAttachWelcome(t *testing.T) {
coinbase := "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182"
coinbase := "0x491937757d1b26e29c507b8d4c0b233c2747e68d"
port := strconv.Itoa(trulyRandInt(1024, 65536)) // Yeah, sometimes this will fail, sorry :P
datadir := setupIstanbul(t)
defer os.RemoveAll(datadir)
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--datadir", datadir, "--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--ws", "--wsport", port)
time.Sleep(2 * time.Second) // Simple way to wait for the RPC endpoint to open
@ -161,3 +206,26 @@ func trulyRandInt(lo, hi int) int {
num, _ := rand.Int(rand.Reader, big.NewInt(int64(hi-lo)))
return int(num.Int64()) + lo
}
// setupIstanbul creates a temporary directory and copies nodekey and genesis.json.
// It initializes istanbul by calling geth init
func setupIstanbul(t *testing.T) string {
datadir := tmpdir(t)
gethPath := filepath.Join(datadir, "geth")
os.Mkdir(gethPath, 0700)
// Initialize the data directory with the custom genesis block
json := filepath.Join(datadir, "genesis.json")
if err := ioutil.WriteFile(json, []byte(genesis), 0600); err != nil {
t.Fatalf("failed to write genesis file: %v", err)
}
nodeKeyFile := filepath.Join(gethPath, "nodekey")
if err := ioutil.WriteFile(nodeKeyFile, []byte(nodeKey), 0600); err != nil {
t.Fatalf("failed to write nodekey file: %v", err)
}
runGeth(t, "--datadir", datadir, "init", json).WaitExit()
return datadir
}

View File

@ -275,6 +275,10 @@ func geth(ctx *cli.Context) error {
}
node := makeFullNode(ctx)
startNode(ctx, node)
// Check if a valid consensus is used
quorumValidateConsensus(node, ctx.GlobalBool(utils.RaftModeFlag.Name))
node.Wait()
return nil
}

View File

@ -103,15 +103,13 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
return nil
}
// CalcGasLimit computes the gas limit of the next block after parent. It aims
// to keep the baseline gas above the provided floor, and increase it towards the
// ceil if the blocks are full. If the ceil is exceeded, it will always decrease
// the gas allowance.
// CalcGasLimit computes the gas limit of the next block after parent.
// This is miner strategy, not consensus protocol.
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
// contrib = (parentGasUsed * 3 / 2) / 1024
// contrib = (parentGasUsed * 3 / 2) / 4096
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
// decay = parentGasLimit / 1024 -1
// decay = parentGasLimit / 4096 -1
decay := parent.GasLimit()/params.GasLimitBoundDivisor - 1
/*
@ -125,7 +123,8 @@ func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
if limit < params.MinGasLimit {
limit = params.MinGasLimit
}
// If we're outside our allowed gas range, we try to hone towards them
// however, if we're now below the target (TargetGasLimit) we increase the
// limit as much as we can (parentGasLimit / 4096 -1)
if limit < gasFloor {
limit = parent.GasLimit() + decay
if limit > gasFloor {

View File

@ -941,7 +941,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
}
triedb := bc.stateCache.TrieDB()
// Explicit commit for privateStateTriedb to handle Raft
// Explicit commit for privateStateTriedb to handle Raft db issues
if privateState != nil {
privateRoot, err := privateState.Commit(bc.chainConfig.IsEIP158(block.Number()))
if err != nil {

406
core/database_util_test.go Normal file
View File

@ -0,0 +1,406 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"bytes"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
)
// Tests block header storage and retrieval operations.
func TestHeaderStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
// Create a test header to move around the database and make sure it's really new
header := &types.Header{Number: big.NewInt(42), Extra: []byte("test header")}
if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
t.Fatalf("Non existent header returned: %v", entry)
}
// Write and verify the header in the database
if err := WriteHeader(db, header); err != nil {
t.Fatalf("Failed to write header into database: %v", err)
}
if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry == nil {
t.Fatalf("Stored header not found")
} else if entry.Hash() != header.Hash() {
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, header)
}
if entry := GetHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
t.Fatalf("Stored header RLP not found")
} else {
hasher := sha3.NewKeccak256()
hasher.Write(entry)
if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
t.Fatalf("Retrieved RLP header mismatch: have %v, want %v", entry, header)
}
}
// Delete the header and verify the execution
DeleteHeader(db, header.Hash(), header.Number.Uint64())
if entry := GetHeader(db, header.Hash(), header.Number.Uint64()); entry != nil {
t.Fatalf("Deleted header returned: %v", entry)
}
}
// Tests block body storage and retrieval operations.
func TestBodyStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
// Create a test body to move around the database and make sure it's really new
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
hasher := sha3.NewKeccak256()
rlp.Encode(hasher, body)
hash := common.BytesToHash(hasher.Sum(nil))
if entry := GetBody(db, hash, 0); entry != nil {
t.Fatalf("Non existent body returned: %v", entry)
}
// Write and verify the body in the database
if err := WriteBody(db, hash, 0, body); err != nil {
t.Fatalf("Failed to write body into database: %v", err)
}
if entry := GetBody(db, hash, 0); entry == nil {
t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
}
if entry := GetBodyRLP(db, hash, 0); entry == nil {
t.Fatalf("Stored body RLP not found")
} else {
hasher := sha3.NewKeccak256()
hasher.Write(entry)
if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
t.Fatalf("Retrieved RLP body mismatch: have %v, want %v", entry, body)
}
}
// Delete the body and verify the execution
DeleteBody(db, hash, 0)
if entry := GetBody(db, hash, 0); entry != nil {
t.Fatalf("Deleted body returned: %v", entry)
}
}
// Tests block storage and retrieval operations.
func TestBlockStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
// Create a test block to move around the database and make sure it's really new
block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash,
})
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry)
}
if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent header returned: %v", entry)
}
if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent body returned: %v", entry)
}
// Write and verify the block in the database
if err := WriteBlock(db, block); err != nil {
t.Fatalf("Failed to write block into database: %v", err)
}
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored block not found")
} else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
}
if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored header not found")
} else if entry.Hash() != block.Header().Hash() {
t.Fatalf("Retrieved header mismatch: have %v, want %v", entry, block.Header())
}
if entry := GetBody(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
}
// Delete the block and verify the execution
DeleteBlock(db, block.Hash(), block.NumberU64())
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Deleted block returned: %v", entry)
}
if entry := GetHeader(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Deleted header returned: %v", entry)
}
if entry := GetBody(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Deleted body returned: %v", entry)
}
}
// Tests that partial block contents don't get reassembled into full blocks.
func TestPartialBlockStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
block := types.NewBlockWithHeader(&types.Header{
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash,
})
// Store a header and check that it's not recognized as a block
if err := WriteHeader(db, block.Header()); err != nil {
t.Fatalf("Failed to write header into database: %v", err)
}
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry)
}
DeleteHeader(db, block.Hash(), block.NumberU64())
// Store a body and check that it's not recognized as a block
if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
t.Fatalf("Failed to write body into database: %v", err)
}
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry != nil {
t.Fatalf("Non existent block returned: %v", entry)
}
DeleteBody(db, block.Hash(), block.NumberU64())
// Store a header and a body separately and check reassembly
if err := WriteHeader(db, block.Header()); err != nil {
t.Fatalf("Failed to write header into database: %v", err)
}
if err := WriteBody(db, block.Hash(), block.NumberU64(), block.Body()); err != nil {
t.Fatalf("Failed to write body into database: %v", err)
}
if entry := GetBlock(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored block not found")
} else if entry.Hash() != block.Hash() {
t.Fatalf("Retrieved block mismatch: have %v, want %v", entry, block)
}
}
// Tests block total difficulty storage and retrieval operations.
func TestTdStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
// Create a test TD to move around the database and make sure it's really new
hash, td := common.Hash{}, big.NewInt(314)
if entry := GetTd(db, hash, 0); entry != nil {
t.Fatalf("Non existent TD returned: %v", entry)
}
// Write and verify the TD in the database
if err := WriteTd(db, hash, 0, td); err != nil {
t.Fatalf("Failed to write TD into database: %v", err)
}
if entry := GetTd(db, hash, 0); entry == nil {
t.Fatalf("Stored TD not found")
} else if entry.Cmp(td) != 0 {
t.Fatalf("Retrieved TD mismatch: have %v, want %v", entry, td)
}
// Delete the TD and verify the execution
DeleteTd(db, hash, 0)
if entry := GetTd(db, hash, 0); entry != nil {
t.Fatalf("Deleted TD returned: %v", entry)
}
}
// Tests that canonical numbers can be mapped to hashes and retrieved.
func TestCanonicalMappingStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
// Create a test canonical number and assinged hash to move around
hash, number := common.Hash{0: 0xff}, uint64(314)
if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
t.Fatalf("Non existent canonical mapping returned: %v", entry)
}
// Write and verify the TD in the database
if err := WriteCanonicalHash(db, hash, number); err != nil {
t.Fatalf("Failed to write canonical mapping into database: %v", err)
}
if entry := GetCanonicalHash(db, number); entry == (common.Hash{}) {
t.Fatalf("Stored canonical mapping not found")
} else if entry != hash {
t.Fatalf("Retrieved canonical mapping mismatch: have %v, want %v", entry, hash)
}
// Delete the TD and verify the execution
DeleteCanonicalHash(db, number)
if entry := GetCanonicalHash(db, number); entry != (common.Hash{}) {
t.Fatalf("Deleted canonical mapping returned: %v", entry)
}
}
// Tests that head headers and head blocks can be assigned, individually.
func TestHeadStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
blockHead := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block header")})
blockFull := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block full")})
blockFast := types.NewBlockWithHeader(&types.Header{Extra: []byte("test block fast")})
// Check that no head entries are in a pristine database
if entry := GetHeadHeaderHash(db); entry != (common.Hash{}) {
t.Fatalf("Non head header entry returned: %v", entry)
}
if entry := GetHeadBlockHash(db); entry != (common.Hash{}) {
t.Fatalf("Non head block entry returned: %v", entry)
}
if entry := GetHeadFastBlockHash(db); entry != (common.Hash{}) {
t.Fatalf("Non fast head block entry returned: %v", entry)
}
// Assign separate entries for the head header and block
if err := WriteHeadHeaderHash(db, blockHead.Hash()); err != nil {
t.Fatalf("Failed to write head header hash: %v", err)
}
if err := WriteHeadBlockHash(db, blockFull.Hash()); err != nil {
t.Fatalf("Failed to write head block hash: %v", err)
}
if err := WriteHeadFastBlockHash(db, blockFast.Hash()); err != nil {
t.Fatalf("Failed to write fast head block hash: %v", err)
}
// Check that both heads are present, and different (i.e. two heads maintained)
if entry := GetHeadHeaderHash(db); entry != blockHead.Hash() {
t.Fatalf("Head header hash mismatch: have %v, want %v", entry, blockHead.Hash())
}
if entry := GetHeadBlockHash(db); entry != blockFull.Hash() {
t.Fatalf("Head block hash mismatch: have %v, want %v", entry, blockFull.Hash())
}
if entry := GetHeadFastBlockHash(db); entry != blockFast.Hash() {
t.Fatalf("Fast head block hash mismatch: have %v, want %v", entry, blockFast.Hash())
}
}
// Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22})
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
// Check that no transactions entries are in a pristine database
for i, tx := range txs {
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn)
}
}
// Insert all the transactions into the database, and verify contents
if err := WriteBlock(db, block); err != nil {
t.Fatalf("failed to write block contents: %v", err)
}
if err := WriteTxLookupEntries(db, block); err != nil {
t.Fatalf("failed to write transactions: %v", err)
}
for i, tx := range txs {
if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil {
t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash())
} else {
if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) {
t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i)
}
if tx.String() != txn.String() {
t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx)
}
}
}
// Delete the transactions and check purge
for i, tx := range txs {
DeleteTxLookupEntry(db, tx.Hash())
if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil {
t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn)
}
}
}
// Tests that receipts associated with a single block can be stored and retrieved.
func TestBlockReceiptStorage(t *testing.T) {
db := ethdb.NewMemDatabase()
receipt1 := &types.Receipt{
Status: types.ReceiptStatusFailed,
CumulativeGasUsed: 1,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x11})},
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
},
TxHash: common.BytesToHash([]byte{0x11, 0x11}),
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
GasUsed: 111111,
}
receipt2 := &types.Receipt{
PostState: common.Hash{2}.Bytes(),
CumulativeGasUsed: 2,
Logs: []*types.Log{
{Address: common.BytesToAddress([]byte{0x22})},
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
},
TxHash: common.BytesToHash([]byte{0x22, 0x22}),
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
GasUsed: 222222,
}
receipts := []*types.Receipt{receipt1, receipt2}
// Check that no receipt entries are in a pristine database
hash := common.BytesToHash([]byte{0x03, 0x14})
if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
t.Fatalf("non existent receipts returned: %v", rs)
}
// Insert the receipt slice into the database and check presence
if err := WriteBlockReceipts(db, hash, 0, receipts); err != nil {
t.Fatalf("failed to write block receipts: %v", err)
}
if rs := GetBlockReceipts(db, hash, 0); len(rs) == 0 {
t.Fatalf("no receipts returned")
} else {
for i := 0; i < len(receipts); i++ {
rlpHave, _ := rlp.EncodeToBytes(rs[i])
rlpWant, _ := rlp.EncodeToBytes(receipts[i])
if !bytes.Equal(rlpHave, rlpWant) {
t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
}
}
}
// Delete the receipt slice and check purge
DeleteBlockReceipts(db, hash, 0)
if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
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

@ -318,21 +318,4 @@ func TestBlockReceiptStorage(t *testing.T) {
}
}
// 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

@ -578,7 +578,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrInvalidGasPrice
}
// Heuristic limit, reject transactions over 32KB to prevent DOS attacks
if tx.Size() > 32*1024 {
// UPDATED to 64KB to support the deployment of bigger contract due to the pressing need for sophisticated/complex contract in financial/capital markets - Nathan Aw
if tx.Size() > 64*1024 {
return ErrOversizedData
}
// Transactions can't be negative. This may never happen using RLP decoded
@ -605,8 +606,8 @@ func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
return ErrNonceTooLow
}
// Ether value is not currently supported on private transactions
if tx.IsPrivate() && (tx.Value().Sign() != 0) {
return ErrEtherValueUnsupported;
if tx.IsPrivate() && (len(tx.Data()) == 0 || tx.Value().Sign() != 0) {
return ErrEtherValueUnsupported
}
// Transactor should have enough funds to cover the costs
// cost == V + GP * GL

View File

@ -19,8 +19,14 @@ package core
import (
"crypto/ecdsa"
"fmt"
"io/ioutil"
"math/big"
"math/rand"
"os"
"reflect"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
@ -28,11 +34,6 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"math/rand"
"time"
"io/ioutil"
"os"
"reflect"
)
// testTxPoolConfig is a transaction pool configuration without stateful disk
@ -277,8 +278,8 @@ func TestInvalidTransactions(t *testing.T) {
t.Error("expected", ErrGasLimit, "; got", err)
}
data := make([]byte, (32*1024)+1)
tx2, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100),100000, big.NewInt(1), data), types.HomesteadSigner{}, key)
data := make([]byte, (64*1024)+1)
tx2, _ := types.SignTx(types.NewTransaction(2, common.Address{}, big.NewInt(100), 100000, big.NewInt(1), data), types.HomesteadSigner{}, key)
if err := pool.AddRemote(tx2); err != ErrOversizedData {
t.Error("expected", ErrOversizedData, "; got", err)
}
@ -307,6 +308,54 @@ func TestQuorumInvalidTransactions(t *testing.T) {
}
func TestValidateTx_whenValueZeroTransferForPrivateTransaction(t *testing.T) {
pool, key := setupQuorumTxPool()
defer pool.Stop()
zeroValue := common.Big0
zeroGasPrice := common.Big0
defaultTxPoolGasLimit := uint64(1000000)
arbitraryTx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, zeroValue, defaultTxPoolGasLimit, zeroGasPrice, nil), types.HomesteadSigner{}, key)
arbitraryTx.SetPrivate()
if err := pool.AddRemote(arbitraryTx); err != ErrEtherValueUnsupported {
t.Error("expected:", ErrEtherValueUnsupported, "; got:", err)
}
}
func TestValidateTx_whenValueNonZeroTransferForPrivateTransaction(t *testing.T) {
pool, key := setupQuorumTxPool()
defer pool.Stop()
arbitraryValue := common.Big3
arbitraryTx, balance, from := newPrivateTransaction(arbitraryValue, nil, key)
pool.currentState.AddBalance(from, balance)
if err := pool.AddRemote(arbitraryTx); err != ErrEtherValueUnsupported {
t.Error("expected: ", ErrEtherValueUnsupported, "; got:", err)
}
}
func newPrivateTransaction(value *big.Int, data []byte, key *ecdsa.PrivateKey) (*types.Transaction, *big.Int, common.Address) {
zeroGasPrice := common.Big0
defaultTxPoolGasLimit := uint64(1000000)
arbitraryTx, _ := types.SignTx(types.NewTransaction(0, common.Address{}, value, defaultTxPoolGasLimit, zeroGasPrice, data), types.HomesteadSigner{}, key)
arbitraryTx.SetPrivate()
balance := new(big.Int).Add(arbitraryTx.Value(), new(big.Int).Mul(new(big.Int).SetUint64(arbitraryTx.Gas()), arbitraryTx.GasPrice()))
from, _ := deriveSender(arbitraryTx)
return arbitraryTx, balance, from
}
func TestValidateTx_whenValueNonZeroWithSmartContractForPrivateTransaction(t *testing.T) {
pool, key := setupQuorumTxPool()
defer pool.Stop()
arbitraryValue := common.Big3
arbitraryTx, balance, from := newPrivateTransaction(arbitraryValue, []byte("arbitrary bytecode"), key)
pool.currentState.AddBalance(from, balance)
if err := pool.AddRemote(arbitraryTx); err != ErrEtherValueUnsupported {
t.Error("expected: ", ErrEtherValueUnsupported, "; got:", err)
}
}
func TestTransactionQueue(t *testing.T) {
t.Parallel()
@ -1909,9 +1958,9 @@ func benchmarkPoolBatchInsert(b *testing.B, size int) {
//Checks that the EIP155 signer is assigned to the TxPool no matter the configuration, even invalid config
func TestEIP155SignerOnTxPool(t *testing.T) {
var flagtests = []struct {
name string
homesteadBlock *big.Int
eip155Block *big.Int
name string
homesteadBlock *big.Int
eip155Block *big.Int
}{
{"hsnileip155nil", nil, nil},
{"hsnileip1550", nil, big.NewInt(0)},
@ -1949,4 +1998,3 @@ func TestEIP155SignerOnTxPool(t *testing.T) {
}
}

View File

@ -543,7 +543,10 @@ func getDualState(env *EVM, addr common.Address) StateDB {
func (env *EVM) PublicState() PublicState { return env.publicState }
func (env *EVM) PrivateState() PrivateState { return env.privateState }
func (env *EVM) Push(statedb StateDB) {
if env.privateState != statedb {
// Quorum : the read only depth to be set up only once for the entire
// op code execution. This will be set first time transition from
// private state to public state happens
if !env.quorumReadOnly && env.privateState != statedb {
env.quorumReadOnly = true
env.readOnlyDepth = env.currentStateDepth
}

View File

@ -392,7 +392,10 @@ func opAddress(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
func opBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
slot := stack.peek()
slot.Set(interpreter.evm.StateDB.GetBalance(common.BigToAddress(slot)))
addr := common.BigToAddress(slot)
// Quorum: get public/private state db based on addr
balance := getDualState(interpreter.evm, addr).GetBalance(addr)
slot.Set(balance)
return nil, nil
}
@ -458,7 +461,9 @@ func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contrac
func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
slot := stack.peek()
slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(common.BigToAddress(slot))))
addr := common.BigToAddress(slot)
// Quorum: get public/private state db based on addr
slot.SetUint64(uint64(getDualState(interpreter.evm, addr).GetCodeSize(addr)))
return nil, nil
}
@ -490,7 +495,8 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, contract *Contract,
codeOffset = stack.pop()
length = stack.pop()
)
codeCopy := getDataBig(interpreter.evm.StateDB.GetCode(addr), codeOffset, length)
// Quorum: get public/private state db based on addr
codeCopy := getDataBig(getDualState(interpreter.evm, addr).GetCode(addr), codeOffset, length)
memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
interpreter.intPool.put(memOffset, codeOffset, length)
@ -604,7 +610,8 @@ func opMstore8(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memo
func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := stack.peek()
val := interpreter.evm.StateDB.GetState(contract.Address(), common.BigToHash(loc))
// Quorum: get public/private state db based on addr
val := getDualState(interpreter.evm, contract.Address()).GetState(contract.Address(), common.BigToHash(loc))
loc.SetBytes(val.Bytes())
return nil, nil
}
@ -612,7 +619,8 @@ func opSload(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
func opSstore(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
loc := common.BigToHash(stack.pop())
val := stack.pop()
interpreter.evm.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
// Quorum: get public/private state db based on addr
getDualState(interpreter.evm, contract.Address()).SetState(contract.Address(), loc, common.BigToHash(val))
interpreter.intPool.put(val)
return nil, nil
@ -855,10 +863,12 @@ func opStop(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
}
func opSuicide(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
balance := interpreter.evm.StateDB.GetBalance(contract.Address())
interpreter.evm.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
// Quorum: get public/private state db based on addr
db := getDualState(interpreter.evm, contract.Address())
balance := db.GetBalance(contract.Address())
db.AddBalance(common.BigToAddress(stack.pop()), balance)
interpreter.evm.StateDB.Suicide(contract.Address())
db.Suicide(contract.Address())
return nil, nil
}

View File

@ -61,7 +61,8 @@ Returns the storage root of given address (Contract/Account etc)
##### Parameters
address, block number (hex)
1. `address`: `String` - The address to fetch the storage root for in hex
2. `block`: `String` - (optional) The block number to look at in hex (e.g. `0x15` for block 21). Uses the latest block if not specified.
##### Returns
@ -99,7 +100,7 @@ curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc": "2.0", "method": "eth_st
// After private state of the contract is changed from '42' to '99'
// Request
curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc": "2.0", "method": "eth_storageRoot", "params":["0x1349f3e1b8d71effb47b840594ff27da7e603d17","0x2"], "id": 67}'
curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc": "2.0", "method": "eth_storageRoot", "params":["0x1349f3e1b8d71effb47b840594ff27da7e603d17", "0x2"], "id": 67}'
// Response
{
@ -117,7 +118,7 @@ Returns the unencrypted payload from Tessera/constellation
##### Parameters
Transaction payload hash in Hex format
1. `id`: `String` - the HEX formatted generated Sha3-512 hash of the encrypted payload from the Private Transaction Manager. This is seen in the transaction as the `input` field
##### Returns
@ -145,3 +146,110 @@ curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc":"2.0", "method":"eth_getQ
"result": "0x"
}
```
***
#### eth_sendTransactionAsync
Sends a transaction to the network asynchronously. This will return
immediately, potentially before the transaction has been submitted to the
transaction pool. A callback can be provided to receive the result of
submitting the transaction; a server must be set up to receive POST requests
at the given URL.
##### Parameters
1. `Object` - The transaction object to send:
- `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.
- `callbackUrl`: `String` - (optional) the URL to perform a POST request to to post the result of submitted the transaction
##### Returns
1. `String` - The empty hash, defined as `0x0000000000000000000000000000000000000000000000000000000000000000`
The callback URL receives the following object:
2. `Object` - The result object:
- `id`: `String` - the identifier in the original RPC call, used to match this result to the request
- `txHash`: `String` - the transaction hash that was generated, if successful
- `error`: `String` - the error that occurred whilst submitting the transaction.
If the transaction was a contract creation use `web3.eth.getTransactionReceipt()` to get the contract address, after the transaction was mined.
##### Example
For the RPC call and the immediate response:
```
// Request
curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc":"2.0", "method":"eth_sendTransactionAsync", "params":[{"from":"0xed9d02e382b34818e88b88a309c7fe71e65f419d", "data": "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029000000000000000000000000000000000000000000000000000000000000002a", "gas": "0x47b760", "privateFor": ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}], "id":67}'
// Response
{
"id": 67,
"jsonrpc": "2.0",
"result": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
// Request
curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc":"2.0", "method":"eth_sendTransactionAsync", "params":[{"from":"0xe2e382b3b8871e65f419d", "data": "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029000000000000000000000000000000000000000000000000000000000000002a", "gas": "0x47b760", "privateFor": ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}], "id":67}'
//If a syntactic error occured with the RPC call.
//In this example the wallet address is the wrong length
//so the error is it cannot convert the parameter to the correct type
//it is NOT an error relating the the address not being managed by this node.
//Response
{
"id": 67,
"jsonrpc": "2.0",
"error": {
"code": -32602,
"message": "invalid argument 0: json: cannot unmarshal hex string of odd length into Go struct field AsyncSendTxArgs.from of type common.Address"
}
}
```
If the callback URL is provided, the following response will be received after
the transaction has been submitted; this example assumes a webserver that can
be accessed by calling http://localhost:8080 has been set up to accept POST
requests:
```
// Request
curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc":"2.0", "method":"eth_sendTransactionAsync", "params":[{"from":"0xed9d02e382b34818e88b88a309c7fe71e65f419d", "data": "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029000000000000000000000000000000000000000000000000000000000000002a", "gas": "0x47b760", "privateFor": ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="], "callbackUrl": "http://localhost:8080"}], "id":67}'
// Response
//Note that the ID is the same in the callback as the request - this can be used to match the request to the response.
{
"id": 67,
"txHash": "0x75ebbf4fbe29355fc8a4b8d1e14ecddf0228b64ef41e6d2fce56047650e2bf17"
}
// Request
curl -X POST http://127.0.0.1:22000 --data '{"jsonrpc":"2.0", "method":"eth_sendTransactionAsync", "params":[{"from":"0xae9bc6cd5145e67fbd1887a5145271fd182f0ee7", "callbackUrl": "http://localhost:8080", "data": "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029000000000000000000000000000000000000000000000000000000000000002a", "gas": "0x47b760", "privateFor": ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc="]}], "id":67}'
//If a semantic error occured with the RPC call.
//In this example the wallet address is not managed by the node
//So the RPC call will succeed (giving the empty hash), but the callback will show a failure
// In the callback
{
"id": 67,
"error":"unknown account"
}
```

View File

@ -64,7 +64,7 @@ func testStatusMsgErrors(t *testing.T, protocol int) {
},
{
code: StatusMsg, data: statusData{uint32(protocol), 999, td, head.Hash(), genesis.Hash()},
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1)"),
wantError: errResp(ErrNetworkIdMismatch, "999 (!= 1337)"),
},
{
code: StatusMsg, data: statusData{uint32(protocol), DefaultConfig.NetworkId, td, head.Hash(), common.Hash{3}},

View File

@ -373,7 +373,9 @@ func (s *Service) login(conn *websocket.Conn) error {
infos := s.server.NodeInfo()
var network, protocol string
if info := infos.Protocols["eth"]; info != nil {
//must pass engine protocol name for quorum
p := s.engine.Protocol()
if info := infos.Protocols[p.Name]; info != nil {
network = fmt.Sprintf("%d", info.(*eth.NodeInfo).Network)
protocol = fmt.Sprintf("eth/%d", eth.ProtocolVersions[0])
} else {

View File

@ -28,7 +28,10 @@ import (
"encoding/hex"
"encoding/json"
"net/http"
"github.com/davecgh/go-spew/spew"
"github.com/davecgh/go-spew/spew"
"sync"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
@ -387,11 +390,13 @@ func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs
if isPrivate {
data := []byte(*args.Data)
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
data, err := private.P.Send(data, args.PrivateFrom, args.PrivateFor)
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
if err != nil {
return common.Hash{}, err
if len(data) > 0 {
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
data, err := private.P.Send(data, args.PrivateFrom, args.PrivateFor)
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
if err != nil {
return common.Hash{}, err
}
}
// zekun: HACK
d := hexutil.Bytes(data)
@ -1266,12 +1271,14 @@ func (s *PublicTransactionPoolAPI) SendTransaction(ctx context.Context, args Sen
log.Info("args.data is nil")
}
//Send private transaction to local Constellation node
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
data, err = private.P.Send(data, args.PrivateFrom, args.PrivateFor)
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
if err != nil {
return common.Hash{}, err
if len(data) > 0 {
//Send private transaction to local Constellation node
log.Info("sending private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
data, err = private.P.Send(data, args.PrivateFrom, args.PrivateFor)
log.Info("sent private tx", "data", fmt.Sprintf("%x", data), "privatefrom", args.PrivateFrom, "privatefor", args.PrivateFor)
if err != nil {
return common.Hash{}, err
}
}
// zekun: HACK
d := hexutil.Bytes(data)
@ -1359,6 +1366,9 @@ func (s *PublicTransactionPoolAPI) SignTransaction(ctx context.Context, args Sen
if err != nil {
return nil, err
}
if args.PrivateFor != nil {
tx.SetPrivate()
}
data, err := rlp.EncodeToBytes(tx)
if err != nil {
return nil, err
@ -1582,6 +1592,11 @@ type AsyncResultFailure struct {
Error string `json:"error"`
}
type Async struct {
sync.Mutex
sem chan struct{}
}
func (s *PublicTransactionPoolAPI) send(ctx context.Context, asyncArgs AsyncSendTxArgs) {
txHash, err := s.SendTransaction(ctx, asyncArgs.SendTxArgs)
@ -1615,6 +1630,14 @@ func (s *PublicTransactionPoolAPI) send(ctx context.Context, asyncArgs AsyncSend
}
func newAsync(n int) *Async {
a := &Async{
sem: make(chan struct{}, n),
}
return a
}
var async = newAsync(100)
// SendTransactionAsync creates a transaction for the given argument, signs it, and
// submits it to the transaction pool. This call returns immediately to allow sending
@ -1628,8 +1651,17 @@ func (s *PublicTransactionPoolAPI) send(ctx context.Context, asyncArgs AsyncSend
// environments when sending many private transactions. It will be removed at a later
// date when account management is handled outside Ethereum.
func (s *PublicTransactionPoolAPI) SendTransactionAsync(ctx context.Context, args AsyncSendTxArgs) (common.Hash, error){
go s.send(ctx, args)
return common.Hash{}, nil
select {
case async.sem <- struct{}{}:
go func() {
s.send(ctx, args)
<-async.sem
}()
return common.Hash{}, nil
default:
return common.Hash{}, errors.New("too many concurrent requests")
}
}
// GetQuorumPayload returns the contents of a private transaction
@ -1656,4 +1688,4 @@ func (s *PublicBlockChainAPI) GetQuorumPayload(digestHex string) (string, error)
}
return fmt.Sprintf("0x%x", data), nil
}
//End-Quorum
//End-Quorum

View File

@ -611,6 +611,12 @@ func (w *worker) resultLoop() {
log.Error("Failed writWriteBlockAndStating block to chain", "err", err)
continue
}
if err := core.WritePrivateBlockBloom(w.eth.ChainDb(), block.NumberU64(), work.privateReceipts); err != nil {
log.Error("Failed writing private block bloom", "err", err)
continue
}
log.Info("Successfully sealed new block", "number", block.Number(), "sealhash", sealhash, "hash", hash,
"elapsed", common.PrettyDuration(time.Since(task.createdAt)))

View File

@ -145,7 +145,7 @@ var incompleteNodeURL = regexp.MustCompile("(?i)^(?:enode://)?([0-9a-f]+)$")
//
// For complete nodes, the node ID is encoded in the username portion
// of the URL, separated from the host by an @ sign. The hostname can
// only be given as an IP address, DNS domain names are not allowed.
// be given as an IP address or a DNS domain name.
// The port in the host name section is the TCP listening port. If the
// TCP and UDP (discovery) ports differ, the UDP port is specified as
// query parameter "discport".
@ -192,7 +192,13 @@ func parseComplete(rawurl string) (*Node, error) {
return nil, fmt.Errorf("invalid host: %v", err)
}
if ip = net.ParseIP(host); ip == nil {
return nil, errors.New("invalid IP address")
// attempt to look up IP addresses if host is a FQDN
lookupIPs, err := net.LookupIP(host)
if err != nil {
return nil, errors.New("invalid IP address")
}
// set to first ip by default
ip = lookupIPs[0]
}
// Ensure the IP is 4 bytes long for IPv4 addresses.
if ipv4 := ip.To4(); ipv4 != nil {

View File

@ -30,7 +30,6 @@ func isNodePermissioned(nodename string, currentNode string, datadir string, dir
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "ALLOWED-BY", currentNode[:NODE_NAME_LENGTH])
return true
}
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH])
}
log.Debug("isNodePermissioned", "connection", direction, "nodename", nodename[:NODE_NAME_LENGTH], "DENIED-BY", currentNode[:NODE_NAME_LENGTH])
return false

View File

@ -19,9 +19,9 @@ package params
import "math/big"
const (
GasLimitBoundDivisor uint64 = 1024 // The bound divisor of the gas limit, used in update calculations.
MinGasLimit uint64 = 5000 // Minimum the gas limit may ever be.
GenesisGasLimit uint64 = 4712388 // Gas limit of the Genesis block.
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.
MaximumExtraDataSize uint64 = 32 // Maximum size extra data may be after Genesis.
ExpByteGas uint64 = 10 // Times ceil(log256(exponent)) for the EXP instruction.
@ -73,18 +73,18 @@ const (
// Precompiled contract gas prices
EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation
Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
IdentityBaseGas uint64 = 15 // Base price for a data copy operation
IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation
Bn256AddGas uint64 = 500 // Gas needed for an elliptic curve addition
Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication
Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check
Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check
EcrecoverGas uint64 = 3000 // Elliptic curve sender recovery gas price
Sha256BaseGas uint64 = 60 // Base price for a SHA256 operation
Sha256PerWordGas uint64 = 12 // Per-word price for a SHA256 operation
Ripemd160BaseGas uint64 = 600 // Base price for a RIPEMD160 operation
Ripemd160PerWordGas uint64 = 120 // Per-word price for a RIPEMD160 operation
IdentityBaseGas uint64 = 15 // Base price for a data copy operation
IdentityPerWordGas uint64 = 3 // Per-work price for a data copy operation
ModExpQuadCoeffDiv uint64 = 20 // Divisor for the quadratic particle of the big int modular exponentiation
Bn256AddGas uint64 = 500 // Gas needed for an elliptic curve addition
Bn256ScalarMulGas uint64 = 40000 // Gas needed for an elliptic curve scalar multiplication
Bn256PairingBaseGas uint64 = 100000 // Base price for an elliptic curve pairing check
Bn256PairingPerPointGas uint64 = 80000 // Per-point price for an elliptic curve pairing check
QuorumMaximumExtraDataSize uint64 = 65 // Maximum size extra data may be after Genesis.
)

View File

@ -28,7 +28,7 @@ const (
QuorumVersionMajor = 2
QuorumVersionMinor = 1
QuorumVersionPatch = 0
QuorumVersionPatch = 1
)
// Version holds the textual version string.

View File

@ -3,6 +3,7 @@ package raft
import (
"sync"
"time"
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core"
@ -32,6 +33,7 @@ type RaftService struct {
// we need an event mux to instantiate the blockchain
eventMux *event.TypeMux
minter *minter
nodeKey *ecdsa.PrivateKey
}
func New(ctx *node.ServiceContext, chainConfig *params.ChainConfig, raftId, raftPort uint16, joinExisting bool, blockTime time.Duration, e *eth.Ethereum, startPeers []*discover.Node, datadir string) (*RaftService, error) {
@ -43,6 +45,7 @@ func New(ctx *node.ServiceContext, chainConfig *params.ChainConfig, raftId, raft
accountManager: e.AccountManager(),
downloader: e.Downloader(),
startPeers: startPeers,
nodeKey: ctx.NodeKey(),
}
service.minter = newMinter(chainConfig, service, blockTime)

View File

@ -25,16 +25,22 @@ import (
"github.com/eapache/channels"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
var (
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for arbitrary signer vanity
)
// Current state information for building the next block
@ -50,7 +56,7 @@ type minter struct {
config *params.ChainConfig
mu sync.Mutex
mux *event.TypeMux
eth miner.Backend
eth *RaftService
chain *core.BlockChain
chainDb ethdb.Database
coinbase common.Address
@ -66,6 +72,11 @@ type minter struct {
txPreSub event.Subscription
}
type extraSeal struct {
RaftId []byte // RaftID of the block minter
Signature []byte // Signature of the block minter
}
func newMinter(config *params.ChainConfig, eth *RaftService, blockTime time.Duration) *minter {
minter := &minter{
config: config,
@ -318,8 +329,6 @@ func (minter *minter) mintNewBlock() {
ethash.AccumulateRewards(minter.chain.Config(), work.publicState, header, nil)
header.Root = work.publicState.IntermediateRoot(minter.chain.Config().IsEIP158(work.header.Number))
// NOTE: < QuorumChain creates a signature here and puts it in header.Extra. >
allReceipts := append(publicReceipts, privateReceipts...)
header.Bloom = types.CreateBloom(allReceipts)
@ -330,6 +339,14 @@ func (minter *minter) mintNewBlock() {
l.BlockHash = headerHash
}
//Sign the block and build the extraSeal struct
extraSealBytes := minter.buildExtraSeal(headerHash)
// add vanity and seal to header
// NOTE: leaving vanity blank for now as a space for any future data
header.Extra = make([]byte, extraVanity+len(extraSealBytes))
copy(header.Extra[extraVanity:], extraSealBytes)
block := types.NewBlock(header, committedTxes, nil, publicReceipts)
log.Info("Generated next block", "block num", block.Number(), "num txes", txCount)
@ -407,3 +424,29 @@ func (env *work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, g
return publicReceipt, privateReceipt, nil
}
func (minter *minter) buildExtraSeal(headerHash common.Hash) []byte {
//Sign the headerHash
nodeKey := minter.eth.nodeKey
sig, err := crypto.Sign(headerHash.Bytes(), nodeKey)
if err != nil {
log.Warn("Block sealing failed", "err", err)
}
//build the extraSeal struct
raftIdString := hexutil.EncodeUint64(uint64(minter.eth.raftProtocolManager.raftId))
var extra extraSeal
extra = extraSeal{
RaftId: []byte(raftIdString[2:]), //remove the 0x prefix
Signature: sig,
}
//encode to byte array for storage
extraDataBytes, err := rlp.EncodeToBytes(extra)
if err != nil {
log.Warn("Header.Extra Data Encoding failed", "err", err)
}
return extraDataBytes
}

69
raft/minter_test.go Normal file
View File

@ -0,0 +1,69 @@
package raft
import (
"testing"
"math/big"
"time"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func TestSignHeader(t *testing.T){
//create only what we need to test the seal
var testRaftId uint16 = 5
config := &node.Config{Name: "unit-test", DataDir: ""}
nodeKey := config.NodeKey()
raftProtocolManager := &ProtocolManager{raftId:testRaftId}
raftService := &RaftService{nodeKey: nodeKey, raftProtocolManager: raftProtocolManager}
minter := minter{eth: raftService,}
//create some fake header to sign
fakeParentHash := common.HexToHash("0xc2c1dc1be8054808c69e06137429899d")
header := &types.Header{
ParentHash: fakeParentHash,
Number: big.NewInt(1),
Difficulty: big.NewInt(1),
GasLimit: uint64(0),
GasUsed: uint64(0),
Coinbase: minter.coinbase,
Time: big.NewInt(time.Now().UnixNano()),
}
headerHash := header.Hash()
extraDataBytes := minter.buildExtraSeal(headerHash)
var seal *extraSeal
err := rlp.DecodeBytes(extraDataBytes[:], &seal)
if err != nil {
t.Fatalf("Unable to decode seal: %s", err.Error())
}
// Check raftId
sealRaftId, err := hexutil.DecodeUint64("0x"+ string(seal.RaftId)) //add the 0x prefix
if err != nil {
t.Errorf("Unable to get RaftId: %s", err.Error())
}
if sealRaftId != uint64(testRaftId) {
t.Errorf("RaftID does not match. Expected: %d, Actual: %d", testRaftId, sealRaftId)
}
//Identify who signed it
sig:= seal.Signature
pubKey, err := crypto.SigToPub(headerHash.Bytes(), sig)
if err != nil {
t.Fatalf("Unable to get public key from signature: %s", err.Error())
}
//Compare derived public key to original public key
if pubKey.X.Cmp(nodeKey.X) != 0 {
t.Errorf("Signature incorrect!")
}
}

View File

@ -126,9 +126,6 @@ func BenchmarkDiscovery_64_4(b *testing.B) { benchmarkDiscovery(b, 64, 4) }
func BenchmarkDiscovery_128_4(b *testing.B) { benchmarkDiscovery(b, 128, 4) }
func BenchmarkDiscovery_256_4(b *testing.B) { benchmarkDiscovery(b, 256, 4) }
func TestDiscoverySimulationDockerAdapter(t *testing.T) {
testDiscoverySimulationDockerAdapter(t, *nodeCount, *initCount)
}
func testDiscoverySimulationDockerAdapter(t *testing.T, nodes, conns int) {
adapter, err := adapters.NewDockerAdapter()