@@ -19,14 +19,12 @@ package dbft
1919
2020import (
2121 "bytes"
22- "context"
2322 "encoding/binary"
2423 "encoding/hex"
2524 "errors"
2625 "fmt"
2726 "io"
2827 "math/big"
29- "sort"
3028 "sync"
3129 "sync/atomic"
3230 "time"
@@ -35,15 +33,12 @@ import (
3533 "github.com/ethereum/go-ethereum/accounts/abi"
3634 "github.com/ethereum/go-ethereum/antimev"
3735 "github.com/ethereum/go-ethereum/common"
38- "github.com/ethereum/go-ethereum/common/hexutil"
39- "github.com/ethereum/go-ethereum/common/lru"
4036 "github.com/ethereum/go-ethereum/consensus"
4137 "github.com/ethereum/go-ethereum/consensus/dbft/dbftutil"
4238 "github.com/ethereum/go-ethereum/consensus/misc"
4339 "github.com/ethereum/go-ethereum/consensus/misc/eip1559"
4440 "github.com/ethereum/go-ethereum/core"
4541 "github.com/ethereum/go-ethereum/core/state"
46- "github.com/ethereum/go-ethereum/core/systemcontracts"
4742 "github.com/ethereum/go-ethereum/core/txpool"
4843 "github.com/ethereum/go-ethereum/core/txpool/legacypool"
4944 "github.com/ethereum/go-ethereum/core/types"
@@ -87,11 +82,6 @@ const (
8782 // msgsChCap is a capacity of channel that accepts consensus messages from
8883 // dBFT protocol.
8984 msgsChCap = 100
90- // validatorsCacheCap is a capacity of validators cache. It's enough to store
91- // validators for only three potentially subsequent heights, i.e. three latest
92- // blocks to effectivaly verify dBFT payloads travelling through the network and
93- // properly initialize dBFT at the latest height.
94- validatorsCacheCap = 3
9585 // crossEpochDecryptionStartRound is the number of DKG round (as denoted in KeyManagement
9686 // system contract) starting from which continuous cross-epoch Envelopes decryption is supported.
9787 // First DKG round setups sharing key, second DKG round setups resharing key, hence resharing
@@ -131,6 +121,9 @@ var (
131121
132122 // errUnauthorizedSigner is returned if a header is signed by a non-authorized entity.
133123 errUnauthorizedSigner = errors .New ("unauthorized signer" )
124+
125+ // errNotInitializedBackend is returned if the eth API backend is not initialized
126+ errNotInitializedBackend = errors .New ("eth API backend is not initialized, DKG can't function properly" )
134127)
135128
136129// DBFT is the proof-of-authority consensus engine.
@@ -218,11 +211,8 @@ type DBFT struct {
218211 finished chan struct {}
219212
220213 // various native contract APIs that dBFT uses.
221- backend * ethapi.Backend
222- txAPI * ethapi.TransactionAPI
223- validatorsCache * lru.Cache [uint64 , []common.Address ]
224- // dkgIndexCache is a cache for storing the index array of the ordered validators
225- dkgIndexCache * lru.Cache [uint64 , []int ]
214+ backend * ethapi.Backend
215+ txAPI * ethapi.TransactionAPI
226216 // staticPool is a legacy pool instance for decrypted transaction verification,
227217 // which is initialized once per height at postBlock callback. It should be reset
228218 // before any reusage at the same height.
@@ -313,9 +303,6 @@ func New(chainCfg *params.ChainConfig, _ ethdb.Database, statisticsCfg Statistic
313303 quit : make (chan struct {}),
314304 finished : make (chan struct {}),
315305
316- validatorsCache : lru.NewCache [uint64 , []common.Address ](validatorsCacheCap ),
317- dkgIndexCache : lru.NewCache [uint64 , []int ](validatorsCacheCap ),
318-
319306 dkgSnapshot : NewSnapshot (),
320307 executeProofTaskChan : make (chan * taskList , 2 ), // The maximum number of task lists per epoch is 3
321308 loopWatchTaskChan : make (chan struct {}),
@@ -439,12 +426,16 @@ func (c *DBFT) getValidatorsCb(txs ...dbft.Transaction[common.Hash]) []dbft.Publ
439426 err error
440427 )
441428 if txs == nil {
442- // getValidatorsSorted with empty args is used by dbft to fill the list of
429+ // GetValidatorsSorted with empty args is used by dbft to fill the list of
443430 // block's validators, thus should return validators from the current
444431 // epoch without recalculation.
445- pKeys , err = c .getValidatorsSorted (& c .lastIndex , nil , nil )
432+ if c .backend != nil {
433+ pKeys , err = (* c .backend ).GetValidatorsSorted (& c .lastIndex , nil , nil )
434+ } else {
435+ err = errNotInitializedBackend
436+ }
446437 }
447- // getValidatorsSorted with non-empty args is used by dbft to fill block's
438+ // GetValidatorsSorted with non-empty args is used by dbft to fill block's
448439 // NextConsensus field, but DBFT doesn't provide WithGetConsensusAddress
449440 // callback and fills NextConsensus by itself via WithNewBlockFromContext
450441 // callback. Thus, leave pKeys empty if txes != nil.
@@ -1062,10 +1053,16 @@ func (c *DBFT) processPreBlockCb(b dbft.PreBlock[common.Hash]) error {
10621053 sharesCurr = make (map [int ][]* tpke.DecryptionShare , ctx .M ())
10631054 sharesPrev = make (map [int ][]* tpke.DecryptionShare )
10641055 blockNum = pre .header .Number .Uint64 () - 1
1056+ dkgIndex int
1057+ err error
10651058 )
10661059 for _ , preC := range ctx .PreCommitPayloads {
10671060 if preC != nil && preC .ViewNumber () == ctx .ViewNumber {
1068- dkgIndex , err := c .getDKGIndex (int (preC .ValidatorIndex ()), blockNum )
1061+ if c .backend != nil {
1062+ dkgIndex , err = (* c .backend ).GetDKGIndex (blockNum , int (preC .ValidatorIndex ()))
1063+ } else {
1064+ err = errNotInitializedBackend
1065+ }
10691066 if err != nil {
10701067 return fmt .Errorf ("get DKG index failed: ValidatorIndex %d, block height %d" , int (preC .ValidatorIndex ()), blockNum )
10711068 }
@@ -1408,8 +1405,13 @@ func (c *DBFT) getBlockWitness(pub *tpke.PublicKey, block *Block) ([]byte, error
14081405 for i := 0 ; i < len (vals ); i ++ {
14091406 if p := dctx .CommitPayloads [i ]; p != nil && p .ViewNumber () == dctx .ViewNumber {
14101407 var err error
1408+ var dkgIndex int
14111409 blockNum := block .header .Number .Uint64 () - 1
1412- dkgIndex , err := c .getDKGIndex (i , blockNum )
1410+ if c .backend != nil {
1411+ dkgIndex , err = (* c .backend ).GetDKGIndex (blockNum , i )
1412+ } else {
1413+ err = errNotInitializedBackend
1414+ }
14131415 if err != nil {
14141416 return nil , fmt .Errorf ("get DKG index failed: ValidatorIndex %d, block height %d" , i , blockNum )
14151417 }
@@ -1482,47 +1484,6 @@ func (c *DBFT) WithRequestTxs(f func(hashed []common.Hash)) {
14821484func (c * DBFT ) WithMux (mux * event.TypeMux ) {
14831485 c .mux = mux
14841486 c .blockQueue .SetMux (mux )
1485-
1486- go c .syncWatcher ()
1487- }
1488-
1489- // syncWatcher is a standalone loop aimed to be active irrespectively of dBFT engine
1490- // activity. It tracks the first chain sync attempt till its end.
1491- func (c * DBFT ) syncWatcher () {
1492- downloaderEvents := c .mux .Subscribe (downloader.StartEvent {}, downloader.DoneEvent {}, downloader.FailedEvent {})
1493- defer func () {
1494- if ! downloaderEvents .Closed () {
1495- downloaderEvents .Unsubscribe ()
1496- }
1497- }()
1498- dlEventCh := downloaderEvents .Chan ()
1499-
1500- events:
1501- for {
1502- select {
1503- case <- c .quit :
1504- break events
1505- case ev := <- dlEventCh :
1506- if ev == nil {
1507- // Unsubscription done, stop listening.
1508- dlEventCh = nil
1509- break events
1510- }
1511- switch ev .Data .(type ) {
1512- case downloader.StartEvent :
1513- c .syncing .Store (true )
1514-
1515- case downloader.FailedEvent :
1516- c .syncing .Store (false )
1517-
1518- case downloader.DoneEvent :
1519- c .syncing .Store (false )
1520-
1521- // Stop reacting to downloader events.
1522- downloaderEvents .Unsubscribe ()
1523- }
1524- }
1525- }
15261487}
15271488
15281489// WithTxPool initializes transaction pool API for DBFT interactions with memory pool
@@ -2212,7 +2173,7 @@ func (c *DBFT) eventLoop() {
22122173 // been broadcasted the events are unregistered and the loop is exited. This to
22132174 // prevent a major security vuln where external parties can DOS you with blocks
22142175 // and halt your dBFT operation for as long as the DOS continues.
2215- downloaderEvents := c .mux .Subscribe (downloader.DoneEvent {}, downloader.FailedEvent {})
2176+ downloaderEvents := c .mux .Subscribe (downloader.StartEvent {}, downloader. DoneEvent {}, downloader.FailedEvent {})
22162177 defer func () {
22172178 if ! downloaderEvents .Closed () {
22182179 downloaderEvents .Unsubscribe ()
@@ -2286,7 +2247,11 @@ events:
22862247 continue
22872248 }
22882249 switch ev .Data .(type ) {
2250+ case downloader.StartEvent :
2251+ c .syncing .Store (true )
22892252 case downloader.FailedEvent :
2253+ c .syncing .Store (false )
2254+
22902255 latest := c .chain .CurrentHeader ()
22912256 err := c .handleChainBlock (latest , false )
22922257 if err != nil {
@@ -2297,6 +2262,8 @@ events:
22972262 }
22982263
22992264 case downloader.DoneEvent :
2265+ c .syncing .Store (false )
2266+
23002267 // Stop reacting to downloader events.
23012268 downloaderEvents .Unsubscribe ()
23022269
@@ -2435,7 +2402,10 @@ func payloadFromMessage(ep *dbftproto.Message, getBlockExtraVersion func(*big.In
24352402
24362403func (c * DBFT ) validatePayload (p * Payload ) error {
24372404 h := c .chain .CurrentBlock ().Number .Uint64 ()
2438- validators , err := c .getValidatorsSorted (& h , nil , nil )
2405+ if c .backend == nil {
2406+ return errNotInitializedBackend
2407+ }
2408+ validators , err := (* c .backend ).GetValidatorsSorted (& h , nil , nil )
24392409 if err != nil {
24402410 return fmt .Errorf ("failed to get next block validators: %w" , err )
24412411 }
@@ -2451,25 +2421,6 @@ func (c *DBFT) validatePayload(p *Payload) error {
24512421 return nil
24522422}
24532423
2454- // IsExtensibleAllowed determines if address is allowed to send extensible payloads
2455- // (only consensus payloads for now) at the specified height.
2456- func (c * DBFT ) IsExtensibleAllowed (h uint64 , u common.Address ) error {
2457- // Can't verify extensible sender if the node has an outdated state.
2458- if c .syncing .Load () {
2459- return dbftproto .ErrSyncing
2460- }
2461- // Only validators are included into extensible whitelist for now.
2462- validators , err := c .getValidatorsSorted (& h , nil , nil )
2463- if err != nil {
2464- return fmt .Errorf ("failed to get validators: %w" , err )
2465- }
2466- _ , found := slices .BinarySearchFunc (validators , u , common .Address .Cmp )
2467- if ! found {
2468- return fmt .Errorf ("address is not a validator" )
2469- }
2470- return nil
2471- }
2472-
24732424func (c * DBFT ) newConsensusPayloadCb (ctx * dbft.Context [common.Hash ], t dbft.MessageType , msg any ) dbft.ConsensusPayload [common.Hash ] {
24742425 var cp = new (Payload )
24752426 cp .BlockIndex = uint64 (ctx .BlockIndex )
@@ -2537,7 +2488,10 @@ func (c *DBFT) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, pa
25372488
25382489func (c * DBFT ) calcDifficulty (signer common.Address , parent * types.Header ) * big.Int {
25392490 h := parent .Number .Uint64 ()
2540- vals , err := c .getValidatorsSorted (& h , nil , nil )
2491+ if c .backend == nil {
2492+ return nil
2493+ }
2494+ vals , err := (* c .backend ).GetValidatorsSorted (& h , nil , nil )
25412495 if err != nil {
25422496 return nil
25432497 }
@@ -2722,111 +2676,6 @@ func (c *DBFT) APIs(chain consensus.ChainHeaderReader) []rpc.API {
27222676 }}
27232677}
27242678
2725- // getValidatorsSorted returns validators chosen in the result of the latest
2726- // finalized voting epoch. It calls Governance contract under the hood. The call
2727- // is based on the provided state or (if not provided) on the state of the block
2728- // with the specified height. Validators returned from this method are always
2729- // sorted by bytes order (even if the list returned from governance contract is
2730- // sorted in another way). This method uses cached values in case of validators
2731- // requested by block height.
2732- func (c * DBFT ) getValidatorsSorted (blockNum * uint64 , state * state.StateDB , header * types.Header ) ([]common.Address , error ) {
2733- res , err := c .getValidators (blockNum , state , header )
2734- if err != nil {
2735- return nil , err
2736- }
2737-
2738- sortedList := slices .Clone (res )
2739- slices .SortFunc (sortedList , common .Address .Cmp )
2740- return sortedList , err
2741- }
2742-
2743- // getValidators returns validators chosen in the result of the latest finalized
2744- // voting epoch. It calls Governance contract under the hood. The call is based
2745- // on the provided state or (if not provided) on the state of the block with the
2746- // specified height. Validators returned from this method are sorted in the original
2747- // order used by Governance contract. This method uses cached values in case of
2748- // validators requested by block height.
2749- func (c * DBFT ) getValidators (blockNum * uint64 , state * state.StateDB , header * types.Header ) ([]common.Address , error ) {
2750- if c .backend == nil {
2751- return nil , errors .New ("eth API backend is not initialized, dBFT can't function properly" )
2752- }
2753-
2754- if state == nil && blockNum != nil {
2755- vals , ok := c .validatorsCache .Get (* blockNum )
2756- if ok {
2757- return vals , nil
2758- }
2759- }
2760-
2761- // Perform smart contract call.
2762- method := "getCurrentConsensus" // latest finalized epoch validators.
2763- data , err := systemcontracts .GovernanceABI .Pack (method )
2764- if err != nil {
2765- return nil , fmt .Errorf ("failed to pack '%s': %w" , method , err )
2766- }
2767- msgData := hexutil .Bytes (data )
2768- gas := hexutil .Uint64 (50_000_000 ) // more than enough for validators call processing.
2769- args := ethapi.TransactionArgs {
2770- Gas : & gas ,
2771- To : & systemcontracts .GovernanceProxyHash ,
2772- Data : & msgData ,
2773- }
2774-
2775- ctx , cancel := context .WithCancel (context .Background ())
2776- // Cancel when we are finished consuming integers.
2777- defer cancel ()
2778- var result * core.ExecutionResult
2779- if state != nil {
2780- result , err = ethapi .DoCallAtState (ctx , * c .backend , args , state , header , nil , nil , 0 , 0 )
2781- } else if blockNum != nil {
2782- blockNr := rpc .BlockNumberOrHashWithNumber (rpc .BlockNumber (* blockNum ))
2783- result , err = ethapi .DoCall (ctx , * c .backend , args , blockNr , nil , nil , 0 , 0 )
2784- } else {
2785- return nil , fmt .Errorf ("failed to compute validators: both block number and state are nil" )
2786- }
2787- if err != nil {
2788- return nil , fmt .Errorf ("failed to perform '%s' call: %w" , method , err )
2789- }
2790- var res []common.Address
2791- err = unpackContractExecutionResult (& res , result , systemcontracts .GovernanceABI , method )
2792- if err != nil {
2793- return nil , err
2794- }
2795-
2796- // Update cache in case if existing state was used for validators retrieval.
2797- if state == nil && blockNum != nil {
2798- _ = c .validatorsCache .Add (* blockNum , res )
2799- }
2800-
2801- return res , err
2802- }
2803-
2804- // getDKGIndex returns validator dkg index (original validator index +1) by validatorIndex (ordered validator index).
2805- func (c * DBFT ) getDKGIndex (validatorIndex int , blockNum uint64 ) (int , error ) {
2806- indices , ok := c .dkgIndexCache .Get (blockNum )
2807- if ! ok {
2808- originValidators , err := c .getValidators (& blockNum , nil , nil )
2809- if err != nil {
2810- return - 1 , err
2811- }
2812-
2813- indices = make ([]int , len (originValidators ))
2814- for i := range indices {
2815- indices [i ] = i
2816- }
2817- sort .Slice (indices , func (i , j int ) bool {
2818- return originValidators [indices [i ]].Cmp (originValidators [indices [j ]]) < 0
2819- })
2820- _ = c .dkgIndexCache .Add (blockNum , indices )
2821- }
2822-
2823- if validatorIndex < 0 || validatorIndex >= len (indices ) {
2824- return - 1 , fmt .Errorf ("invalid validator index: validators count is %d, requested %d" , len (indices ), validatorIndex )
2825- }
2826- dkgIndex := indices [validatorIndex ] + 1
2827- return dkgIndex , nil
2828- }
2829-
28302679// getGlobalPublicKey returns TPKE global public key. If state is provided, then this state
28312680// is used to recalculate local key based on the KeyManagement contract state. If state is
28322681// not provided, then the node's local keystore is used to retrieve global public key.
@@ -2860,7 +2709,10 @@ func (c *DBFT) getGlobalPublicKey(h *types.Header, s *state.StateDB) (*tpke.Publ
28602709func (c * DBFT ) getNextConsensus (h * types.Header , s * state.StateDB ) (common.Hash , common.Hash ) {
28612710 var multisig , threshold common.Hash
28622711
2863- nextVals , err := c .getValidatorsSorted (nil , s .Copy (), h )
2712+ if c .backend == nil {
2713+ log .Crit ("Can't calculate next consensus" , "err" , errNotInitializedBackend )
2714+ }
2715+ nextVals , err := (* c .backend ).GetValidatorsSorted (nil , s .Copy (), h )
28642716 if err != nil {
28652717 log .Crit ("Failed to compute next block validators" ,
28662718 "err" , err )
0 commit comments