diff --git a/consensus/istanbul/validator/default.go b/consensus/istanbul/validator/default.go
new file mode 100644
index 0000000000..17edda5521
--- /dev/null
+++ b/consensus/istanbul/validator/default.go
@@ -0,0 +1,201 @@
+// Copyright 2017 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 .
+
+package validator
+
+import (
+ "math"
+ "reflect"
+ "sort"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/istanbul"
+)
+
+type defaultValidator struct {
+ address common.Address
+}
+
+func (val *defaultValidator) Address() common.Address {
+ return val.address
+}
+
+func (val *defaultValidator) String() string {
+ return val.Address().String()
+}
+
+// ----------------------------------------------------------------------------
+
+type defaultSet struct {
+ validators istanbul.Validators
+ policy istanbul.ProposerPolicy
+
+ proposer istanbul.Validator
+ validatorMu sync.RWMutex
+ selector istanbul.ProposalSelector
+}
+
+func newDefaultSet(addrs []common.Address, policy istanbul.ProposerPolicy) *defaultSet {
+ valSet := &defaultSet{}
+
+ valSet.policy = policy
+ // init validators
+ valSet.validators = make([]istanbul.Validator, len(addrs))
+ for i, addr := range addrs {
+ valSet.validators[i] = New(addr)
+ }
+ // sort validator
+ sort.Sort(valSet.validators)
+ // init proposer
+ if valSet.Size() > 0 {
+ valSet.proposer = valSet.GetByIndex(0)
+ }
+ valSet.selector = roundRobinProposer
+ if policy == istanbul.Sticky {
+ valSet.selector = stickyProposer
+ }
+
+ return valSet
+}
+
+func (valSet *defaultSet) Size() int {
+ valSet.validatorMu.RLock()
+ defer valSet.validatorMu.RUnlock()
+ return len(valSet.validators)
+}
+
+func (valSet *defaultSet) List() []istanbul.Validator {
+ valSet.validatorMu.RLock()
+ defer valSet.validatorMu.RUnlock()
+ return valSet.validators
+}
+
+func (valSet *defaultSet) GetByIndex(i uint64) istanbul.Validator {
+ valSet.validatorMu.RLock()
+ defer valSet.validatorMu.RUnlock()
+ if i < uint64(valSet.Size()) {
+ return valSet.validators[i]
+ }
+ return nil
+}
+
+func (valSet *defaultSet) GetByAddress(addr common.Address) (int, istanbul.Validator) {
+ for i, val := range valSet.List() {
+ if addr == val.Address() {
+ return i, val
+ }
+ }
+ return -1, nil
+}
+
+func (valSet *defaultSet) GetProposer() istanbul.Validator {
+ return valSet.proposer
+}
+
+func (valSet *defaultSet) IsProposer(address common.Address) bool {
+ _, val := valSet.GetByAddress(address)
+ return reflect.DeepEqual(valSet.GetProposer(), val)
+}
+
+func (valSet *defaultSet) CalcProposer(lastProposer common.Address, round uint64) {
+ valSet.validatorMu.RLock()
+ defer valSet.validatorMu.RUnlock()
+ valSet.proposer = valSet.selector(valSet, lastProposer, round)
+}
+
+func calcSeed(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) uint64 {
+ offset := 0
+ if idx, val := valSet.GetByAddress(proposer); val != nil {
+ offset = idx
+ }
+ return uint64(offset) + round
+}
+
+func emptyAddress(addr common.Address) bool {
+ return addr == common.Address{}
+}
+
+func roundRobinProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator {
+ if valSet.Size() == 0 {
+ return nil
+ }
+ seed := uint64(0)
+ if emptyAddress(proposer) {
+ seed = round
+ } else {
+ seed = calcSeed(valSet, proposer, round) + 1
+ }
+ pick := seed % uint64(valSet.Size())
+ return valSet.GetByIndex(pick)
+}
+
+func stickyProposer(valSet istanbul.ValidatorSet, proposer common.Address, round uint64) istanbul.Validator {
+ if valSet.Size() == 0 {
+ return nil
+ }
+ seed := uint64(0)
+ if emptyAddress(proposer) {
+ seed = round
+ } else {
+ seed = calcSeed(valSet, proposer, round)
+ }
+ pick := seed % uint64(valSet.Size())
+ return valSet.GetByIndex(pick)
+}
+
+func (valSet *defaultSet) AddValidator(address common.Address) bool {
+ valSet.validatorMu.Lock()
+ defer valSet.validatorMu.Unlock()
+ for _, v := range valSet.validators {
+ if v.Address() == address {
+ return false
+ }
+ }
+ valSet.validators = append(valSet.validators, New(address))
+ // TODO: we may not need to re-sort it again
+ // sort validator
+ sort.Sort(valSet.validators)
+ return true
+}
+
+func (valSet *defaultSet) RemoveValidator(address common.Address) bool {
+ valSet.validatorMu.Lock()
+ defer valSet.validatorMu.Unlock()
+
+ for i, v := range valSet.validators {
+ if v.Address() == address {
+ valSet.validators = append(valSet.validators[:i], valSet.validators[i+1:]...)
+ return true
+ }
+ }
+ return false
+}
+
+func (valSet *defaultSet) Copy() istanbul.ValidatorSet {
+ valSet.validatorMu.RLock()
+ defer valSet.validatorMu.RUnlock()
+
+ addresses := make([]common.Address, 0, len(valSet.validators))
+ for _, v := range valSet.validators {
+ addresses = append(addresses, v.Address())
+ }
+ return NewSet(addresses, valSet.policy)
+}
+
+func (valSet *defaultSet) F() int { return int(math.Ceil(float64(valSet.Size())/3)) - 1 }
+
+func (valSet *defaultSet) Policy() istanbul.ProposerPolicy { return valSet.policy }
diff --git a/consensus/istanbul/validator/default_test.go b/consensus/istanbul/validator/default_test.go
new file mode 100644
index 0000000000..987ed12c84
--- /dev/null
+++ b/consensus/istanbul/validator/default_test.go
@@ -0,0 +1,208 @@
+// Copyright 2017 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 .
+
+package validator
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/istanbul"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+var (
+ testAddress = "70524d664ffe731100208a0154e556f9bb679ae6"
+ testAddress2 = "b37866a925bccd69cfa98d43b510f1d23d78a851"
+)
+
+func TestValidatorSet(t *testing.T) {
+ testNewValidatorSet(t)
+ testNormalValSet(t)
+ testEmptyValSet(t)
+ testStickyProposer(t)
+ testAddAndRemoveValidator(t)
+}
+
+func testNewValidatorSet(t *testing.T) {
+ var validators []istanbul.Validator
+ const ValCnt = 100
+
+ // Create 100 validators with random addresses
+ b := []byte{}
+ for i := 0; i < ValCnt; i++ {
+ key, _ := crypto.GenerateKey()
+ addr := crypto.PubkeyToAddress(key.PublicKey)
+ val := New(addr)
+ validators = append(validators, val)
+ b = append(b, val.Address().Bytes()...)
+ }
+
+ // Create ValidatorSet
+ valSet := NewSet(ExtractValidators(b), istanbul.RoundRobin)
+ if valSet == nil {
+ t.Errorf("the validator byte array cannot be parsed")
+ t.FailNow()
+ }
+
+ // Check validators sorting: should be in ascending order
+ for i := 0; i < ValCnt-1; i++ {
+ val := valSet.GetByIndex(uint64(i))
+ nextVal := valSet.GetByIndex(uint64(i + 1))
+ if strings.Compare(val.String(), nextVal.String()) >= 0 {
+ t.Errorf("validator set is not sorted in ascending order")
+ }
+ }
+}
+
+func testNormalValSet(t *testing.T) {
+ b1 := common.Hex2Bytes(testAddress)
+ b2 := common.Hex2Bytes(testAddress2)
+ addr1 := common.BytesToAddress(b1)
+ addr2 := common.BytesToAddress(b2)
+ val1 := New(addr1)
+ val2 := New(addr2)
+
+ valSet := newDefaultSet([]common.Address{addr1, addr2}, istanbul.RoundRobin)
+ if valSet == nil {
+ t.Errorf("the format of validator set is invalid")
+ t.FailNow()
+ }
+
+ // check size
+ if size := valSet.Size(); size != 2 {
+ t.Errorf("the size of validator set is wrong: have %v, want 2", size)
+ }
+ // test get by index
+ if val := valSet.GetByIndex(uint64(0)); !reflect.DeepEqual(val, val1) {
+ t.Errorf("validator mismatch: have %v, want %v", val, val1)
+ }
+ // test get by invalid index
+ if val := valSet.GetByIndex(uint64(2)); val != nil {
+ t.Errorf("validator mismatch: have %v, want nil", val)
+ }
+ // test get by address
+ if _, val := valSet.GetByAddress(addr2); !reflect.DeepEqual(val, val2) {
+ t.Errorf("validator mismatch: have %v, want %v", val, val2)
+ }
+ // test get by invalid address
+ invalidAddr := common.HexToAddress("0x9535b2e7faaba5288511d89341d94a38063a349b")
+ if _, val := valSet.GetByAddress(invalidAddr); val != nil {
+ t.Errorf("validator mismatch: have %v, want nil", val)
+ }
+ // test get proposer
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val1)
+ }
+ // test calculate proposer
+ lastProposer := addr1
+ valSet.CalcProposer(lastProposer, uint64(0))
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val2)
+ }
+ valSet.CalcProposer(lastProposer, uint64(3))
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val1)
+ }
+ // test empty last proposer
+ lastProposer = common.Address{}
+ valSet.CalcProposer(lastProposer, uint64(3))
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val2)
+ }
+}
+
+func testEmptyValSet(t *testing.T) {
+ valSet := NewSet(ExtractValidators([]byte{}), istanbul.RoundRobin)
+ if valSet == nil {
+ t.Errorf("validator set should not be nil")
+ }
+}
+
+func testAddAndRemoveValidator(t *testing.T) {
+ valSet := NewSet(ExtractValidators([]byte{}), istanbul.RoundRobin)
+ if !valSet.AddValidator(common.StringToAddress(string(2))) {
+ t.Error("the validator should be added")
+ }
+ if valSet.AddValidator(common.StringToAddress(string(2))) {
+ t.Error("the existing validator should not be added")
+ }
+ valSet.AddValidator(common.StringToAddress(string(1)))
+ valSet.AddValidator(common.StringToAddress(string(0)))
+ if len(valSet.List()) != 3 {
+ t.Error("the size of validator set should be 3")
+ }
+
+ for i, v := range valSet.List() {
+ expected := common.StringToAddress(string(i))
+ if v.Address() != expected {
+ t.Errorf("the order of validators is wrong: have %v, want %v", v.Address().Hex(), expected.Hex())
+ }
+ }
+
+ if !valSet.RemoveValidator(common.StringToAddress(string(2))) {
+ t.Error("the validator should be removed")
+ }
+ if valSet.RemoveValidator(common.StringToAddress(string(2))) {
+ t.Error("the non-existing validator should not be removed")
+ }
+ if len(valSet.List()) != 2 {
+ t.Error("the size of validator set should be 2")
+ }
+ valSet.RemoveValidator(common.StringToAddress(string(1)))
+ if len(valSet.List()) != 1 {
+ t.Error("the size of validator set should be 1")
+ }
+ valSet.RemoveValidator(common.StringToAddress(string(0)))
+ if len(valSet.List()) != 0 {
+ t.Error("the size of validator set should be 0")
+ }
+}
+
+func testStickyProposer(t *testing.T) {
+ b1 := common.Hex2Bytes(testAddress)
+ b2 := common.Hex2Bytes(testAddress2)
+ addr1 := common.BytesToAddress(b1)
+ addr2 := common.BytesToAddress(b2)
+ val1 := New(addr1)
+ val2 := New(addr2)
+
+ valSet := newDefaultSet([]common.Address{addr1, addr2}, istanbul.Sticky)
+
+ // test get proposer
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val1)
+ }
+ // test calculate proposer
+ lastProposer := addr1
+ valSet.CalcProposer(lastProposer, uint64(0))
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val1) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val1)
+ }
+
+ valSet.CalcProposer(lastProposer, uint64(1))
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val2)
+ }
+ // test empty last proposer
+ lastProposer = common.Address{}
+ valSet.CalcProposer(lastProposer, uint64(3))
+ if val := valSet.GetProposer(); !reflect.DeepEqual(val, val2) {
+ t.Errorf("proposer mismatch: have %v, want %v", val, val2)
+ }
+}
diff --git a/consensus/istanbul/validator/validator.go b/consensus/istanbul/validator/validator.go
new file mode 100644
index 0000000000..9a1e15c2d8
--- /dev/null
+++ b/consensus/istanbul/validator/validator.go
@@ -0,0 +1,47 @@
+// Copyright 2017 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 .
+
+package validator
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/istanbul"
+)
+
+func New(addr common.Address) istanbul.Validator {
+ return &defaultValidator{
+ address: addr,
+ }
+}
+
+func NewSet(addrs []common.Address, policy istanbul.ProposerPolicy) istanbul.ValidatorSet {
+ return newDefaultSet(addrs, policy)
+}
+
+func ExtractValidators(extraData []byte) []common.Address {
+ // get the validator addresses
+ addrs := make([]common.Address, (len(extraData) / common.AddressLength))
+ for i := 0; i < len(addrs); i++ {
+ copy(addrs[i][:], extraData[i*common.AddressLength:])
+ }
+
+ return addrs
+}
+
+// Check whether the extraData is presented in prescribed form
+func ValidExtraData(extraData []byte) bool {
+ return len(extraData)%common.AddressLength == 0
+}