merge 1.8.18 with quorum/master

refactor raft & istanbul based on upstream changes
fix unit test cases
This commit is contained in:
amalraj.manigmail.com 2019-02-08 17:29:01 +08:00
parent 4dbd95947d
commit 73667e7942
26 changed files with 323 additions and 219 deletions

View File

@ -1,95 +1,108 @@
# simplifed version of the upstream travis configuration
# simplified version of the upstream travis configuration.
# automated acceptance test is run[in linux box] for raft, istanbul and clique consensus in parallel as part of every build
# unit test is run in linux and macOS boxes as part of every build
language: go
go_import_path: github.com/ethereum/go-ethereum
go: 1.9.x
sudo: true
branches:
only:
- /.*/ # everything including tags
sudo: false
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="
- TESSERA_JAR="$HOME/tessera.jar"
addons:
apt:
update: true
sources:
- sourceline: ppa:ethereum/ethereum
- sourceline: ppa:openjdk-r/ppa
packages:
- openjdk-8-jdk
- dpkg # fixes issue with dpkg-deb error due to travis image
- openssh-client
- dnsutils
- maven
- solc
- jq
- curl
before_install:
- if [ $TRAVIS_OS_NAME = linux ]; then git clone https://github.com/amalrajmani/quorum-acceptance-tests.git $TRAVIS_HOME/quorum-acceptance-tests; fi;
- if [ $TRAVIS_OS_NAME = linux ]; then sudo chmod 755 $TRAVIS_HOME/quorum-acceptance-tests/src/travis/install-linux.sh; fi;
- if [ $TRAVIS_OS_NAME = linux ]; then sudo chmod 755 $TRAVIS_HOME/quorum-acceptance-tests/src/travis/script-linux.sh; fi;
matrix:
include:
- 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
- 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
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
- name: linux-raft
os: linux
dist: trusty
sudo: true
go: 1.10.x
- 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
cache:
directories:
- $HOME/.m2
env:
- TF_VAR_consensus_mechanism=raft
install: $TRAVIS_HOME/quorum-acceptance-tests/src/travis/install-linux.sh
script: $TRAVIS_HOME/quorum-acceptance-tests/src/travis/script-linux.sh
- name: linux-istanbul
os: linux
dist: trusty
sudo: true
go: 1.10.x
directories:
- $HOME/.m2
env:
- TF_VAR_consensus_mechanism=istanbul
install: $TRAVIS_HOME/quorum-acceptance-tests/src/travis/install-linux.sh
script: $TRAVIS_HOME/quorum-acceptance-tests/src/travis/script-linux.sh
- name: linux-clique
os: linux
dist: trusty
sudo: true
go: 1.10.x
directories:
- $HOME/.m2
env:
- TF_VAR_consensus_mechanism=clique
install: $TRAVIS_HOME/quorum-acceptance-tests/src/travis/install-linux.sh
script: $TRAVIS_HOME/quorum-acceptance-tests/src/travis/script-linux.sh
- 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
- 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.10.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
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

View File

@ -23,9 +23,10 @@ import (
const (
testSource = `
pragma solidity ^0.5.0;
contract test {
/// @notice Will multiply ` + "`a`" + ` by 7.
function multiply(uint a) returns(uint d) {
function multiply(uint a) public returns(uint d) {
return a * 7;
}
}

View File

@ -47,7 +47,7 @@ func newBlockChain(n int) (*core.BlockChain, *backend) {
// Use the first key as private key
b, _ := New(config, nodeKeys[0], memDB).(*backend)
genesis.MustCommit(memDB)
blockchain, err := core.NewBlockChain(memDB, nil, genesis.Config, b, vm.Config{})
blockchain, err := core.NewBlockChain(memDB, nil, genesis.Config, b, vm.Config{}, nil)
if err != nil {
panic(err)
}
@ -120,7 +120,7 @@ func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
header := &types.Header{
ParentHash: parent.Hash(),
Number: parent.Number().Add(parent.Number(), common.Big1),
GasLimit: core.CalcGasLimit(parent),
GasLimit: core.CalcGasLimit(parent, parent.GasLimit(), parent.GasLimit()),
GasUsed: 0,
Extra: parent.Extra(),
Time: new(big.Int).Add(parent.Time(), new(big.Int).SetUint64(config.BlockPeriod)),
@ -131,8 +131,11 @@ func makeHeader(parent *types.Block, config *istanbul.Config) *types.Header {
func makeBlock(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
block := makeBlockWithoutSeal(chain, engine, parent)
block, _ = engine.Seal(chain, block, nil)
return block
stopCh := make(chan struct{})
resultCh := make(chan *types.Block, 10)
go engine.Seal(chain, block, resultCh, stopCh)
blk := <-resultCh
return blk
}
func makeBlockWithoutSeal(chain *core.BlockChain, engine *backend, parent *types.Block) *types.Block {
@ -174,10 +177,15 @@ func TestSealStopChannel(t *testing.T) {
eventSub.Unsubscribe()
}
go eventLoop()
finalBlock, err := engine.Seal(chain, block, stop)
if err != nil {
t.Errorf("error mismatch: have %v, want nil", err)
}
resultCh := make(chan *types.Block, 10)
go func() {
err := engine.Seal(chain, block, resultCh, stop)
if err != nil {
t.Errorf("error mismatch: have %v, want nil", err)
}
}()
finalBlock := <-resultCh
if finalBlock != nil {
t.Errorf("block mismatch: have %v, want nil", finalBlock)
}
@ -201,7 +209,7 @@ func TestSealCommittedOtherHash(t *testing.T) {
}
go eventLoop()
seal := func() {
engine.Seal(chain, block, nil)
engine.Seal(chain, block, nil, make(chan struct{}))
t.Error("seal should not be completed")
}
go seal()
@ -218,11 +226,16 @@ func TestSealCommitted(t *testing.T) {
chain, engine := newBlockChain(1)
block := makeBlockWithoutSeal(chain, engine, chain.Genesis())
expectedBlock, _ := engine.updateBlock(engine.chain.GetHeader(block.ParentHash(), block.NumberU64()-1), block)
resultCh := make(chan *types.Block, 10)
go func() {
err := engine.Seal(chain, block, resultCh, make(chan struct{}))
finalBlock, err := engine.Seal(chain, block, nil)
if err != nil {
t.Errorf("error mismatch: have %v, want nil", err)
}
if err != nil {
t.Errorf("error mismatch: have %v, want %v", err, expectedBlock)
}
}()
finalBlock := <-resultCh
if finalBlock.Hash() != expectedBlock.Hash() {
t.Errorf("hash mismatch: have %v, want %v", finalBlock.Hash(), expectedBlock.Hash())
}

View File

@ -347,7 +347,7 @@ func TestVoting(t *testing.T) {
config.Epoch = tt.epoch
}
engine := New(config, accounts.accounts[tt.validators[0]], db).(*backend)
chain, err := core.NewBlockChain(db, nil, genesis.Config, engine, vm.Config{})
chain, err := core.NewBlockChain(db, nil, genesis.Config, engine, vm.Config{}, nil)
// Assemble a chain of headers from the cast votes
headers := make([]*types.Header, len(tt.votes))

View File

@ -156,6 +156,10 @@ func (self *testSystemBackend) ParentValidators(proposal istanbul.Proposal) ista
return self.peers
}
func (sb *testSystemBackend) Close() error {
return nil
}
// ==============================================
//
// define the struct that need to be provided for integration tests.

View File

@ -103,8 +103,10 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
return nil
}
// CalcGasLimit computes the gas limit of the next block after parent.
// This is miner strategy, not consensus protocol.
// 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.
func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
// contrib = (parentGasUsed * 3 / 2) / 4096
contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor
@ -123,8 +125,7 @@ func CalcGasLimit(parent *types.Block, gasFloor, gasCeil uint64) uint64 {
if limit < params.MinGasLimit {
limit = params.MinGasLimit
}
// however, if we're now below the target (TargetGasLimit) we increase the
// limit as much as we can (parentGasLimit / 4096 -1)
// If we're outside our allowed gas range, we try to hone towards them
if limit < gasFloor {
limit = parent.GasLimit() + decay
if limit > gasFloor {

View File

@ -117,25 +117,70 @@ func gasReturnDataCopy(gt params.GasTable, evm *EVM, contract *Contract, stack *
func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
var (
db = getDualState(evm, contract.Address())
y, x = stack.Back(1), stack.Back(0)
val = db.GetState(contract.Address(), common.BigToHash(x))
db = getDualState(evm, contract.Address())
y, x = stack.Back(1), stack.Back(0)
current = db.GetState(contract.Address(), common.BigToHash(x))
)
// This checks for 3 scenario's and calculates gas accordingly
// 1. From a zero-value address to a non-zero value (NEW VALUE)
// 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a non-zero to a non-zero (CHANGE)
if val == (common.Hash{}) && y.Sign() != 0 {
// 0 => non 0
return params.SstoreSetGas, nil
} else if val != (common.Hash{}) && y.Sign() == 0 {
// non 0 => 0
db.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
} else {
// non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
// The legacy gas metering only takes into consideration the current state
if !evm.chainRules.IsConstantinople {
// This checks for 3 scenario's and calculates gas accordingly:
//
// 1. From a zero-value address to a non-zero value (NEW VALUE)
// 2. From a non-zero value address to a zero-value address (DELETE)
// 3. From a non-zero to a non-zero (CHANGE)
switch {
case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
return params.SstoreSetGas, nil
case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
evm.StateDB.AddRefund(params.SstoreRefundGas)
return params.SstoreClearGas, nil
default: // non 0 => non 0 (or 0 => 0)
return params.SstoreResetGas, nil
}
}
// The new gas metering is based on net gas costs (EIP-1283):
//
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
// 2. If current value does not equal new value
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
// 2.1.1. If original value is 0, 20000 gas is deducted.
// 2.1.2. Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
// 2.2.1. If original value is not 0
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
// 2.2.2. If original value equals new value (this storage slot is reset)
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
value := common.BigToHash(y)
if current == value { // noop (1)
return params.NetSstoreNoopGas, nil
}
original := evm.StateDB.GetCommittedState(contract.Address(), common.BigToHash(x))
if original == current {
if original == (common.Hash{}) { // create slot (2.1.1)
return params.NetSstoreInitGas, nil
}
if value == (common.Hash{}) { // delete slot (2.1.2b)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
}
if original != (common.Hash{}) {
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
evm.StateDB.SubRefund(params.NetSstoreClearRefund)
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
evm.StateDB.AddRefund(params.NetSstoreClearRefund)
}
}
if original == value {
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
evm.StateDB.AddRefund(params.NetSstoreResetClearRefund)
} else { // reset to original existing slot (2.2.2.2)
evm.StateDB.AddRefund(params.NetSstoreResetRefund)
}
}
return params.NetSstoreDirtyGas, nil
}
func makeGasLog(n uint64) gasFunc {

View File

@ -494,7 +494,7 @@ func BenchmarkOpMstore(bench *testing.B) {
func BenchmarkOpSHA3(bench *testing.B) {
var (
env = NewEVM(Context{}, nil, params.TestChainConfig, Config{})
env = NewEVM(Context{}, nil, nil, params.TestChainConfig, Config{})
stack = newstack()
mem = NewMemory()
evmInterpreter = NewEVMInterpreter(env, env.vmConfig)

View File

@ -26,6 +26,7 @@ import (
// Quorum uses a cut-down StateDB, MinimalApiState. We leave the methods in StateDB commented out so they'll produce a
// conflict when upstream changes.
//TODO(Amal): to be reviewed
type MinimalApiState interface {
GetBalance(addr common.Address) *big.Int
GetCode(addr common.Address) []byte

View File

@ -50,7 +50,8 @@ func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func TestStoreCapture(t *testing.T) {
var (
env = NewEVM(Context{}, &dummyStatedb{}, nil, params.TestChainConfig, Config{})
db = &dummyStatedb{}
env = NewEVM(Context{}, db, db, params.TestChainConfig, Config{})
logger = NewStructLogger(nil)
mem = NewMemory()
stack = newstack()

View File

@ -288,7 +288,7 @@ func (s EthAPIState) GetNonce(addr common.Address) uint64 {
return s.state.GetNonce(addr)
}
// TODO: implement the following methods for Quorum
// TODO(Amal): implement the following methods for Quorum
func (s EthAPIState) GetProof(common.Address) ([][]byte, error) {
return nil, nil
}

View File

@ -562,7 +562,7 @@ func testBroadcastBlock(t *testing.T, totalPeers, broadcastExpected int) {
if err != nil {
t.Fatalf("failed to create new blockchain: %v", err)
}
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db)
pm, err := NewProtocolManager(config, downloader.FullSync, DefaultConfig.NetworkId, evmux, new(testTxPool), pow, blockchain, db, false)
if err != nil {
t.Fatalf("failed to start test protocol manager: %v", err)
}

View File

@ -51,7 +51,8 @@ type dummyStatedb struct {
func (*dummyStatedb) GetRefund() uint64 { return 1337 }
func runTrace(tracer *Tracer) (json.RawMessage, error) {
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
db := &dummyStatedb{}
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, db, db, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
contract := vm.NewContract(account{}, account{}, big.NewInt(0), 10000)
contract.Code = []byte{byte(vm.PUSH1), 0x1, byte(vm.PUSH1), 0x1, 0x0}
@ -132,8 +133,8 @@ func TestHaltBetweenSteps(t *testing.T) {
if err != nil {
t.Fatal(err)
}
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, &dummyStatedb{}, nil, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
db := &dummyStatedb{}
env := vm.NewEVM(vm.Context{BlockNumber: big.NewInt(1)}, db, db, params.TestChainConfig, vm.Config{Debug: true, Tracer: tracer})
contract := vm.NewContract(&account{}, &account{}, big.NewInt(0), 0)
tracer.CaptureState(env, 0, 0, 0, 0, nil, nil, contract, 0, nil)

View File

@ -124,6 +124,7 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
}
}
func (b *testWorkerBackend) ChainDb() ethdb.Database { return b.db }
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool }
func (b *testWorkerBackend) PostChainEvents(events []interface{}) {
@ -153,7 +154,7 @@ func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, eng
// Ensure snapshot has been updated.
time.Sleep(100 * time.Millisecond)
block, state := w.pending()
block, state, _ := w.pending()
if block.NumberU64() != 1 {
t.Errorf("block number mismatch: have %d, want %d", block.NumberU64(), 1)
}
@ -164,7 +165,7 @@ func testPendingStateAndBlock(t *testing.T, chainConfig *params.ChainConfig, eng
// Ensure the new tx events has been processed
time.Sleep(100 * time.Millisecond)
block, state = w.pending()
block, state, _ = w.pending()
if balance := state.GetBalance(testUserAddress); balance.Cmp(big.NewInt(2000)) != 0 {
t.Errorf("account balance mismatch: have %d, want %d", balance, 2000)
}

View File

@ -368,9 +368,7 @@ func (c *Config) parsePersistentNodes(path string) []*enode.Node {
if url == "" {
continue
}
log.Info("AJ-parsePersistentNodes1", "url", url)
node, err := enode.ParseV4(url)
log.Info("AJ-parsePersistentNodes2", "url", url, "ID", node.ID().String(), "EnodeID", node.EnodeID())
if err != nil {
log.Error(fmt.Sprintf("Node URL %s: %v\n", url, err))
continue

View File

@ -86,7 +86,8 @@ func (V4ID) NodeAddr(r *enr.Record) []byte {
buf := make([]byte, 64)
math.ReadBits(pubkey.X, buf[:32])
math.ReadBits(pubkey.Y, buf[32:])
return crypto.Keccak256(buf)
b := crypto.Keccak256(buf)
return b
}
// Secp256k1 is the "secp256k1" key, which holds a public key.

View File

@ -82,6 +82,7 @@ func (n *Node) UDP() int {
return int(port)
}
//TODO: Amal to review
// RAFTPORT returns the RAFT PORT of the node
func (n *Node) RAFTPORT() int {
var port enr.RAFTPORT
@ -194,6 +195,34 @@ func (n *ID) UnmarshalText(text []byte) error {
return nil
}
// ID is a unique identifier for each node used by RAFT
type EnodeID [64]byte
// ID prints as a long hexadecimal number.
func (n EnodeID) String() string {
return fmt.Sprintf("%x", n[:])
}
// The Go syntax representation of a ID is a call to HexID.
func (n EnodeID) GoString() string {
return fmt.Sprintf("enode.HexID(\"%x\")", n[:])
}
// MarshalText implements the encoding.TextMarshaler interface.
func (n EnodeID) MarshalText() ([]byte, error) {
return []byte(hex.EncodeToString(n[:])), nil
}
// UnmarshalText implements the encoding.TextUnmarshaler interface.
func (n *EnodeID) UnmarshalText(text []byte) error {
id, err := RaftHexID(string(text))
if err != nil {
return err
}
*n = id
return nil
}
// HexID converts a hex string to an ID.
// The string may be prefixed with 0x.
// It panics if the string is not a valid ID.
@ -205,6 +234,20 @@ func HexID(in string) ID {
return id
}
//TODO(Amal): to review
func RaftHexID(in string) (EnodeID, error) {
var id EnodeID
b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))
if err != nil {
return id, err
} else if len(b) != len(id) {
return id, fmt.Errorf("wrong length, want %d hex chars", len(id)*2)
}
copy(id[:], b)
return id, nil
}
func parseID(in string) (ID, error) {
var id ID
b, err := hex.DecodeString(strings.TrimPrefix(in, "0x"))

View File

@ -28,7 +28,6 @@ import (
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enr"
)
@ -93,7 +92,7 @@ func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp, raftPort int) *Node {
}
if raftPort != 0 {
r.Set(enr.RAFTPORT(tcp))
r.Set(enr.RAFTPORT(raftPort))
}
signV4Compat(&r, pubkey)
@ -101,7 +100,6 @@ func NewV4(pubkey *ecdsa.PublicKey, ip net.IP, tcp, udp, raftPort int) *Node {
if err != nil {
panic(err)
}
log.Info("NewV4", "raftPort", raftPort, "nodeId", n.ID().String(), "NodeId.bytes", n.ID().Bytes())
return n
}
@ -122,11 +120,9 @@ func parseComplete(rawurl string) (*Node, error) {
if u.User == nil {
return nil, errors.New("does not contain node ID")
}
log.Info("AJ-parseComplete1", "u.User", u.User.String())
if id, err = parsePubkey(u.User.String()); err != nil {
return nil, fmt.Errorf("invalid node ID (%v)", err)
}
log.Info("AJ-parseComplete2", "id", id)
// Parse the IP address.
host, port, err := net.SplitHostPort(u.Host)
if err != nil {
@ -187,7 +183,7 @@ func parsePubkey(in string) (*ecdsa.PublicKey, error) {
return crypto.UnmarshalPubkey(b)
}
// TODO: Amal to review it - added for RAFT
// TODO(Amal): to review it - added for RAFT
func (n *Node) EnodeID() string {
var (
scheme enr.ID
@ -215,10 +211,8 @@ func (n *Node) v4URL() string {
n.Load((*Secp256k1)(&key))
switch {
case scheme == "v4" || key != ecdsa.PublicKey{}:
log.Info("AJ-schemeV4")
nodeid = fmt.Sprintf("%x", crypto.FromECDSAPub(&key)[1:])
default:
log.Info("AJ-NOT schemeV4")
nodeid = fmt.Sprintf("%s.%x", scheme, n.id[:])
}
u := url.URL{Scheme: "enode"}

View File

@ -40,7 +40,6 @@ import (
"io"
"sort"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
)
@ -121,7 +120,6 @@ func (r *Record) Load(e Entry) error {
if err := rlp.DecodeBytes(r.pairs[i].v, e); err != nil {
return &KeyError{Key: e.ENRKey(), Err: err}
}
log.Info("AJ-Load", "key", e.ENRKey(), "value", r.pairs[i].v)
return nil
}
return &KeyError{Key: e.ENRKey(), Err: errNotFound}

View File

@ -925,8 +925,6 @@ func (srv *Server) setupConn(c *conn, flags connFlag, dialDest *enode.Node) erro
} else {
c.node = nodeFromConn(remotePubkey, c.fd)
}
log.Info("AJ-setupConn1", "remotePubkey", remotePubkey)
log.Info("AJ-setupConn2", "c.node.ID", c.node.ID(), "c.node.ID.Bytes", c.node.ID().Bytes(), "node", c.node.String())
clog := srv.log.New("id", c.node.ID(), "addr", c.fd.RemoteAddr(), "conn", c.flags)
//START - QUORUM Permissioning

View File

@ -131,16 +131,16 @@ var (
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil, false}
AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil, false}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false}
AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}, nil, false}
TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, new(EthashConfig), nil, nil, false}
TestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil, nil, false}
TestRules = TestChainConfig.Rules(new(big.Int))
QuorumTestChainConfig = &ChainConfig{big.NewInt(10), big.NewInt(0), nil, false, nil, common.Hash{}, nil, nil, nil, nil, nil, new(EthashConfig), nil, nil, true}

View File

@ -31,6 +31,7 @@ func (s *PublicRaftAPI) RemovePeer(raftId uint16) {
}
func (s *PublicRaftAPI) Leader() (string, error) {
addr, err := s.raftService.raftProtocolManager.LeaderAddress()
if nil != err {
return "", err

View File

@ -23,17 +23,16 @@ import (
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rlp"
"crypto/ecdsa"
"github.com/coreos/etcd/etcdserver/stats"
raftTypes "github.com/coreos/etcd/pkg/types"
etcdRaft "github.com/coreos/etcd/raft"
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/rafthttp"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/syndtr/goleveldb/leveldb"
"gopkg.in/fatih/set.v0"
"github.com/deckarep/golang-set"
)
type ProtocolManager struct {
@ -56,7 +55,7 @@ type ProtocolManager struct {
// Remote peer state (protected by mu vs concurrent access via JS)
leader uint16
peers map[uint16]*Peer
removedPeers *set.Set // *Permanently removed* peers
removedPeers mapset.Set // *Permanently removed* peers
// P2P transport
p2pServer *p2p.Server // Initialized in start()
@ -107,7 +106,7 @@ func NewProtocolManager(raftId uint16, raftPort uint16, blockchain *core.BlockCh
bootstrapNodes: bootstrapNodes,
peers: make(map[uint16]*Peer),
leader: uint16(etcdRaft.None),
removedPeers: set.New(set.ThreadSafe).(*set.Set),
removedPeers: mapset.NewSet(),
joinExisting: joinExisting,
blockchain: blockchain,
eventMux: mux,
@ -201,10 +200,12 @@ func (pm *ProtocolManager) NodeInfo() *RaftNodeInfo {
peerIdx += 1
}
removedPeerIfaces := pm.removedPeers.List()
removedPeerIds := make([]uint16, len(removedPeerIfaces))
for i, removedIface := range removedPeerIfaces {
removedPeerIfaces := pm.removedPeers
removedPeerIds := make([]uint16, removedPeerIfaces.Cardinality())
i := 0
for removedIface := range removedPeerIfaces.Iterator().C {
removedPeerIds[i] = removedIface.(uint16)
i++
}
//
@ -247,8 +248,8 @@ func (pm *ProtocolManager) nextRaftId() uint16 {
}
}
removedPeerIfaces := pm.removedPeers.List()
for _, removedIface := range removedPeerIfaces {
removedPeerIfaces := pm.removedPeers
for removedIface := range removedPeerIfaces.Iterator().C {
removedId := removedIface.(uint16)
if maxId < removedId {
@ -263,7 +264,7 @@ func (pm *ProtocolManager) isRaftIdRemoved(id uint16) bool {
pm.mu.RLock()
defer pm.mu.RUnlock()
return pm.removedPeers.Has(id)
return pm.removedPeers.Contains(id)
}
func (pm *ProtocolManager) isRaftIdUsed(raftId uint16) bool {
@ -286,14 +287,14 @@ func (pm *ProtocolManager) isNodeAlreadyInCluster(node *enode.Node) error {
peerNode := peer.p2pNode
if peerNode.ID() == node.ID() {
return fmt.Errorf("node with this enode has already been added to the cluster: %v", node.ID)
return fmt.Errorf("node with this enode has already been added to the cluster: %s", node.ID())
}
if peerNode.IP().Equal(node.IP()) {
if peerNode.TCP() == node.TCP() {
return fmt.Errorf("existing node %v with raft ID %v is already using eth p2p at %v:%v", peerNode.ID, peerRaftId, node.IP, node.TCP)
return fmt.Errorf("existing node %v with raft ID %v is already using eth p2p at %v:%v", peerNode.ID(), peerRaftId, node.IP(), node.TCP())
} else if peer.address.RaftPort == enr.RAFTPORT(node.RAFTPORT()) {
return fmt.Errorf("existing node %v with raft ID %v is already using raft at %v:%v", peerNode.ID, peerRaftId, node.IP, node.RAFTPORT())
return fmt.Errorf("existing node %v with raft ID %v is already using raft at %v:%v", peerNode.ID(), peerRaftId, node.IP(), node.RAFTPORT())
}
}
}
@ -641,31 +642,21 @@ func raftUrl(address *Address) string {
return fmt.Sprintf("http://%s:%d", address.Ip, address.RaftPort)
}
func decodePubkey64(b []byte) (*ecdsa.PublicKey, error) {
return crypto.UnmarshalPubkey(append([]byte{0x04}, b...))
}
func (pm *ProtocolManager) addPeer(address *Address) {
pm.mu.Lock()
defer pm.mu.Unlock()
raftId := address.RaftId
log.Info("AJ-addPeer:", "raftId", raftId, "nodeId", address.NodeId.String(), "bytes", address.NodeId.Bytes(), "address", address)
//TODO: Amal to confirm if this decoding is correct
//pubKey, err := decodePubkey64(address.NodeId.Bytes())
/*if err != nil {
log.Error("AJ-error decoding pub key", "nodeId", address.NodeId.String(), "err", err)
return
}*/
log.Info("addPeer: decoded pub key", "pubKey", address.PubKey)
//TODO(Amal): to review
pubKey, err := enode.HexPubkey(address.NodeId.String())
if err != nil {
log.Error("error decoding pub key from enodeId", "enodeId", address.NodeId.String(), "err", err)
panic(err)
}
// Add P2P connection:
p2pNode := enode.NewV4(address.PubKey, address.Ip, 0, int(address.P2pPort), 0)
log.Info("AJ-p2pNode", "ID1", address.NodeId.String(), "ID2", p2pNode.ID().String())
p2pNode := enode.NewV4(pubKey, address.Ip, 0, int(address.P2pPort), int(address.RaftPort))
pm.p2pServer.AddPeer(p2pNode)
// Add raft transport connection:
@ -858,7 +849,6 @@ func (pm *ProtocolManager) makeInitialRaftPeers() (raftPeers []etcdRaft.Peer, pe
// requiring the use of static peers for the initial set, and load them from e.g. another JSON file which
// contains pairs of enodes and raft ports, or we can get this initial peer list from commandline flags.
address := newAddress(raftId, node.RAFTPORT(), node)
raftPeers[i] = etcdRaft.Peer{
ID: uint64(raftId),
Context: address.toBytes(),

View File

@ -5,38 +5,35 @@ import (
"net"
"fmt"
"log"
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/rlp"
"log"
)
const nodeIDBits = 512
type EnodeID [nodeIDBits / 8]byte
// Serializable information about a Peer. Sufficient to build `etcdRaft.Peer`
// or `discover.Node`.
type Address struct {
RaftId uint16 `json:"raftId"`
NodeId [64]byte `json:"nodeId"`
Ip net.IP `json:"ip"`
P2pPort enr.TCP `json:"p2pPort"`
RaftPort enr.RAFTPORT `json:"raftPort"`
PubKey *ecdsa.PublicKey
RaftId uint16 `json:"raftId"`
NodeId enode.EnodeID `json:"nodeId"`
Ip net.IP `json:"ip"`
P2pPort enr.TCP `json:"p2pPort"`
RaftPort enr.RAFTPORT `json:"raftPort"`
}
// TODO(Amal): to review
func newAddress(raftId uint16, raftPort int, node *enode.Node) *Address {
node.ID().Bytes()
id, err := enode.RaftHexID(node.EnodeID())
if err != nil {
panic(err)
}
return &Address{
RaftId: raftId,
NodeId: []byte(node.EnodeID()),
NodeId: id,
Ip: node.IP(),
P2pPort: enr.TCP(node.TCP()),
RaftPort: enr.RAFTPORT(raftPort),
PubKey: node.Pubkey(),
}
}
@ -54,7 +51,7 @@ func (addr *Address) DecodeRLP(s *rlp.Stream) error {
// These fields need to be public:
var temp struct {
RaftId uint16
NodeId enode.ID
NodeId enode.EnodeID
Ip net.IP
P2pPort enr.TCP
RaftPort enr.RAFTPORT

View File

@ -10,12 +10,12 @@ import (
"github.com/coreos/etcd/raft/raftpb"
"github.com/coreos/etcd/snap"
"github.com/coreos/etcd/wal/walpb"
"github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"gopkg.in/fatih/set.v0"
)
// Snapshot
@ -37,9 +37,7 @@ func (pm *ProtocolManager) buildSnapshot() *Snapshot {
defer pm.mu.RUnlock()
numNodes := len(pm.confState.Nodes)
log.Info("AJ-numNodes", "nodesSize", numNodes, "nodes", pm.confState.Nodes)
log.Info("AJ-peers", "pm.peers", pm.peers)
numRemovedNodes := pm.removedPeers.Size()
numRemovedNodes := pm.removedPeers.Cardinality()
snapshot := &Snapshot{
addresses: make([]Address, numNodes),
@ -61,9 +59,10 @@ func (pm *ProtocolManager) buildSnapshot() *Snapshot {
sort.Sort(ByRaftId(snapshot.addresses))
// Populate removed IDs
for i, removedIface := range pm.removedPeers.List() {
i := 0
for removedIface := range pm.removedPeers.Iterator().C {
snapshot.removedRaftIds[i] = removedIface.(uint16)
i++
}
return snapshot
@ -101,8 +100,8 @@ func (pm *ProtocolManager) triggerSnapshot(index uint64) {
pm.mu.Unlock()
}
func confStateIdSet(confState raftpb.ConfState) *set.Set {
set := set.New(set.ThreadSafe).(*set.Set)
func confStateIdSet(confState raftpb.ConfState) mapset.Set {
set := mapset.NewSet()
for _, rawRaftId := range confState.Nodes {
set.Add(uint16(rawRaftId))
}
@ -117,7 +116,7 @@ func (pm *ProtocolManager) updateClusterMembership(newConfState raftpb.ConfState
// Update tombstones for permanently removed peers. For simplicity we do not
// allow the re-use of peer IDs once a peer is removed.
removedPeers := set.New(set.ThreadSafe).(*set.Set)
removedPeers := mapset.NewSet()
for _, removedRaftId := range removedRaftIds {
removedPeers.Add(removedRaftId)
}
@ -129,8 +128,8 @@ func (pm *ProtocolManager) updateClusterMembership(newConfState raftpb.ConfState
prevIds := confStateIdSet(prevConfState)
newIds := confStateIdSet(newConfState)
idsToRemove := set.Difference(prevIds, newIds)
for _, idIfaceToRemove := range idsToRemove.List() {
idsToRemove := prevIds.Difference(newIds)
for idIfaceToRemove := range idsToRemove.Iterator().C {
raftId := idIfaceToRemove.(uint16)
log.Info("removing old raft peer", "peer id", raftId)

View File

@ -5,8 +5,8 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/fatih/set.v0"
lane "gopkg.in/oleiade/lane.v1"
"github.com/deckarep/golang-set"
"gopkg.in/oleiade/lane.v1"
)
// The speculative chain represents blocks that we have minted which haven't been accepted into the chain yet, building
@ -21,16 +21,16 @@ import (
type speculativeChain struct {
head *types.Block
unappliedBlocks *lane.Deque
expectedInvalidBlockHashes *set.Set // This is thread-safe. This set is referred to as our "guard" below.
proposedTxes *set.Set // This is thread-safe.
expectedInvalidBlockHashes mapset.Set // This is thread-safe. This set is referred to as our "guard" below.
proposedTxes mapset.Set // This is thread-safe.
}
func newSpeculativeChain() *speculativeChain {
return &speculativeChain{
head: nil,
unappliedBlocks: lane.NewDeque(),
expectedInvalidBlockHashes: set.New(set.ThreadSafe).(*set.Set),
proposedTxes: set.New(set.ThreadSafe).(*set.Set),
expectedInvalidBlockHashes: mapset.NewSet(),
proposedTxes: mapset.NewSet(),
}
}
@ -87,7 +87,7 @@ func (chain *speculativeChain) unwindFrom(invalidHash common.Hash, headBlock *ty
// check our "guard" to see if this is a (descendant) block we're
// expected to be ruled invalid. if we find it, remove from the guard
if chain.expectedInvalidBlockHashes.Has(invalidHash) {
if chain.expectedInvalidBlockHashes.Contains(invalidHash) {
log.Info("Removing expected-invalid block from guard.", "block", invalidHash)
chain.expectedInvalidBlockHashes.Remove(invalidHash)
@ -140,7 +140,9 @@ func (chain *speculativeChain) recordProposedTransactions(txes types.Transaction
for i, tx := range txes {
txHashIs[i] = tx.Hash()
}
chain.proposedTxes.Add(txHashIs...)
for _, i := range txHashIs {
chain.proposedTxes.Add(i)
}
}
// Removes txes in block from our "blacklist" of "proposed tx" hashes. When we
@ -161,7 +163,9 @@ func (chain *speculativeChain) removeProposedTxes(block *types.Block) {
// here and in mintNewBlock concurrently. using a finer-grained set-specific
// lock here is preferable, because mintNewBlock holds its locks for a
// nontrivial amount of time.
chain.proposedTxes.Remove(minedTxInterfaces...)
for _, i := range minedTxInterfaces {
chain.proposedTxes.Remove(i)
}
}
func (chain *speculativeChain) withoutProposedTxes(addrTxes AddressTxes) AddressTxes {
@ -170,7 +174,7 @@ func (chain *speculativeChain) withoutProposedTxes(addrTxes AddressTxes) Address
for addr, txes := range addrTxes {
filteredTxes := make(types.Transactions, 0)
for _, tx := range txes {
if !chain.proposedTxes.Has(tx.Hash()) {
if !chain.proposedTxes.Contains(tx.Hash()) {
filteredTxes = append(filteredTxes, tx)
}
}