package core
import (
"encoding/hex"
"fmt"
"math/big"
"sync"
"time"
"github.com/hashicorp/golang-lru/simplelru"
coreCommon "github.com/tangerine-network/tangerine-consensus/common"
dexCore "github.com/tangerine-network/tangerine-consensus/core"
coreCrypto "github.com/tangerine-network/tangerine-consensus/core/crypto"
coreEcdsa "github.com/tangerine-network/tangerine-consensus/core/crypto/ecdsa"
coreTypes "github.com/tangerine-network/tangerine-consensus/core/types"
dkgTypes "github.com/tangerine-network/tangerine-consensus/core/types/dkg"
coreUtils "github.com/tangerine-network/tangerine-consensus/core/utils"
"github.com/tangerine-network/go-tangerine/common"
"github.com/tangerine-network/go-tangerine/core/state"
"github.com/tangerine-network/go-tangerine/core/vm"
"github.com/tangerine-network/go-tangerine/log"
)
const dkgCacheSize = 5
type GovernanceStateDB interface {
State() (*state.StateDB, error)
StateAt(height uint64) (*state.StateDB, error)
}
func NewGovernanceStateDB(bc *BlockChain) GovernanceStateDB {
return &governanceStateDB{bc: bc}
}
type governanceStateDB struct {
bc *BlockChain
}
func (g *governanceStateDB) State() (*state.StateDB, error) {
return g.bc.State()
}
func (g *governanceStateDB) StateAt(height uint64) (*state.StateDB, error) {
header := g.bc.GetHeaderByNumber(height)
if header == nil {
return nil, fmt.Errorf("header at %d not exists", height)
}
return g.bc.StateAt(header.Root)
}
type dkgCacheItem struct {
Round uint64
Reset uint64
MasterPublicKeysLen uint64
MasterPublicKeys []*dkgTypes.MasterPublicKey
ComplaintsLen uint64
Complaints []*dkgTypes.Complaint
}
type Governance struct {
db GovernanceStateDB
nodeSetCache *dexCore.NodeSetCache
dkgCache *simplelru.LRU
dkgCacheMu sync.RWMutex
util vm.GovUtil
}
func NewGovernance(db GovernanceStateDB) *Governance {
cache, err := simplelru.NewLRU(dkgCacheSize, nil)
if err != nil {
log.Error("Failed to initialize DKG cache", "error", err)
return nil
}
g := &Governance{
db: db,
dkgCache: cache,
}
g.nodeSetCache = dexCore.NewNodeSetCache(g)
g.util = vm.GovUtil{g}
return g
}
func (g *Governance) GetHeadGovState() (*vm.GovernanceState, error) {
headState, err := g.db.State()
if err != nil {
log.Error("Governance head state not ready", "err", err)
return nil, err
}
return &vm.GovernanceState{StateDB: headState}, nil
}
func (g *Governance) StateAt(height uint64) (*state.StateDB, error) {
return g.db.StateAt(height)
}
func (g *Governance) GetConfigState(round uint64) (*vm.GovernanceState, error) {
return g.util.GetConfigState(round)
}
func (g *Governance) GetStateForDKGAtRound(round uint64) (*vm.GovernanceState, error) {
gs, err := g.GetHeadGovState()
if err != nil {
return nil, err
}
dkgRound := gs.DKGRound().Uint64()
if round > dkgRound {
return nil, fmt.Errorf("invalid round input: %d, dkgRound: %d", round, dkgRound)
}
if round == dkgRound {
return gs, nil
}
return g.util.GetStateAtRound(round)
}
func (g *Governance) CRSRound() uint64 {
gs, err := g.GetHeadGovState()
if err != nil {
log.Error("Failed to get head governance state", "err", err)
return 0
}
return gs.CRSRound().Uint64()
}
// CRS returns the CRS for a given round.
func (g *Governance) CRS(round uint64) coreCommon.Hash {
return coreCommon.Hash(g.util.CRS(round))
}
func (g *Governance) GetRoundHeight(round uint64) uint64 {
return g.util.GetRoundHeight(round)
}
func (g *Governance) Configuration(round uint64) *coreTypes.Config {
s, err := g.util.GetConfigState(round)
if err != nil {
panic(err)
}
c := s.Configuration()
return &coreTypes.Config{
LambdaBA: time.Duration(c.LambdaBA) * time.Millisecond,
LambdaDKG: time.Duration(c.LambdaDKG) * time.Millisecond,
NotarySetSize: uint32(s.NotarySetSize().Uint64()),
RoundLength: c.RoundLength,
MinBlockInterval: time.Duration(c.MinBlockInterval) * time.Millisecond,
}
}
// NodeSet returns the current node set.
func (g *Governance) NodeSet(round uint64) []coreCrypto.PublicKey {
configState, err := g.util.GetConfigState(round)
if err != nil {
panic(err)
}
var pks []coreCrypto.PublicKey
for _, n := range configState.QualifiedNodes() {
pk, err := coreEcdsa.NewPublicKeyFromByteSlice(n.PublicKey)
if err != nil {
panic(err)
}
pks = append(pks, pk)
}
return pks
}
func (g *Governance) PurgeNotarySet(round uint64) {
g.nodeSetCache.Purge(round)
}
func (g *Governance) NotarySet(round uint64) (map[string]struct{}, error) {
notarySet, err := g.nodeSetCache.GetNotarySet(round)
if err != nil {
return nil, err
}
r := make(map[string]struct{}, len(notarySet))
for id := range notarySet {
if key, exists := g.nodeSetCache.GetPublicKey(id); exists {
r[hex.EncodeToString(key.Bytes())] = struct{}{}
}
}
return r, nil
}
func (g *Governance) DKGSetNodeKeyAddresses(round uint64) (map[common.Address]struct{}, error) {
config := g.Configuration(round)
mpks := g.DKGMasterPublicKeys(round)
complaints := g.DKGComplaints(round)
threshold := coreUtils.GetDKGThreshold(&coreTypes.Config{
NotarySetSize: config.NotarySetSize})
_, ids, err := dkgTypes.CalcQualifyNodes(mpks, complaints, threshold)
if err != nil {
return nil, err
}
r := make(map[common.Address]struct{})
for id := range ids {
r[vm.IdToAddress(id)] = struct{}{}
}
return r, nil
}
func (g *Governance) getOrUpdateDKGCache(round uint64) *dkgCacheItem {
s, err := g.GetStateForDKGAtRound(round)
if err != nil {
log.Error("Failed to get DKG state", "round", round, "err", err)
return nil
}
reset := s.DKGResetCount(new(big.Int).SetUint64(round))
mpksLen := s.LenDKGMasterPublicKeys().Uint64()
compsLen := s.LenDKGComplaints().Uint64()
var cache *dkgCacheItem
g.dkgCacheMu.RLock()
if v, ok := g.dkgCache.Get(round); ok {
cache = v.(*dkgCacheItem)
}
g.dkgCacheMu.RUnlock()
if cache != nil && cache.Reset == reset.Uint64() &&
cache.MasterPublicKeysLen == mpksLen &&
cache.ComplaintsLen == compsLen {
return cache
}
g.dkgCacheMu.Lock()
defer g.dkgCacheMu.Unlock()
cache = &dkgCacheItem{
Round: round,
Reset: reset.Uint64(),
}
if cache == nil || cache.MasterPublicKeysLen != mpksLen {
cache.MasterPublicKeys = s.DKGMasterPublicKeyItems()
cache.MasterPublicKeysLen = uint64(len(cache.MasterPublicKeys))
}
if cache == nil || cache.ComplaintsLen != compsLen {
cache.Complaints = s.DKGComplaintItems()
cache.ComplaintsLen = uint64(len(cache.Complaints))
}
g.dkgCache.Add(round, cache)
return cache
}
func (g *Governance) DKGComplaints(round uint64) []*dkgTypes.Complaint {
cache := g.getOrUpdateDKGCache(round)
return cache.Complaints
}
func (g *Governance) DKGMasterPublicKeys(round uint64) []*dkgTypes.MasterPublicKey {
cache := g.getOrUpdateDKGCache(round)
return cache.MasterPublicKeys
}
func (g *Governance) IsDKGMPKReady(round uint64) bool {
s, err := g.GetStateForDKGAtRound(round)
if err != nil {
log.Error("Failed to get state for DKG", "round", round, "err", err)
return false
}
config := g.Configuration(round)
threshold := 2*uint64(config.NotarySetSize)/3 + 1
count := s.DKGMPKReadysCount().Uint64()
return count >= threshold
}
func (g *Governance) IsDKGFinal(round uint64) bool {
s, err := g.GetStateForDKGAtRound(round)
if err != nil {
log.Error("Failed to get state for DKG", "round", round, "err", err)
return false
}
config := g.Configuration(round)
threshold := 2*uint64(config.NotarySetSize)/3 + 1
count := s.DKGFinalizedsCount().Uint64()
return count >= threshold
}
func (g *Governance) IsDKGSuccess(round uint64) bool {
s, err := g.GetStateForDKGAtRound(round)
if err != nil {
log.Error("Failed to get state for DKG", "round", round, "err", err)
return false
}
return s.DKGSuccessesCount().Uint64() >=
uint64(coreUtils.GetDKGValidThreshold(g.Configuration(round)))
}
func (g *Governance) DKGResetCount(round uint64) uint64 {
gs, err := g.GetHeadGovState()
if err != nil {
log.Error("Failed to get head governance state", "err", err)
}
return gs.DKGResetCount(big.NewInt(int64(round))).Uint64()
}