@@ -27,6 +27,7 @@ import (
27
27
"sync"
28
28
"time"
29
29
30
+ "github.com/ethereum/go-ethereum/accounts"
30
31
"github.com/ethereum/go-ethereum/common"
31
32
"github.com/ethereum/go-ethereum/common/hexutil"
32
33
"github.com/ethereum/go-ethereum/common/lru"
@@ -50,6 +51,8 @@ const (
50
51
checkpointInterval = 1024 // Number of blocks after which to save the vote snapshot to the database
51
52
inmemorySnapshots = 128 // Number of recent vote snapshots to keep in memory
52
53
inmemorySignatures = 4096 // Number of recent block signatures to keep in memory
54
+
55
+ wiggleTime = 500 * time .Millisecond // Random delay (per signer) to allow concurrent signers
53
56
)
54
57
55
58
// Clique proof-of-authority protocol constants.
@@ -138,6 +141,9 @@ var (
138
141
errRecentlySigned = errors .New ("recently signed" )
139
142
)
140
143
144
+ // SignerFn hashes and signs the data to be signed by a backing account.
145
+ type SignerFn func (signer accounts.Account , mimeType string , message []byte ) ([]byte , error )
146
+
141
147
// ecrecover extracts the Ethereum account address from a signed header.
142
148
func ecrecover (header * types.Header , sigcache * sigLRU ) (common.Address , error ) {
143
149
// If the signature's already cached, return that
@@ -179,6 +185,7 @@ type Clique struct {
179
185
proposals map [common.Address ]bool // Current list of proposals we are pushing
180
186
181
187
signer common.Address // Ethereum address of the signing key
188
+ signFn SignerFn // Signer function to authorize hashes with
182
189
lock sync.RWMutex // Protects the signer and proposals fields
183
190
184
191
// The fields below are for testing only
@@ -635,17 +642,82 @@ func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *
635
642
636
643
// Authorize injects a private key into the consensus engine to mint new blocks
637
644
// with.
638
- func (c * Clique ) Authorize (signer common.Address ) {
645
+ func (c * Clique ) Authorize (signer common.Address , signFn SignerFn ) {
639
646
c .lock .Lock ()
640
647
defer c .lock .Unlock ()
641
648
642
649
c .signer = signer
650
+ c .signFn = signFn
643
651
}
644
652
645
653
// Seal implements consensus.Engine, attempting to create a sealed block using
646
654
// the local signing credentials.
647
655
func (c * Clique ) Seal (chain consensus.ChainHeaderReader , block * types.Block , results chan <- * types.Block , stop <- chan struct {}) error {
648
- panic ("clique (poa) sealing not supported any more" )
656
+ header := block .Header ()
657
+
658
+ // Sealing the genesis block is not supported
659
+ number := header .Number .Uint64 ()
660
+ if number == 0 {
661
+ return errUnknownBlock
662
+ }
663
+ // For 0-period chains, refuse to seal empty blocks (no reward but would spin sealing)
664
+ if c .config .Period == 0 && len (block .Transactions ()) == 0 {
665
+ return errors .New ("sealing paused while waiting for transactions" )
666
+ }
667
+ // Don't hold the signer fields for the entire sealing procedure
668
+ c .lock .RLock ()
669
+ signer , signFn := c .signer , c .signFn
670
+ c .lock .RUnlock ()
671
+
672
+ // Bail out if we're unauthorized to sign a block
673
+ snap , err := c .snapshot (chain , number - 1 , header .ParentHash , nil )
674
+ if err != nil {
675
+ return err
676
+ }
677
+ if _ , authorized := snap .Signers [signer ]; ! authorized {
678
+ return errUnauthorizedSigner
679
+ }
680
+ // If we're amongst the recent signers, wait for the next block
681
+ for seen , recent := range snap .Recents {
682
+ if recent == signer {
683
+ // Signer is among recents, only wait if the current block doesn't shift it out
684
+ if limit := uint64 (len (snap .Signers )/ 2 + 1 ); number < limit || seen > number - limit {
685
+ return errors .New ("signed recently, must wait for others" )
686
+ }
687
+ }
688
+ }
689
+ // Sweet, the protocol permits us to sign the block, wait for our time
690
+ delay := time .Unix (int64 (header .Time ), 0 ).Sub (time .Now ()) // nolint: gosimple
691
+ if header .Difficulty .Cmp (diffNoTurn ) == 0 {
692
+ // It's not our turn explicitly to sign, delay it a bit
693
+ wiggle := time .Duration (len (snap .Signers )/ 2 + 1 ) * wiggleTime
694
+ delay += time .Duration (rand .Int63n (int64 (wiggle )))
695
+
696
+ log .Trace ("Out-of-turn signing requested" , "wiggle" , common .PrettyDuration (wiggle ))
697
+ }
698
+ // Sign all the things!
699
+ sighash , err := signFn (accounts.Account {Address : signer }, accounts .MimetypeClique , CliqueRLP (header ))
700
+ if err != nil {
701
+ return err
702
+ }
703
+ copy (header .Extra [len (header .Extra )- extraSeal :], sighash )
704
+ // Wait until sealing is terminated or delay timeout.
705
+ log .Trace ("Waiting for slot to sign and propagate" , "delay" , common .PrettyDuration (delay ))
706
+ go func () {
707
+ select {
708
+ case <- stop :
709
+ return
710
+ case <- time .After (delay ):
711
+ }
712
+
713
+ select {
714
+ case results <- block .WithSeal (header ):
715
+ default :
716
+ log .Warn ("Sealing result is not read by miner" , "sealhash" , SealHash (header ))
717
+ }
718
+ }()
719
+
720
+ return nil
649
721
}
650
722
651
723
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
0 commit comments