| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | // 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 <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package clique | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"crypto/ecdsa" | 
					
						
							|  |  |  | 	"math/big" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core" | 
					
						
							| 
									
										
										
										
											2018-05-07 14:35:06 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/rawdb" | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/params" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type testerVote struct { | 
					
						
							|  |  |  | 	signer string | 
					
						
							|  |  |  | 	voted  string | 
					
						
							|  |  |  | 	auth   bool | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testerAccountPool is a pool to maintain currently active tester accounts, | 
					
						
							|  |  |  | // mapped from textual names used in the tests below to actual Ethereum private | 
					
						
							|  |  |  | // keys capable of signing transactions. | 
					
						
							|  |  |  | type testerAccountPool struct { | 
					
						
							|  |  |  | 	accounts map[string]*ecdsa.PrivateKey | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func newTesterAccountPool() *testerAccountPool { | 
					
						
							|  |  |  | 	return &testerAccountPool{ | 
					
						
							|  |  |  | 		accounts: make(map[string]*ecdsa.PrivateKey), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ap *testerAccountPool) sign(header *types.Header, signer string) { | 
					
						
							|  |  |  | 	// Ensure we have a persistent key for the signer | 
					
						
							|  |  |  | 	if ap.accounts[signer] == nil { | 
					
						
							|  |  |  | 		ap.accounts[signer], _ = crypto.GenerateKey() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Sign the header and embed the signature in extra data | 
					
						
							|  |  |  | 	sig, _ := crypto.Sign(sigHash(header).Bytes(), ap.accounts[signer]) | 
					
						
							|  |  |  | 	copy(header.Extra[len(header.Extra)-65:], sig) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (ap *testerAccountPool) address(account string) common.Address { | 
					
						
							|  |  |  | 	// Ensure we have a persistent key for the account | 
					
						
							|  |  |  | 	if ap.accounts[account] == nil { | 
					
						
							|  |  |  | 		ap.accounts[account], _ = crypto.GenerateKey() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Resolve and return the Ethereum address | 
					
						
							|  |  |  | 	return crypto.PubkeyToAddress(ap.accounts[account].PublicKey) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // testerChainReader implements consensus.ChainReader to access the genesis | 
					
						
							|  |  |  | // block. All other methods and requests will panic. | 
					
						
							|  |  |  | type testerChainReader struct { | 
					
						
							|  |  |  | 	db ethdb.Database | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-24 13:40:42 +03:00
										 |  |  | func (r *testerChainReader) Config() *params.ChainConfig                 { return params.AllCliqueProtocolChanges } | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | func (r *testerChainReader) CurrentHeader() *types.Header                { panic("not supported") } | 
					
						
							|  |  |  | func (r *testerChainReader) GetHeader(common.Hash, uint64) *types.Header { panic("not supported") } | 
					
						
							|  |  |  | func (r *testerChainReader) GetBlock(common.Hash, uint64) *types.Block   { panic("not supported") } | 
					
						
							| 
									
										
										
										
											2017-04-12 15:37:10 +03:00
										 |  |  | func (r *testerChainReader) GetHeaderByHash(common.Hash) *types.Header   { panic("not supported") } | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | func (r *testerChainReader) GetHeaderByNumber(number uint64) *types.Header { | 
					
						
							|  |  |  | 	if number == 0 { | 
					
						
							| 
									
										
										
										
											2018-05-07 14:35:06 +03:00
										 |  |  | 		return rawdb.ReadHeader(r.db, rawdb.ReadCanonicalHash(r.db, 0), 0) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-08-21 14:39:28 +03:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Tests that voting is evaluated correctly for various simple and complex scenarios. | 
					
						
							|  |  |  | func TestVoting(t *testing.T) { | 
					
						
							|  |  |  | 	// Define the various voting scenarios to test | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		epoch   uint64 | 
					
						
							|  |  |  | 		signers []string | 
					
						
							|  |  |  | 		votes   []testerVote | 
					
						
							|  |  |  | 		results []string | 
					
						
							|  |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			// Single signer, no votes cast | 
					
						
							|  |  |  | 			signers: []string{"A"}, | 
					
						
							|  |  |  | 			votes:   []testerVote{{signer: "A"}}, | 
					
						
							|  |  |  | 			results: []string{"A"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Single signer, voting to add two others (only accept first, second needs 2 votes) | 
					
						
							|  |  |  | 			signers: []string{"A"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Two signers, voting to add three others (only accept first two, third needs 3 votes already) | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: true}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: true}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "E", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "E", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Single signer, dropping itself (weird, but one less cornercase by explicitly allowing this) | 
					
						
							|  |  |  | 			signers: []string{"A"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "A", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Two signers, actually needing mutual consent to drop either of them (not fulfilled) | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Two signers, actually needing mutual consent to drop either of them (fulfilled) | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "B", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Three signers, two of them deciding to drop the third | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Four signers, consensus of two not being enough to drop anyone | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Four signers, consensus of three already being enough to drop someone | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "D", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Authorizations are counted once per signer per target | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Authorizing multiple accounts concurrently is permitted | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: true}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Deauthorizations are counted once per signer per target | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Deauthorizing multiple accounts concurrently is permitted | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Votes from deauthorized signers are discarded immediately (deauth votes) | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "C", voted: "B", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Votes from deauthorized signers are discarded immediately (auth votes) | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "C", voted: "B", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "B", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 			// Cascading changes are not allowed, only the account being voted on may change | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			signers: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "D", auth: false}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Changes reaching consensus out of bounds (via a deauth) execute on touch | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Changes reaching consensus out of bounds (via a deauth) may go out of consensus on first touch | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C", "D"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C"}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "D", auth: false}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Ensure that pending votes don't survive authorization status changes. This | 
					
						
							| 
									
										
										
										
											2017-06-12 14:45:17 +02:00
										 |  |  | 			// corner case can only appear if a signer is quickly added, removed and then | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			// readded (or the inverse), while one of the original voters dropped. If a | 
					
						
							|  |  |  | 			// past vote is left cached in the system somewhere, this will interfere with | 
					
						
							|  |  |  | 			// the final signer outcome. | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C", "D", "E"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "F", auth: true}, // Authorize F, 3 votes needed | 
					
						
							|  |  |  | 				{signer: "B", voted: "F", auth: true}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "F", auth: true}, | 
					
						
							|  |  |  | 				{signer: "D", voted: "F", auth: false}, // Deauthorize F, 4 votes needed (leave A's previous vote "unchanged") | 
					
						
							|  |  |  | 				{signer: "E", voted: "F", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "F", auth: false}, | 
					
						
							|  |  |  | 				{signer: "C", voted: "F", auth: false}, | 
					
						
							|  |  |  | 				{signer: "D", voted: "F", auth: true}, // Almost authorize F, 2/3 votes needed | 
					
						
							|  |  |  | 				{signer: "E", voted: "F", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "A", auth: false}, // Deauthorize A, 3 votes needed | 
					
						
							|  |  |  | 				{signer: "C", voted: "A", auth: false}, | 
					
						
							|  |  |  | 				{signer: "D", voted: "A", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "F", auth: true}, // Finish authorizing F, 3/3 votes needed | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"B", "C", "D", "E", "F"}, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Epoch transitions reset all votes to allow chain checkpointing | 
					
						
							|  |  |  | 			epoch:   3, | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A", voted: "C", auth: true}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A"}, // Checkpoint block, (don't vote here, it's validated outside of snapshots) | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Run through the scenarios and test them | 
					
						
							|  |  |  | 	for i, tt := range tests { | 
					
						
							|  |  |  | 		// Create the account pool and generate the initial set of signers | 
					
						
							|  |  |  | 		accounts := newTesterAccountPool() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		signers := make([]common.Address, len(tt.signers)) | 
					
						
							|  |  |  | 		for j, signer := range tt.signers { | 
					
						
							|  |  |  | 			signers[j] = accounts.address(signer) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := 0; j < len(signers); j++ { | 
					
						
							|  |  |  | 			for k := j + 1; k < len(signers); k++ { | 
					
						
							|  |  |  | 				if bytes.Compare(signers[j][:], signers[k][:]) > 0 { | 
					
						
							|  |  |  | 					signers[j], signers[k] = signers[k], signers[j] | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Create the genesis block with the initial set of signers | 
					
						
							|  |  |  | 		genesis := &core.Genesis{ | 
					
						
							|  |  |  | 			ExtraData: make([]byte, extraVanity+common.AddressLength*len(signers)+extraSeal), | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j, signer := range signers { | 
					
						
							|  |  |  | 			copy(genesis.ExtraData[extraVanity+j*common.AddressLength:], signer[:]) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Create a pristine blockchain with the genesis injected | 
					
						
							| 
									
										
										
										
											2018-05-09 20:24:25 +08:00
										 |  |  | 		db := ethdb.NewMemDatabase() | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 		genesis.Commit(db) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Assemble a chain of headers from the cast votes | 
					
						
							|  |  |  | 		headers := make([]*types.Header, len(tt.votes)) | 
					
						
							|  |  |  | 		for j, vote := range tt.votes { | 
					
						
							|  |  |  | 			headers[j] = &types.Header{ | 
					
						
							|  |  |  | 				Number:   big.NewInt(int64(j) + 1), | 
					
						
							| 
									
										
										
										
											2018-07-24 08:14:15 +08:00
										 |  |  | 				Time:     big.NewInt(int64(j) * 15), | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 				Coinbase: accounts.address(vote.voted), | 
					
						
							|  |  |  | 				Extra:    make([]byte, extraVanity+extraSeal), | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if j > 0 { | 
					
						
							|  |  |  | 				headers[j].ParentHash = headers[j-1].Hash() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if vote.auth { | 
					
						
							|  |  |  | 				copy(headers[j].Nonce[:], nonceAuthVote) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			accounts.sign(headers[j], vote.signer) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Pass all the headers through clique and ensure tallying succeeds | 
					
						
							|  |  |  | 		head := headers[len(headers)-1] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		snap, err := New(¶ms.CliqueConfig{Epoch: tt.epoch}, db).snapshot(&testerChainReader{db: db}, head.Number.Uint64(), head.Hash(), headers) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Errorf("test %d: failed to create voting snapshot: %v", i, err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Verify the final list of signers against the expected ones | 
					
						
							|  |  |  | 		signers = make([]common.Address, len(tt.results)) | 
					
						
							|  |  |  | 		for j, signer := range tt.results { | 
					
						
							|  |  |  | 			signers[j] = accounts.address(signer) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := 0; j < len(signers); j++ { | 
					
						
							|  |  |  | 			for k := j + 1; k < len(signers); k++ { | 
					
						
							|  |  |  | 				if bytes.Compare(signers[j][:], signers[k][:]) > 0 { | 
					
						
							|  |  |  | 					signers[j], signers[k] = signers[k], signers[j] | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		result := snap.signers() | 
					
						
							|  |  |  | 		if len(result) != len(signers) { | 
					
						
							|  |  |  | 			t.Errorf("test %d: signers mismatch: have %x, want %x", i, result, signers) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for j := 0; j < len(result); j++ { | 
					
						
							|  |  |  | 			if !bytes.Equal(result[j][:], signers[j][:]) { | 
					
						
							|  |  |  | 				t.Errorf("test %d, signer %d: signer mismatch: have %x, want %x", i, j, result[j], signers[j]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |