Skip to content

Commit

Permalink
Set header.coinbase in proof-of-authority to etherbase (ethereum#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
asaj authored Aug 27, 2018
1 parent 6db3f93 commit b826ab3
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 42 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ jobs:
- checkout
- run: make lint
# TODO(celo): Use testing.Skip instead
- run: build/env.sh go run build/ci.go test --skip "github.com/ethereum/go-ethereum/cmd/swarm,github.com/ethereum/go-ethereum/swarm/network/simulations/discovery"
- run: build/env.sh go run build/ci.go test --skip "github.com/ethereum/go-ethereum/cmd/swarm,github.com/ethereum/go-ethereum/swarm/network/simulations/discovery,github.com/ethereum/go-ethereum/swarm/network/stream"
4 changes: 2 additions & 2 deletions cmd/puppeth/wizard_genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ func (w *wizard) makeGenesis() {
}
}
}
genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65)
genesis.ExtraData = make([]byte, 52+len(signers)*common.AddressLength+65)
for i, signer := range signers {
copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:])
copy(genesis.ExtraData[52+i*common.AddressLength:], signer[:])
}

default:
Expand Down
64 changes: 45 additions & 19 deletions consensus/clique/clique.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@ const (
var (
epochLength = uint64(30000) // Default number of blocks after which to checkpoint and reset the pending votes

extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal
extraVanity = 32 // Fixed number of extra-data prefix bytes reserved for signer vanity
extraProposedSigner = 20 // Fixed number of extra-data prefix bytes reserved for proposed signer. Comes after extraVanity.
extraPrefix = extraVanity + extraProposedSigner // The number of extra-data prefix bytes reserved for the vaniry and proposed signer.
extraSeal = 65 // Fixed number of extra-data suffix bytes reserved for signer seal

nonceAuthVote = hexutil.MustDecode("0xffffffffffffffff") // Magic nonce number to vote on adding a new signer
nonceDropVote = hexutil.MustDecode("0x0000000000000000") // Magic nonce number to vote on removing a signer.
Expand All @@ -75,9 +77,9 @@ var (
// that is not part of the local blockchain.
errUnknownBlock = errors.New("unknown block")

// errInvalidCheckpointBeneficiary is returned if a checkpoint/epoch transition
// block has a beneficiary set to non-zeroes.
errInvalidCheckpointBeneficiary = errors.New("beneficiary in checkpoint block non-zero")
// errInvalidCheckpointProposedSigner is returned if a checkpoint/epoch transition
// block has a proposed signer set to non-zeroes.
errInvalidCheckpointProposedSigner = errors.New("proposed signer in checkpoint block non-zero")

// errInvalidVote is returned if a nonce value is something else that the two
// allowed constants of 0x00..0 or 0xff..f.
Expand All @@ -91,6 +93,10 @@ var (
// 32 bytes, which is required to store the signer vanity.
errMissingVanity = errors.New("extra-data 32 byte vanity prefix missing")

// errMissingProposedSigner is returned if a block's extra-data section is shorter than
// 20 bytes, which is required to store the signer's proposed signer.
errMissingProposedSigner = errors.New("extra-data 20 byte proposed signer prefix missing")

// errMissingSignature is returned if a block's extra-data section doesn't seem
// to contain a 65 byte secp256k1 signature.
errMissingSignature = errors.New("extra-data 65 byte suffix signature missing")
Expand Down Expand Up @@ -191,6 +197,23 @@ func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, er
return signer, nil
}

// Insert the proposed signer into the header extra data.
func SetProposedSigner(extra []byte, signer common.Address) []byte {
if len(extra) < (extraPrefix) {
extra = append(extra, bytes.Repeat([]byte{0x00}, (extraPrefix)-len(extra))...)
}
copy(extra[extraVanity:extraPrefix], signer.Bytes())
return extra
}

// Return the proposed signer from the header extra data.
func ProposedSigner(extra []byte) common.Address {
if len(extra) < extraPrefix {
return common.Address{}
}
return common.BytesToAddress(extra[extraVanity:extraPrefix])
}

// Clique is the proof-of-authority consensus engine proposed to support the
// Ethereum testnet following the Ropsten attacks.
type Clique struct {
Expand Down Expand Up @@ -274,10 +297,10 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
return consensus.ErrFutureBlock
}
// Checkpoint blocks need to enforce zero beneficiary
// Checkpoint blocks need to enforce zero proposed signer
checkpoint := (number % c.config.Epoch) == 0
if checkpoint && header.Coinbase != (common.Address{}) {
return errInvalidCheckpointBeneficiary
if checkpoint && ProposedSigner(header.Extra) != (common.Address{}) {
return errInvalidCheckpointProposedSigner
}
// Nonces must be 0x00..0 or 0xff..f, zeroes enforced on checkpoints
if !bytes.Equal(header.Nonce[:], nonceAuthVote) && !bytes.Equal(header.Nonce[:], nonceDropVote) {
Expand All @@ -290,11 +313,14 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
if len(header.Extra) < extraVanity {
return errMissingVanity
}
if len(header.Extra) < extraVanity+extraSeal {
if len(header.Extra) < extraPrefix {
return errMissingProposedSigner
}
if len(header.Extra) < extraPrefix+extraSeal {
return errMissingSignature
}
// Ensure that the extra-data contains a signer list on checkpoint, but none otherwise
signersBytes := len(header.Extra) - extraVanity - extraSeal
signersBytes := len(header.Extra) - extraPrefix - extraSeal
if !checkpoint && signersBytes != 0 {
return errExtraSigners
}
Expand Down Expand Up @@ -358,7 +384,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
copy(signers[i*common.AddressLength:], signer[:])
}
extraSuffix := len(header.Extra) - extraSeal
if !bytes.Equal(header.Extra[extraVanity:extraSuffix], signers) {
if !bytes.Equal(header.Extra[extraPrefix:extraSuffix], signers) {
return errInvalidCheckpointSigners
}
}
Expand Down Expand Up @@ -393,9 +419,9 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
if checkpoint != nil {
hash := checkpoint.Hash()

signers := make([]common.Address, (len(checkpoint.Extra)-extraVanity-extraSeal)/common.AddressLength)
signers := make([]common.Address, (len(checkpoint.Extra)-extraPrefix-extraSeal)/common.AddressLength)
for i := 0; i < len(signers); i++ {
copy(signers[i][:], checkpoint.Extra[extraVanity+i*common.AddressLength:])
copy(signers[i][:], checkpoint.Extra[extraPrefix+i*common.AddressLength:])
}
snap = newSnapshot(c.config, c.signatures, number, hash, signers)
if err := snap.store(c.db); err != nil {
Expand Down Expand Up @@ -506,7 +532,7 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
// header for running the transactions on top.
func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) error {
// If the block isn't a checkpoint, cast a random vote (good enough for now)
header.Coinbase = common.Address{}
header.Extra = SetProposedSigner(header.Extra, common.Address{})
header.Nonce = types.BlockNonce{}

number := header.Number.Uint64()
Expand All @@ -527,8 +553,8 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
}
// If there's pending proposals, cast a vote on them
if len(addresses) > 0 {
header.Coinbase = addresses[rand.Intn(len(addresses))]
if c.proposals[header.Coinbase] {
header.Extra = SetProposedSigner(header.Extra, addresses[rand.Intn(len(addresses))])
if c.proposals[ProposedSigner(header.Extra)] {
copy(header.Nonce[:], nonceAuthVote)
} else {
copy(header.Nonce[:], nonceDropVote)
Expand All @@ -540,10 +566,10 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
header.Difficulty = CalcDifficulty(snap, c.signer)

// Ensure the extra data has all it's components
if len(header.Extra) < extraVanity {
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraVanity-len(header.Extra))...)
if len(header.Extra) < extraPrefix {
header.Extra = append(header.Extra, bytes.Repeat([]byte{0x00}, extraPrefix-len(header.Extra))...)
}
header.Extra = header.Extra[:extraVanity]
header.Extra = header.Extra[:extraPrefix]

if number%c.config.Epoch == 0 {
for _, signer := range snap.signers() {
Expand Down
19 changes: 10 additions & 9 deletions consensus/clique/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,9 +223,10 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
}
snap.Recents[number] = signer

proposedSigner := ProposedSigner(header.Extra)
// Header authorized, discard any previous votes from the signer
for i, vote := range snap.Votes {
if vote.Signer == signer && vote.Address == header.Coinbase {
if vote.Signer == signer && vote.Address == proposedSigner {
// Uncast the vote from the cached tally
snap.uncast(vote.Address, vote.Authorize)

Expand All @@ -244,28 +245,28 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
default:
return nil, errInvalidVote
}
if snap.cast(header.Coinbase, authorize) {
if snap.cast(proposedSigner, authorize) {
snap.Votes = append(snap.Votes, &Vote{
Signer: signer,
Block: number,
Address: header.Coinbase,
Address: proposedSigner,
Authorize: authorize,
})
}
// If the vote passed, update the list of signers
if tally := snap.Tally[header.Coinbase]; tally.Votes > len(snap.Signers)/2 {
if tally := snap.Tally[proposedSigner]; tally.Votes > len(snap.Signers)/2 {
if tally.Authorize {
snap.Signers[header.Coinbase] = struct{}{}
snap.Signers[proposedSigner] = struct{}{}
} else {
delete(snap.Signers, header.Coinbase)
delete(snap.Signers, proposedSigner)

// Signer list shrunk, delete any leftover recent caches
if limit := uint64(len(snap.Signers)/2 + 1); number >= limit {
delete(snap.Recents, number-limit)
}
// Discard any previous votes the deauthorized signer cast
for i := 0; i < len(snap.Votes); i++ {
if snap.Votes[i].Signer == header.Coinbase {
if snap.Votes[i].Signer == proposedSigner {
// Uncast the vote from the cached tally
snap.uncast(snap.Votes[i].Address, snap.Votes[i].Authorize)

Expand All @@ -278,12 +279,12 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) {
}
// Discard any previous votes around the just changed account
for i := 0; i < len(snap.Votes); i++ {
if snap.Votes[i].Address == header.Coinbase {
if snap.Votes[i].Address == proposedSigner {
snap.Votes = append(snap.Votes[:i], snap.Votes[i+1:]...)
i--
}
}
delete(snap.Tally, header.Coinbase)
delete(snap.Tally, proposedSigner)
}
}
snap.Number += uint64(len(headers))
Expand Down
12 changes: 6 additions & 6 deletions consensus/clique/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,10 @@ func TestVoting(t *testing.T) {
}
// Create the genesis block with the initial set of signers
genesis := &core.Genesis{
ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal),
ExtraData: make([]byte, extraPrefix+common.AddressLength*len(signers)+extraSeal),
}
for j, signer := range signers {
copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:])
copy(genesis.ExtraData[extraPrefix+j*common.AddressLength:], signer[:])
}
// Create a pristine blockchain with the genesis injected
db := ethdb.NewMemDatabase()
Expand All @@ -359,11 +359,11 @@ func TestVoting(t *testing.T) {
headers := make([]*types.Header, len(tt.votes))
for j, vote := range tt.votes {
headers[j] = &types.Header{
Number: big.NewInt(int64(j) + 1),
Time: big.NewInt(int64(j) * 15),
Coinbase: accounts.address(vote.voted),
Extra: make([]byte, extraVanity+extraSeal),
Number: big.NewInt(int64(j) + 1),
Time: big.NewInt(int64(j) * 15),
Extra: make([]byte, extraPrefix+extraSeal),
}
headers[j].Extra = SetProposedSigner(headers[j].Extra, accounts.address(vote.voted))
if j > 0 {
headers[j].ParentHash = headers[j-1].Hash()
}
Expand Down
4 changes: 2 additions & 2 deletions core/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func DefaultRinkebyGenesisBlock() *Genesis {
return &Genesis{
Config: params.RinkebyChainConfig,
Timestamp: 1492009146,
ExtraData: hexutil.MustDecode("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
ExtraData: hexutil.MustDecode("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e000000000000000000000000000000000000000042eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
GasLimit: 4700000,
Difficulty: big.NewInt(1),
Alloc: decodePrealloc(rinkebyAllocData),
Expand All @@ -343,7 +343,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
// Assemble and return the genesis with the precompiles and faucet pre-funded
return &Genesis{
Config: &config,
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, 65)...),
ExtraData: append(append(make([]byte, 52), faucet[:]...), make([]byte, 65)...),
GasLimit: 6283185,
Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
Expand Down
5 changes: 3 additions & 2 deletions miner/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine

switch engine.(type) {
case *clique.Clique:
gspec.ExtraData = make([]byte, 32+common.AddressLength+65)
copy(gspec.ExtraData[32:], testBankAddress[:])
gspec.ExtraData = make([]byte, 52+common.AddressLength+65)
copy(gspec.ExtraData[52:], testBankAddress[:])
copy(gspec.ExtraData[52:], testBankAddress[:])
case *ethash.Ethash:
default:
t.Fatal("unexpect consensus engine type")
Expand Down
2 changes: 1 addition & 1 deletion params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import (
var (
MainnetGenesisHash = common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
TestnetGenesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
RinkebyGenesisHash = common.HexToHash("0x6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177")
RinkebyGenesisHash = common.HexToHash("0x2cdb663cd9fc9d5cf03ec8dc2e3d25b9805a5ed88d6895fc2cb7b328737758f4")
)

var (
Expand Down

0 comments on commit b826ab3

Please sign in to comment.