| 
									
										
										
										
											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" | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 	"sort" | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	"testing" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/core" | 
					
						
							| 
									
										
										
										
											2018-09-24 15:57:49 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/rawdb" | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/vm" | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/params" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // 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), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | // checkpoint creates a Clique checkpoint signer section from the provided list | 
					
						
							|  |  |  | // of authorized signers and embeds it into the provided header. | 
					
						
							|  |  |  | func (ap *testerAccountPool) checkpoint(header *types.Header, signers []string) { | 
					
						
							|  |  |  | 	auths := make([]common.Address, len(signers)) | 
					
						
							|  |  |  | 	for i, signer := range signers { | 
					
						
							|  |  |  | 		auths[i] = ap.address(signer) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	sort.Sort(signersAscending(auths)) | 
					
						
							|  |  |  | 	for i, auth := range auths { | 
					
						
							|  |  |  | 		copy(header.Extra[extraVanity+i*common.AddressLength:], auth.Bytes()) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | // address retrieves the Ethereum address of a tester account by label, creating | 
					
						
							|  |  |  | // a new account if no previous one exists yet. | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | func (ap *testerAccountPool) address(account string) common.Address { | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 	// Return the zero account for non-addresses | 
					
						
							|  |  |  | 	if account == "" { | 
					
						
							|  |  |  | 		return common.Address{} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	// 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) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | // sign calculates a Clique digital signature for the given block and embeds it | 
					
						
							|  |  |  | // back into the header. | 
					
						
							|  |  |  | 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 | 
					
						
							| 
									
										
										
											
												accounts, eth, clique, signer: support for external signer API  (#18079)
* accounts, eth, clique: implement external backend + move sighash calc to backend
* signer: implement account_Version on external API
* accounts/external: enable ipc, add copyright
* accounts, internal, signer: formatting
* node: go fmt
* flags: disallow --dev in combo with --externalsigner
* accounts: remove clique-specific signing method, replace with more generic
* accounts, consensus: formatting + fix error in tests
* signer/core: remove (test-) import cycle
* clique: remove unused import
* accounts: remove CliqueHash and avoid dependency on package crypto
* consensus/clique: unduplicate header encoding
											
										 
											2019-02-05 11:23:57 +01:00
										 |  |  | 	sig, _ := crypto.Sign(SealHash(header).Bytes(), ap.accounts[signer]) | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 	copy(header.Extra[len(header.Extra)-extraSeal:], sig) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | // testerVote represents a single block signed by a parcitular account, where | 
					
						
							|  |  |  | // the account may or may not have cast a Clique vote. | 
					
						
							|  |  |  | type testerVote struct { | 
					
						
							|  |  |  | 	signer     string | 
					
						
							|  |  |  | 	voted      string | 
					
						
							|  |  |  | 	auth       bool | 
					
						
							|  |  |  | 	checkpoint []string | 
					
						
							|  |  |  | 	newbatch   bool | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | // Tests that Clique signer voting is evaluated correctly for various simple and | 
					
						
							|  |  |  | // complex scenarios, as well as that a few special corner cases fail correctly. | 
					
						
							|  |  |  | func TestClique(t *testing.T) { | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	// Define the various voting scenarios to test | 
					
						
							|  |  |  | 	tests := []struct { | 
					
						
							|  |  |  | 		epoch   uint64 | 
					
						
							|  |  |  | 		signers []string | 
					
						
							|  |  |  | 		votes   []testerVote | 
					
						
							|  |  |  | 		results []string | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 		failure error | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 	}{ | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			// 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{ | 
					
						
							| 
									
										
										
										
											2019-03-06 12:42:08 +02:00
										 |  |  | 				{signer: "C", voted: "D", auth: true}, | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 				{signer: "A", voted: "C", auth: false}, | 
					
						
							|  |  |  | 				{signer: "B", voted: "C", auth: false}, | 
					
						
							| 
									
										
										
										
											2019-03-06 12:42:08 +02:00
										 |  |  | 				{signer: "A", voted: "D", auth: true}, | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			}, | 
					
						
							|  |  |  | 			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"}, | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 				{signer: "A", checkpoint: []string{"A", "B"}}, | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 				{signer: "B", voted: "C", auth: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			results: []string{"A", "B"}, | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 		}, { | 
					
						
							|  |  |  | 			// An unauthorized signer should not be able to sign blocks | 
					
						
							|  |  |  | 			signers: []string{"A"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			failure: errUnauthorizedSigner, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// An authorized signer that signed recenty should not be able to sign again | 
					
						
							|  |  |  | 			signers: []string{"A", "B"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			failure: errRecentlySigned, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Recent signatures should not reset on checkpoint blocks imported in a batch | 
					
						
							|  |  |  | 			epoch:   3, | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", checkpoint: []string{"A", "B", "C"}}, | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			failure: errRecentlySigned, | 
					
						
							|  |  |  | 		}, { | 
					
						
							|  |  |  | 			// Recent signatures should not reset on checkpoint blocks imported in a new | 
					
						
							|  |  |  | 			// batch (https://github.com/ethereum/go-ethereum/issues/17593). Whilst this | 
					
						
							|  |  |  | 			// seems overly specific and weird, it was a Rinkeby consensus split. | 
					
						
							|  |  |  | 			epoch:   3, | 
					
						
							|  |  |  | 			signers: []string{"A", "B", "C"}, | 
					
						
							|  |  |  | 			votes: []testerVote{ | 
					
						
							|  |  |  | 				{signer: "A"}, | 
					
						
							|  |  |  | 				{signer: "B"}, | 
					
						
							|  |  |  | 				{signer: "A", checkpoint: []string{"A", "B", "C"}}, | 
					
						
							|  |  |  | 				{signer: "A", newbatch: true}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 			failure: errRecentlySigned, | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// 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-09-24 15:57:49 +03:00
										 |  |  | 		db := rawdb.NewMemoryDatabase() | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 		genesis.Commit(db) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Assemble a chain of headers from the cast votes | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 		config := *params.TestChainConfig | 
					
						
							|  |  |  | 		config.Clique = ¶ms.CliqueConfig{ | 
					
						
							|  |  |  | 			Period: 1, | 
					
						
							|  |  |  | 			Epoch:  tt.epoch, | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		engine := New(config.Clique, db) | 
					
						
							|  |  |  | 		engine.fakeDiff = true | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		blocks, _ := core.GenerateChain(&config, genesis.ToBlock(db), engine, db, len(tt.votes), func(j int, gen *core.BlockGen) { | 
					
						
							|  |  |  | 			// Cast the vote contained in this block | 
					
						
							|  |  |  | 			gen.SetCoinbase(accounts.address(tt.votes[j].voted)) | 
					
						
							|  |  |  | 			if tt.votes[j].auth { | 
					
						
							|  |  |  | 				var nonce types.BlockNonce | 
					
						
							|  |  |  | 				copy(nonce[:], nonceAuthVote) | 
					
						
							|  |  |  | 				gen.SetNonce(nonce) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 		}) | 
					
						
							|  |  |  | 		// Iterate through the blocks and seal them individually | 
					
						
							|  |  |  | 		for j, block := range blocks { | 
					
						
							| 
									
										
										
										
											2020-10-12 21:02:38 +08:00
										 |  |  | 			// Get the header and prepare it for signing | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 			header := block.Header() | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			if j > 0 { | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 				header.ParentHash = blocks[j-1].Hash() | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 			header.Extra = make([]byte, extraVanity+extraSeal) | 
					
						
							|  |  |  | 			if auths := tt.votes[j].checkpoint; auths != nil { | 
					
						
							|  |  |  | 				header.Extra = make([]byte, extraVanity+len(auths)*common.AddressLength+extraSeal) | 
					
						
							|  |  |  | 				accounts.checkpoint(header, auths) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			header.Difficulty = diffInTurn // Ignored, we just need a valid number | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Generate the signature, embed it into the header and the block | 
					
						
							|  |  |  | 			accounts.sign(header, tt.votes[j].signer) | 
					
						
							|  |  |  | 			blocks[j] = block.WithSeal(header) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// Split the blocks up into individual import batches (cornercase testing) | 
					
						
							|  |  |  | 		batches := [][]*types.Block{nil} | 
					
						
							|  |  |  | 		for j, block := range blocks { | 
					
						
							|  |  |  | 			if tt.votes[j].newbatch { | 
					
						
							|  |  |  | 				batches = append(batches, nil) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 			batches[len(batches)-1] = append(batches[len(batches)-1], block) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		// Pass all the headers through clique and ensure tallying succeeds | 
					
						
							| 
									
										
										
										
											2020-05-11 17:58:43 +02:00
										 |  |  | 		chain, err := core.NewBlockChain(db, nil, &config, engine, vm.Config{}, nil, nil) | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			t.Errorf("test %d: failed to create test chain: %v", i, err) | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		failed := false | 
					
						
							|  |  |  | 		for j := 0; j < len(batches)-1; j++ { | 
					
						
							|  |  |  | 			if k, err := chain.InsertChain(batches[j]); err != nil { | 
					
						
							|  |  |  | 				t.Errorf("test %d: failed to import batch %d, block %d: %v", i, j, k, err) | 
					
						
							|  |  |  | 				failed = true | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if failed { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure { | 
					
						
							|  |  |  | 			t.Errorf("test %d: failure mismatch: have %v, want %v", i, err, tt.failure) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if tt.failure != nil { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		// No failure was produced or requested, generate the final voting snapshot | 
					
						
							|  |  |  | 		head := blocks[len(blocks)-1] | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 		snap, err := engine.snapshot(chain, head.NumberU64(), head.Hash(), nil) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2018-09-10 17:12:39 +03:00
										 |  |  | 			t.Errorf("test %d: failed to retrieve voting snapshot: %v", i, err) | 
					
						
							| 
									
										
										
										
											2017-04-10 13:24:12 +03:00
										 |  |  | 			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]) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |