@@ -20,6 +20,7 @@ package dccs
2020import (
2121 "bytes"
2222 "errors"
23+ "fmt"
2324 "math"
2425 "math/big"
2526 "time"
@@ -42,6 +43,14 @@ import (
4243 "github.com/ethereum/go-ethereum/params"
4344)
4445
46+ var (
47+ errSnapshotNotAvailable = errors .New ("Snapshot contract not available" )
48+ errNotCheckpoint = errors .New ("Not a checkpoint block" )
49+ errNotSnapshot = errors .New ("Not a snapshot block" )
50+ errMissingHeaderExtra = errors .New ("missing extra data in header" )
51+ errMissingCheckpointSigners = errors .New ("missing signer list on checkpoint block" )
52+ )
53+
4554var (
4655 rewards = []* big.Int {
4756 big .NewInt (1e+4 ),
@@ -148,7 +157,7 @@ func (d *Dccs) verifyCascadingFields1(chain consensus.ChainReader, header *types
148157 return ErrInvalidTimestamp
149158 }
150159 // Retrieve the snapshot needed to verify this header and cache it
151- snap , err := d .snapshot1 (chain , number - 1 , header . ParentHash , parents )
160+ snap , err := d .snapshot1 (chain , header , parents )
152161 if err != nil {
153162 return err
154163 }
@@ -168,66 +177,150 @@ func (d *Dccs) verifyCascadingFields1(chain consensus.ChainReader, header *types
168177}
169178
170179// snapshot1 retrieves the authorization snapshot at a given point in time.
171- func (d * Dccs ) snapshot1 (chain consensus.ChainReader , number uint64 , hash common.Hash , parents []* types.Header ) (* Snapshot , error ) {
172- // Search for a snapshot in memory or on disk for checkpoints
173- var (
174- snap * Snapshot
175- )
176- cp := d .config .Snapshot (number + 1 )
177- // looping until data/state are available on local
178- for snap == nil {
179- // Get signers from Nexty staking smart contract at the latest epoch checkpoint from block number
180- checkpoint := chain .GetHeaderByNumber (cp )
181- if checkpoint == nil {
182- log .Trace ("snapshot header not available" , "number" , cp )
183- continue
184- }
185- hash := checkpoint .Hash ()
186- log .Trace ("Reading signers from epoch checkpoint" , "number" , cp , "hash" , hash )
187- // If an in-memory snapshot was found, use that
188- if s , ok := d .recents .Get (hash ); ok && number + 1 != d .config .ThangLongBlock .Uint64 () {
189- snap = s .(* Snapshot )
190- log .Trace ("Loading snapshot from mem-cache" , "hash" , snap .Hash , "length" , len (snap .signers ()))
191- break
192- }
193- state , err := chain .StateAt (checkpoint .Root )
194- if state == nil || err != nil {
195- log .Trace ("snapshot state not available" , "number" , cp , "err" , err )
196- continue
197- }
198- size := state .GetCodeSize (chain .Config ().Dccs .Contract )
199- if size <= 0 || state .Error () != nil {
200- log .Trace ("snapshot contract state not available" , "number" , cp , "err" , state .Error ())
201- continue
202- }
203- index := common .BigToHash (common .Big0 )
204- result := state .GetState (chain .Config ().Dccs .Contract , index )
205- var length int64
206- if (result == common.Hash {}) {
207- length = 0
208- } else {
209- length = result .Big ().Int64 ()
210- }
211- log .Trace ("Total number of signer from staking smart contract" , "length" , length )
212- signers := make ([]common.Address , length )
213- key := crypto .Keccak256Hash (hexutil .MustDecode (index .String ()))
214- for i := 0 ; i < len (signers ); i ++ {
215- log .Trace ("key hash" , "key" , key )
216- singer := state .GetState (chain .Config ().Dccs .Contract , key )
217- signers [i ] = common .HexToAddress (singer .Hex ())
218- key = key .Plus ()
180+ //
181+ // Note: signers list for checkpoint block is gotten from it's snapshot block,
182+ // which is (checkpoint-canonicalDepth). This list is then recorded to the
183+ // checkpoint block extra itself.
184+ // So the signers list for block n can be retrieved from either:
185+ // 1. header extra of block checkpoint(n), or
186+ // 2. state of block snapshot(n) => this is required for miner
187+ func (d * Dccs ) snapshot1 (chain consensus.ChainReader , header * types.Header , parents []* types.Header ) (* Snapshot , error ) {
188+ number := header .Number .Uint64 ()
189+ ssNumber := d .config .Snapshot (number )
190+ ssHeader := getAvailableHeader (ssNumber , header , parents , chain )
191+ if ssHeader == nil {
192+ log .Error ("💀 Snapshot header missing" , "snapshot number" , ssNumber , "for number" , number )
193+ return nil , errSnapshotNotAvailable
194+ }
195+ if s , ok := d .recents .Get (ssHeader .Hash ()); ok {
196+ // in-memory snapshot found
197+ snap := s .(* Snapshot )
198+ log .Trace ("Snapshot loaded from mem-cache" , "snapshot number" , snap .Number , "hash" , snap .Hash , "signers length" , len (snap .Signers ), "for number" , header .Number )
199+ return snap , nil
200+ }
201+
202+ if d .config .IsCheckpoint (number ) {
203+ snap , err := d .getStateSnapshot (chain , ssHeader )
204+ if err == nil && snap != nil {
205+ log .Trace ("Snapshot retrieved from state and cached" , "for number" , header .Number , "snapshot number" , snap .Number , "hash" , snap .Hash )
206+ // Store found snapshot into mem-cache
207+ d .recents .Add (snap .Hash , snap )
208+ return snap , nil
219209 }
220- snap = newSnapshot (d .config , d .signatures , number , hash , signers )
221- // Store found snapshot into mem-cache
222- d .recents .Add (snap .Hash , snap )
223- break
210+ log .Warn ("☢ Snapshot state missing for checkpoint block, continue at your own risk" , "snapshot number" , ssNumber , "for number" , number , "err" , err )
224211 }
225212
226- // Set current block number for snapshot to calculate the inturn & difficulty
227- snap .Number = number
213+ snap , err := d .getHeaderSnapshotFor (header , chain , parents )
214+ if err != nil || snap == nil {
215+ return nil , err
216+ }
217+ // Store found snapshot into mem-cache
218+ d .recents .Add (snap .Hash , snap )
228219 return snap , nil
229220}
230221
222+ func (d * Dccs ) getStateSnapshot (chain consensus.ChainReader , header * types.Header ) (* Snapshot , error ) {
223+ number := header .Number .Uint64 ()
224+ state , err := chain .StateAt (header .Root )
225+ if state == nil || err != nil {
226+ log .Trace ("Snapshot state not available" , "number" , number , "err" , err )
227+ return nil , errSnapshotNotAvailable
228+ }
229+ size := state .GetCodeSize (chain .Config ().Dccs .Contract )
230+ if size <= 0 || state .Error () != nil {
231+ log .Trace ("Snapshot contract state not available" , "number" , number , "err" , state .Error ())
232+ return nil , errSnapshotNotAvailable
233+ }
234+ index := common .BigToHash (common .Big0 )
235+ result := state .GetState (chain .Config ().Dccs .Contract , index )
236+ var length int64
237+ if (result == common.Hash {}) {
238+ length = 0
239+ } else {
240+ length = result .Big ().Int64 ()
241+ }
242+ log .Trace ("Total number of signer from staking smart contract" , "length" , length )
243+ signers := make ([]common.Address , length )
244+ key := crypto .Keccak256Hash (hexutil .MustDecode (index .String ()))
245+ for i := 0 ; i < len (signers ); i ++ {
246+ log .Trace ("key hash" , "key" , key )
247+ singer := state .GetState (chain .Config ().Dccs .Contract , key )
248+ signers [i ] = common .HexToAddress (singer .Hex ())
249+ key = key .Plus ()
250+ }
251+ return newSnapshot (d .config , d .signatures , number , header .Hash (), signers ), nil
252+ }
253+
254+ // getHeaderFromInput returns either:
255+ // + the input header, if number == header.Number
256+ // + the header in parents if available (nessesary for batch headers processing)
257+ // + chain.GetHeaderByNumber(number), if all else fail
258+ func getAvailableHeader (number uint64 , header * types.Header , parents []* types.Header , chain consensus.ChainReader ) * types.Header {
259+ headerNumber := header .Number .Uint64 ()
260+ if number == headerNumber {
261+ return header
262+ }
263+ if number > headerNumber {
264+ return chain .GetHeaderByNumber (number )
265+ }
266+ idx := len (parents ) - int (headerNumber ) + int (number )
267+ if idx >= 0 {
268+ header := parents [idx ]
269+ if header .Number .Uint64 () == number {
270+ return header
271+ }
272+ log .Error ("invalid parrent number" , "expected" , number , "actual" , header .Number )
273+ }
274+ return chain .GetHeaderByNumber (number )
275+ }
276+
277+ func (d * Dccs ) getHeaderSnapshotFor (header * types.Header , chain consensus.ChainReader , parents []* types.Header ) (* Snapshot , error ) {
278+ number := header .Number .Uint64 ()
279+ cp := d .config .Checkpoint (number )
280+ cpHeader := getAvailableHeader (cp , header , parents , chain )
281+ if cpHeader == nil {
282+ return nil , fmt .Errorf ("checkpoint header missing: checkpoint = %v, header = %v" , cp , number )
283+ }
284+ ss := d .config .Snapshot (number )
285+ ssHeader := getAvailableHeader (ss , header , parents , chain )
286+ return d .getHeaderSnapshot (cpHeader , ssHeader )
287+ }
288+
289+ // the signer list is retrieved from checkpoint header extra,
290+ // but the snapshot hash is hash of snapshot header
291+ func (d * Dccs ) getHeaderSnapshot (cpHeader , ssHeader * types.Header ) (* Snapshot , error ) {
292+ cp := cpHeader .Number .Uint64 ()
293+ if ! d .config .IsCheckpoint (cp ) {
294+ return nil , errNotCheckpoint
295+ }
296+ ss := ssHeader .Number .Uint64 ()
297+ if d .config .Snapshot (cp ) != ss {
298+ return nil , errNotSnapshot
299+ }
300+ if len (cpHeader .Extra ) <= extraVanity + extraSeal {
301+ return nil , errMissingCheckpointSigners
302+ }
303+ extraSuffix := len (cpHeader .Extra ) - extraSeal
304+ extraSealers := cpHeader .Extra [extraVanity :extraSuffix ]
305+ if len (extraSealers ) == 0 {
306+ log .Error ("empty sealers list" , "number" , cp , "extra" , common .Bytes2Hex (cpHeader .Extra ))
307+ return nil , errMissingCheckpointSigners
308+ }
309+ if len (extraSealers )% common .AddressLength != 0 {
310+ log .Error ("not divided by common.AddressLength" , "number" , cp , "actual" , common .Bytes2Hex (extraSealers ))
311+ return nil , errInvalidCheckpointSigners
312+ }
313+ signersCount := len (extraSealers ) / common .AddressLength
314+ signers := make ([]common.Address , signersCount )
315+ for i := 0 ; i < signersCount ; i ++ {
316+ offset := i * common .AddressLength
317+ signers [i ] = common .BytesToAddress (extraSealers [offset : offset + common .AddressLength ])
318+ }
319+
320+ log .Trace ("Snapshot parsed from checkpoint header" , "snapshot number" , ss , "hash" , ssHeader .Hash (), "for number" , cpHeader .Number )
321+ return newSnapshot (d .config , d .signatures , ss , ssHeader .Hash (), signers ), nil
322+ }
323+
231324// verifySeal1 checks whether the signature contained in the header satisfies the
232325// consensus protocol requirements. The method accepts an optional list of parent
233326// headers that aren't yet part of the local blockchain to generate the snapshots
@@ -239,7 +332,7 @@ func (d *Dccs) verifySeal1(chain consensus.ChainReader, header *types.Header, pa
239332 return errUnknownBlock
240333 }
241334 // Retrieve the snapshot needed to verify this header and cache it
242- snap , err := d .snapshot1 (chain , number - 1 , header . ParentHash , parents )
335+ snap , err := d .snapshot1 (chain , header , parents )
243336 if err != nil {
244337 return err
245338 }
@@ -285,28 +378,14 @@ func (d *Dccs) verifySeal1(chain consensus.ChainReader, header *types.Header, pa
285378// header for running the transactions on top.
286379func (d * Dccs ) prepare1 (chain consensus.ChainReader , header * types.Header ) error {
287380 header .Nonce = types.BlockNonce {}
288- // Get the beneficiary of signer from smart contract and set to header's coinbase to give sealing reward later
289- number := header .Number .Uint64 ()
290- cp := d .config .Snapshot (number )
291- checkpoint := chain .GetHeaderByNumber (cp )
292- if checkpoint != nil {
293- root , _ := chain .StateAt (checkpoint .Root )
294- index := common .BigToHash (common .Big1 ).String ()[2 :]
295- coinbase := "0x000000000000000000000000" + header .Coinbase .String ()[2 :]
296- key := crypto .Keccak256Hash (hexutil .MustDecode (coinbase + index ))
297- result := root .GetState (chain .Config ().Dccs .Contract , key )
298- beneficiary := common .HexToAddress (result .Hex ())
299- header .Coinbase = beneficiary
300- } else {
301- log .Error ("state is not available at the block number" , "number" , cp )
302- return errors .New ("state is not available at the block number" )
303- }
381+ d .prepareBeneficiary (header , chain )
304382
305383 // Set the correct difficulty
306- snap , err := d .snapshot1 (chain , number - 1 , header . ParentHash , nil )
384+ snap , err := d .snapshot1 (chain , header , nil )
307385 if err != nil {
308386 return err
309387 }
388+ number := header .Number .Uint64 ()
310389 parent := chain .GetHeader (header .ParentHash , number - 1 )
311390 if parent == nil {
312391 return consensus .ErrUnknownAncestor
@@ -338,6 +417,47 @@ func (d *Dccs) prepare1(chain consensus.ChainReader, header *types.Header) error
338417 return nil
339418}
340419
420+ // prepareBeneficiary gets the beneficiary of signer from smart contract and
421+ // set to header's coinbase to give sealing reward later.
422+ // if all else fails, the sealer address is kept as reward beneficiary
423+ func (d * Dccs ) prepareBeneficiary (header * types.Header , chain consensus.ChainReader ) {
424+ index := common .BigToHash (common .Big1 ).String ()[2 :]
425+ sealer := "0x000000000000000000000000" + header .Coinbase .String ()[2 :]
426+ key := crypto .Keccak256Hash (hexutil .MustDecode (sealer + index ))
427+
428+ number := header .Number .Uint64 ()
429+
430+ // try the current active state first
431+ state , err := chain .State ()
432+ if err != nil {
433+ log .Error ("Chain state not available" , "number" , number , "err" , err )
434+ } else if state != nil {
435+ hash := state .GetState (chain .Config ().Dccs .Contract , key )
436+ if (hash != common.Hash {}) {
437+ header .Coinbase = common .HexToAddress (hash .Hex ())
438+ return
439+ }
440+ }
441+
442+ // then try the snapshot state
443+ ss := d .config .Snapshot (number )
444+ ssHeader := chain .GetHeaderByNumber (ss )
445+ if ssHeader == nil {
446+ log .Warn ("Snapshot header not avaialbe" , "for number" , number , "snapshot number" , ss )
447+ return
448+ }
449+ state , err = chain .StateAt (ssHeader .Root )
450+ if err != nil || state == nil {
451+ log .Warn ("Snapshot state not available" , "for number" , number , "snapshot number" , ss , "err" , err )
452+ return
453+ }
454+
455+ hash := state .GetState (chain .Config ().Dccs .Contract , key )
456+ if (hash != common.Hash {}) {
457+ header .Coinbase = common .HexToAddress (hash .Hex ())
458+ }
459+ }
460+
341461// finalize1 implements consensus.Engine, ensuring no uncles are set, nor block
342462// rewards given, and returns the final block.
343463func (d * Dccs ) finalize1 (chain consensus.ChainReader , header * types.Header , state * state.StateDB , txs []* types.Transaction , uncles []* types.Header ) {
@@ -379,7 +499,7 @@ func (d *Dccs) seal1(chain consensus.ChainReader, block *types.Block, results ch
379499 d .lock .RUnlock ()
380500
381501 // Bail out if we're unauthorized to sign a block
382- snap , err := d .snapshot1 (chain , number - 1 , header . ParentHash , nil )
502+ snap , err := d .snapshot1 (chain , header , nil )
383503 if err != nil {
384504 return err
385505 }
0 commit comments