From 22b3e0acd79f28d5fd1b908c85cca2c31267ba16 Mon Sep 17 00:00:00 2001 From: envestcc Date: Wed, 8 Feb 2023 22:21:21 +0800 Subject: [PATCH 1/7] implement nodinfo.isDelegate --- chainservice/builder.go | 18 ++++++++-- go.mod | 2 +- go.sum | 3 +- nodeinfo/config.go | 2 ++ nodeinfo/manager.go | 74 ++++++++++++++++++++++++---------------- nodeinfo/manager_test.go | 74 ++++++++++++++++++++++++++++------------ 6 files changed, 117 insertions(+), 56 deletions(-) diff --git a/chainservice/builder.go b/chainservice/builder.go index 86cbc66cf8..9685729154 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -34,6 +34,7 @@ import ( "github.com/iotexproject/iotex-core/nodeinfo" "github.com/iotexproject/iotex-core/p2p" "github.com/iotexproject/iotex-core/pkg/log" + "github.com/iotexproject/iotex-core/state" "github.com/iotexproject/iotex-core/state/factory" "github.com/iotexproject/iotex-election/committee" "github.com/pkg/errors" @@ -378,10 +379,19 @@ func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.B return blockchain.NewBlockchain(builder.cfg.Chain, builder.cfg.Genesis, builder.cs.blockdao, factory.NewMinter(builder.cs.factory, builder.cs.actpool), chainOpts...) } -func (builder *Builder) buildNodeInfoManager() { - dm := nodeinfo.NewInfoManager(&builder.cfg.NodeInfo, builder.cs.p2pAgent, builder.cs.chain, builder.cfg.Chain.ProducerPrivateKey()) +func (builder *Builder) buildNodeManager() error { + cs := builder.cs + protocol := staking.FindProtocol(builder.cs.Registry()) + if protocol == nil { + return errors.New("cannot find staking protocol") + } + + dm := nodeinfo.NewInfoManager(&builder.cfg.NodeInfo, builder.cs.p2pAgent, builder.cs.chain, builder.cfg.Chain.ProducerPrivateKey(), func(ctx context.Context) (state.CandidateList, error) { + return protocol.ActiveCandidates(ctx, cs.factory, 0) + }) builder.cs.nodeInfoManager = dm builder.cs.lifecycle.Add(dm) + return nil } func (builder *Builder) buildBlockSyncer() error { @@ -629,7 +639,9 @@ func (builder *Builder) build(forSubChain, forTest bool) (*ChainService, error) if err := builder.buildBlockSyncer(); err != nil { return nil, err } - builder.buildNodeInfoManager() + if err := builder.buildNodeManager(); err != nil { + return nil, err + } cs := builder.cs builder.cs = nil diff --git a/go.mod b/go.mod index 659a900b4f..a2f38e4ae0 100644 --- a/go.mod +++ b/go.mod @@ -59,6 +59,7 @@ require ( github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible github.com/shirou/gopsutil/v3 v3.22.2 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.34.0 + golang.org/x/exp v0.0.0-20230206171751-46f607a40771 golang.org/x/text v0.5.0 ) @@ -195,7 +196,6 @@ require ( golang.org/x/sys v0.3.0 // indirect golang.org/x/term v0.3.0 // indirect golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba // indirect - golang.org/x/tools v0.2.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/square/go-jose.v2 v2.5.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 755e909c63..84e9215d2e 100644 --- a/go.sum +++ b/go.sum @@ -1375,6 +1375,8 @@ golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm0 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg= +golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= @@ -1607,7 +1609,6 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.2.0 h1:G6AHpWxTMGY1KyEYoAQ5WTtIekUUvDNjan3ugu60JvE= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= golang.org/x/xerrors v0.0.0-20190212162355-a5947ffaace3/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.0.0-20181121035319-3f7ecaa7e8ca/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= diff --git a/nodeinfo/config.go b/nodeinfo/config.go index d248079a38..ad3351fd77 100644 --- a/nodeinfo/config.go +++ b/nodeinfo/config.go @@ -12,6 +12,7 @@ type Config struct { EnableBroadcastNodeInfo bool `yaml:"enableBroadcastNodeInfo"` BroadcastNodeInfoInterval time.Duration `yaml:"broadcastNodeInfoInterval"` NodeMapSize int `yaml:"nodeMapSize"` + DelegateCacheTTL time.Duration `yaml:"delegateCacheTTL"` } // DefaultConfig is the default config @@ -19,4 +20,5 @@ var DefaultConfig = Config{ EnableBroadcastNodeInfo: false, BroadcastNodeInfoInterval: 5 * time.Minute, NodeMapSize: 1000, + DelegateCacheTTL: 30 * time.Minute, } diff --git a/nodeinfo/manager.go b/nodeinfo/manager.go index 61cec9b932..46fc132839 100644 --- a/nodeinfo/manager.go +++ b/nodeinfo/manager.go @@ -7,6 +7,7 @@ package nodeinfo import ( "context" + "sync/atomic" "time" "github.com/iotexproject/go-pkgs/cache/lru" @@ -17,13 +18,16 @@ import ( "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" "go.uber.org/zap" + "golang.org/x/exp/slices" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/iotexproject/iotex-core/pkg/lifecycle" "github.com/iotexproject/iotex-core/pkg/log" "github.com/iotexproject/iotex-core/pkg/routine" "github.com/iotexproject/iotex-core/pkg/util/byteutil" "github.com/iotexproject/iotex-core/pkg/version" + "github.com/iotexproject/iotex-core/state" ) type ( @@ -37,6 +41,8 @@ type ( TipHeight() uint64 } + delegatesGetFunc func(context.Context) (state.CandidateList, error) + // Info node infomation Info struct { Version string @@ -48,13 +54,15 @@ type ( // InfoManager manage delegate node info InfoManager struct { - version string - address string - nodeMap *lru.Cache - broadcastTask *routine.RecurringTask - transmitter transmitter - chain chain - privKey crypto.PrivateKey + lifecycle.Lifecycle + version string + address string + isDelegate atomic.Bool + nodeMap *lru.Cache + transmitter transmitter + chain chain + privKey crypto.PrivateKey + getDelegates delegatesGetFunc } ) @@ -71,19 +79,20 @@ func init() { } // NewInfoManager new info manager -func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateKey) *InfoManager { +func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateKey, fun delegatesGetFunc) *InfoManager { dm := &InfoManager{ - nodeMap: lru.New(cfg.NodeMapSize), - transmitter: t, - chain: h, - privKey: privKey, - version: version.PackageVersion, - address: privKey.PublicKey().Address().String(), - } - - dm.broadcastTask = routine.NewRecurringTask(func() { + nodeMap: lru.New(cfg.NodeMapSize), + transmitter: t, + chain: h, + privKey: privKey, + version: version.PackageVersion, + address: privKey.PublicKey().Address().String(), + getDelegates: fun, + } + // init recurring tasks + broadcastTask := routine.NewRecurringTask(func() { // delegates or nodes who are turned on will broadcast - if cfg.EnableBroadcastNodeInfo || dm.isDelegate() { + if cfg.EnableBroadcastNodeInfo || dm.isDelegate.Load() { if err := dm.BroadcastNodeInfo(context.Background()); err != nil { log.L().Error("nodeinfo manager broadcast node info failed", zap.Error(err)) } @@ -91,23 +100,23 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK log.L().Debug("nodeinfo manager general node disabled node info broadcast") } }, cfg.BroadcastNodeInfoInterval) + updateDelegateCacheTask := routine.NewRecurringTask(func() { + if err := dm.updateDelegateCache(); err != nil { + log.L().Error("nodeinfo manager update delegate cache failed", zap.Error(err)) + } + }, cfg.DelegateCacheTTL) + dm.AddModels(updateDelegateCacheTask, broadcastTask) return dm } // Start start delegate broadcast task func (dm *InfoManager) Start(ctx context.Context) error { - if dm.broadcastTask != nil { - return dm.broadcastTask.Start(ctx) - } - return nil + return dm.OnStart(ctx) } // Stop stop delegate broadcast task func (dm *InfoManager) Stop(ctx context.Context) error { - if dm.broadcastTask != nil { - return dm.broadcastTask.Stop(ctx) - } - return nil + return dm.OnStop(ctx) } // HandleNodeInfo handle node info message @@ -214,9 +223,16 @@ func (dm *InfoManager) genNodeInfoMsg() (*iotextypes.NodeInfo, error) { return req, nil } -func (dm *InfoManager) isDelegate() bool { - // TODO whether i am delegate - return false +func (dm *InfoManager) updateDelegateCache() error { + candList, err := dm.getDelegates(context.Background()) + if err != nil { + return err + } + log.L().Debug("nodeinfo manager active candidates", zap.Any("candidates", candList)) + dm.isDelegate.Store(slices.ContainsFunc(candList, func(e *state.Candidate) bool { + return dm.address == e.Address + })) + return nil } func hashNodeInfo(msg *iotextypes.NodeInfoCore) hash.Hash256 { diff --git a/nodeinfo/manager_test.go b/nodeinfo/manager_test.go index 103b15a79e..6ed324a4f3 100644 --- a/nodeinfo/manager_test.go +++ b/nodeinfo/manager_test.go @@ -12,6 +12,7 @@ import ( "github.com/golang/mock/gomock" "github.com/iotexproject/go-pkgs/crypto" + "github.com/iotexproject/iotex-core/state" "github.com/iotexproject/iotex-core/test/mock/mock_nodeinfo" "github.com/iotexproject/iotex-proto/golang/iotextypes" "github.com/libp2p/go-libp2p-core/peer" @@ -21,23 +22,26 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) +func getEmptyCandidates(context.Context) (state.CandidateList, error) { + return state.CandidateList{}, nil +} + func TestNewDelegateManager(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() - hMock := mock_nodeinfo.NewMockchain(ctrl) - tMock := mock_nodeinfo.NewMocktransmitter(ctrl) privK, err := crypto.GenerateKey() require.NoError(err) t.Run("disable_broadcast", func(t *testing.T) { - cfg := Config{false, 100 * time.Millisecond, 1000} - dm := NewInfoManager(&cfg, tMock, hMock, privK) + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) + cfg := Config{false, 100 * time.Millisecond, 1000, time.Minute} + dm := NewInfoManager(&cfg, tMock, hMock, privK, getEmptyCandidates) require.NotNil(dm.nodeMap) require.Equal(tMock, dm.transmitter) require.Equal(hMock, dm.chain) require.Equal(privK, dm.privKey) - require.Equal(true, dm.broadcastTask != nil) tMock.EXPECT().BroadcastOutbound(gomock.Any(), gomock.Any()).Times(0) err := dm.Start(context.Background()) require.NoError(err) @@ -46,13 +50,37 @@ func TestNewDelegateManager(t *testing.T) { }) t.Run("enable_broadcast", func(t *testing.T) { - cfg := Config{true, 100 * time.Millisecond, 1000} - dm := NewInfoManager(&cfg, tMock, hMock, privK) + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) + cfg := Config{true, 100 * time.Millisecond, 1000, time.Minute} + dm := NewInfoManager(&cfg, tMock, hMock, privK, getEmptyCandidates) + require.NotNil(dm.nodeMap) + require.Equal(tMock, dm.transmitter) + require.Equal(hMock, dm.chain) + require.Equal(privK, dm.privKey) + tMock.EXPECT().Info().Return(peer.AddrInfo{}, nil).MinTimes(1) + hMock.EXPECT().TipHeight().Return(uint64(10)).MinTimes(1) + tMock.EXPECT().BroadcastOutbound(gomock.Any(), gomock.Any()).Return(nil).MinTimes(1) + err := dm.Start(context.Background()) + require.NoError(err) + defer dm.Stop(context.Background()) + time.Sleep(time.Second) + }) + t.Run("delegate_broadcast", func(t *testing.T) { + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) + cfg := Config{false, 100 * time.Millisecond, 1000, 100 * time.Millisecond} + dm := NewInfoManager(&cfg, tMock, hMock, privK, func(ctx context.Context) (state.CandidateList, error) { + return state.CandidateList{ + &state.Candidate{ + Address: privK.PublicKey().Address().String(), + }, + }, nil + }) require.NotNil(dm.nodeMap) require.Equal(tMock, dm.transmitter) require.Equal(hMock, dm.chain) require.Equal(privK, dm.privKey) - require.Equal(true, dm.broadcastTask != nil) tMock.EXPECT().Info().Return(peer.AddrInfo{}, nil).MinTimes(1) hMock.EXPECT().TipHeight().Return(uint64(10)).MinTimes(1) tMock.EXPECT().BroadcastOutbound(gomock.Any(), gomock.Any()).Return(nil).MinTimes(1) @@ -66,14 +94,14 @@ func TestNewDelegateManager(t *testing.T) { func TestDelegateManager_HandleNodeInfo(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - hMock := mock_nodeinfo.NewMockchain(ctrl) - tMock := mock_nodeinfo.NewMocktransmitter(ctrl) require := require.New(t) privKey, err := crypto.GenerateKey() require.NoError(err) t.Run("verify_pass", func(t *testing.T) { + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) msg := &iotextypes.NodeInfo{ Info: &iotextypes.NodeInfoCore{ Version: "v1.8.0", @@ -84,7 +112,7 @@ func TestDelegateManager_HandleNodeInfo(t *testing.T) { } hash := hashNodeInfo(msg.Info) msg.Signature, _ = privKey.Sign(hash[:]) - dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey) + dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey, getEmptyCandidates) dm.HandleNodeInfo(context.Background(), "abc", msg) addr := msg.Info.Address nodeGot, ok := dm.nodeMap.Get(addr) @@ -100,6 +128,8 @@ func TestDelegateManager_HandleNodeInfo(t *testing.T) { }) t.Run("verify_fail", func(t *testing.T) { + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) privKey2, _ := crypto.GenerateKey() msg := &iotextypes.NodeInfo{ Info: &iotextypes.NodeInfoCore{ @@ -110,7 +140,7 @@ func TestDelegateManager_HandleNodeInfo(t *testing.T) { }, Signature: []byte("xxxx"), } - dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey) + dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey, getEmptyCandidates) dm.HandleNodeInfo(context.Background(), "abc", msg) addr := msg.Info.Address _, ok := dm.nodeMap.Get(addr) @@ -125,13 +155,13 @@ func TestDelegateManager_BroadcastNodeInfo(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() - hMock := mock_nodeinfo.NewMockchain(ctrl) - tMock := mock_nodeinfo.NewMocktransmitter(ctrl) privKey, err := crypto.GenerateKey() require.NoError(err) t.Run("update_self", func(t *testing.T) { - dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey) + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) + dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey, getEmptyCandidates) height := uint64(200) peerID, err := peer.IDFromString("12D3KooWF2fns5ZWKbPfx2U1wQDdxoTK2D6HC3ortbSAQYR4BQp4") require.NoError(err) @@ -155,13 +185,13 @@ func TestDelegateManager_HandleNodeInfoRequest(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() - hMock := mock_nodeinfo.NewMockchain(ctrl) - tMock := mock_nodeinfo.NewMocktransmitter(ctrl) privKey, err := crypto.GenerateKey() require.NoError(err) t.Run("unicast", func(t *testing.T) { - dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey) + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) + dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey, getEmptyCandidates) height := uint64(200) var sig []byte message := &iotextypes.NodeInfo{} @@ -185,13 +215,13 @@ func TestDelegateManager_RequestSingleNodeInfoAsync(t *testing.T) { require := require.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() - hMock := mock_nodeinfo.NewMockchain(ctrl) - tMock := mock_nodeinfo.NewMocktransmitter(ctrl) privKey, err := crypto.GenerateKey() require.NoError(err) t.Run("request_single", func(t *testing.T) { - dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey) + hMock := mock_nodeinfo.NewMockchain(ctrl) + tMock := mock_nodeinfo.NewMocktransmitter(ctrl) + dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey, getEmptyCandidates) var paramPeer peer.AddrInfo var paramMsg iotextypes.NodeInfoRequest peerID, err := peer.IDFromString("12D3KooWF2fns5ZWKbPfx2U1wQDdxoTK2D6HC3ortbSAQYR4BQp4") @@ -216,7 +246,7 @@ func TestDelegateManager_GetNodeByAddr(t *testing.T) { privKey, err := crypto.GenerateKey() require.NoError(err) - dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey) + dm := NewInfoManager(&DefaultConfig, tMock, hMock, privKey, getEmptyCandidates) dm.updateNode(&Info{Address: "1"}) dm.updateNode(&Info{Address: "2"}) From 2bf6a62e51d1b7852a5b85a95379a1462d69d235 Mon Sep 17 00:00:00 2001 From: envestcc Date: Wed, 8 Feb 2023 23:41:26 +0800 Subject: [PATCH 2/7] add nodeinfo feature enable switch --- action/protocol/context.go | 2 ++ e2etest/nodeinfo_test.go | 1 + nodeinfo/manager.go | 12 ++++++++++++ nodeinfo/manager_test.go | 17 +++++++++++++++++ test/mock/mock_nodeinfo/mock_manager.go | 15 +++++++++++++++ 5 files changed, 47 insertions(+) diff --git a/action/protocol/context.go b/action/protocol/context.go index 725874c3ce..18b33e902f 100644 --- a/action/protocol/context.go +++ b/action/protocol/context.go @@ -110,6 +110,7 @@ type ( CorrectGasRefund bool FixRewardErroCheckPosition bool EnableWeb3Rewarding bool + EnableNodeInfo bool } // FeatureWithHeightCtx provides feature check functions. @@ -246,6 +247,7 @@ func WithFeatureCtx(ctx context.Context) context.Context { CorrectGasRefund: g.IsOkhotsk(height), FixRewardErroCheckPosition: g.IsOkhotsk(height), EnableWeb3Rewarding: g.IsToBeEnabled(height), + EnableNodeInfo: g.IsToBeEnabled(height), }, ) } diff --git a/e2etest/nodeinfo_test.go b/e2etest/nodeinfo_test.go index 0eb4b9d95b..7d14207545 100644 --- a/e2etest/nodeinfo_test.go +++ b/e2etest/nodeinfo_test.go @@ -22,6 +22,7 @@ func newConfigForNodeInfoTest(triePath, dBPath, idxDBPath string) (config.Config if err != nil { return cfg, nil, err } + cfg.Genesis.ToBeEnabledBlockHeight = 0 testTriePath, err := testutil.PathOfTempFile(triePath) if err != nil { return cfg, nil, err diff --git a/nodeinfo/manager.go b/nodeinfo/manager.go index 46fc132839..b563f7af3b 100644 --- a/nodeinfo/manager.go +++ b/nodeinfo/manager.go @@ -22,6 +22,8 @@ import ( "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" + "github.com/iotexproject/iotex-core/action/protocol" + "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/pkg/lifecycle" "github.com/iotexproject/iotex-core/pkg/log" "github.com/iotexproject/iotex-core/pkg/routine" @@ -39,6 +41,7 @@ type ( chain interface { TipHeight() uint64 + Genesis() genesis.Genesis } delegatesGetFunc func(context.Context) (state.CandidateList, error) @@ -91,6 +94,15 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK } // init recurring tasks broadcastTask := routine.NewRecurringTask(func() { + ctx := protocol.WithFeatureCtx( + protocol.WithBlockCtx( + genesis.WithGenesisContext(context.Background(), dm.chain.Genesis()), + protocol.BlockCtx{BlockHeight: dm.chain.TipHeight()}, + ), + ) + if !protocol.MustGetFeatureCtx(ctx).EnableNodeInfo { + return + } // delegates or nodes who are turned on will broadcast if cfg.EnableBroadcastNodeInfo || dm.isDelegate.Load() { if err := dm.BroadcastNodeInfo(context.Background()); err != nil { diff --git a/nodeinfo/manager_test.go b/nodeinfo/manager_test.go index 6ed324a4f3..5f53a3f6d6 100644 --- a/nodeinfo/manager_test.go +++ b/nodeinfo/manager_test.go @@ -12,6 +12,7 @@ import ( "github.com/golang/mock/gomock" "github.com/iotexproject/go-pkgs/crypto" + "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/state" "github.com/iotexproject/iotex-core/test/mock/mock_nodeinfo" "github.com/iotexproject/iotex-proto/golang/iotextypes" @@ -43,6 +44,12 @@ func TestNewDelegateManager(t *testing.T) { require.Equal(hMock, dm.chain) require.Equal(privK, dm.privKey) tMock.EXPECT().BroadcastOutbound(gomock.Any(), gomock.Any()).Times(0) + hMock.EXPECT().TipHeight().Return(uint64(2)).MinTimes(1) + hMock.EXPECT().Genesis().DoAndReturn(func() genesis.Genesis { + g := genesis.TestDefault() + g.ToBeEnabledBlockHeight = 1 + return g + }).MinTimes(1) err := dm.Start(context.Background()) require.NoError(err) defer dm.Stop(context.Background()) @@ -60,6 +67,11 @@ func TestNewDelegateManager(t *testing.T) { require.Equal(privK, dm.privKey) tMock.EXPECT().Info().Return(peer.AddrInfo{}, nil).MinTimes(1) hMock.EXPECT().TipHeight().Return(uint64(10)).MinTimes(1) + hMock.EXPECT().Genesis().DoAndReturn(func() genesis.Genesis { + g := genesis.TestDefault() + g.ToBeEnabledBlockHeight = 1 + return g + }).MinTimes(1) tMock.EXPECT().BroadcastOutbound(gomock.Any(), gomock.Any()).Return(nil).MinTimes(1) err := dm.Start(context.Background()) require.NoError(err) @@ -83,6 +95,11 @@ func TestNewDelegateManager(t *testing.T) { require.Equal(privK, dm.privKey) tMock.EXPECT().Info().Return(peer.AddrInfo{}, nil).MinTimes(1) hMock.EXPECT().TipHeight().Return(uint64(10)).MinTimes(1) + hMock.EXPECT().Genesis().DoAndReturn(func() genesis.Genesis { + g := genesis.TestDefault() + g.ToBeEnabledBlockHeight = 1 + return g + }).MinTimes(1) tMock.EXPECT().BroadcastOutbound(gomock.Any(), gomock.Any()).Return(nil).MinTimes(1) err := dm.Start(context.Background()) require.NoError(err) diff --git a/test/mock/mock_nodeinfo/mock_manager.go b/test/mock/mock_nodeinfo/mock_manager.go index 71d26a5232..fc46dd8221 100644 --- a/test/mock/mock_nodeinfo/mock_manager.go +++ b/test/mock/mock_nodeinfo/mock_manager.go @@ -9,6 +9,7 @@ import ( reflect "reflect" gomock "github.com/golang/mock/gomock" + genesis "github.com/iotexproject/iotex-core/blockchain/genesis" peer "github.com/libp2p/go-libp2p-core/peer" proto "google.golang.org/protobuf/proto" ) @@ -102,6 +103,20 @@ func (m *Mockchain) EXPECT() *MockchainMockRecorder { return m.recorder } +// Genesis mocks base method. +func (m *Mockchain) Genesis() genesis.Genesis { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Genesis") + ret0, _ := ret[0].(genesis.Genesis) + return ret0 +} + +// Genesis indicates an expected call of Genesis. +func (mr *MockchainMockRecorder) Genesis() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Genesis", reflect.TypeOf((*Mockchain)(nil).Genesis)) +} + // TipHeight mocks base method. func (m *Mockchain) TipHeight() uint64 { m.ctrl.T.Helper() From 8d359b1cbf0757bd6b81cbeeb7be3959100f7c74 Mon Sep 17 00:00:00 2001 From: envestcc Date: Wed, 8 Feb 2023 23:57:53 +0800 Subject: [PATCH 3/7] fix atomic.Bool is not supported before go1.19 --- nodeinfo/manager.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nodeinfo/manager.go b/nodeinfo/manager.go index b563f7af3b..e439e6af8a 100644 --- a/nodeinfo/manager.go +++ b/nodeinfo/manager.go @@ -60,7 +60,7 @@ type ( lifecycle.Lifecycle version string address string - isDelegate atomic.Bool + isDelegate atomic.Value // bool nodeMap *lru.Cache transmitter transmitter chain chain @@ -92,6 +92,7 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK address: privKey.PublicKey().Address().String(), getDelegates: fun, } + dm.isDelegate.Store(false) // init recurring tasks broadcastTask := routine.NewRecurringTask(func() { ctx := protocol.WithFeatureCtx( @@ -104,7 +105,7 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK return } // delegates or nodes who are turned on will broadcast - if cfg.EnableBroadcastNodeInfo || dm.isDelegate.Load() { + if cfg.EnableBroadcastNodeInfo || dm.isDelegate.Load().(bool) { if err := dm.BroadcastNodeInfo(context.Background()); err != nil { log.L().Error("nodeinfo manager broadcast node info failed", zap.Error(err)) } From b4b27b37b1a46b945b2d83a3621a9440cd7bcb32 Mon Sep 17 00:00:00 2001 From: envestcc Date: Thu, 9 Feb 2023 21:48:41 +0800 Subject: [PATCH 4/7] fix comment --- chainservice/builder.go | 17 ++++++++--------- nodeinfo/manager.go | 1 + 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/chainservice/builder.go b/chainservice/builder.go index 9685729154..83bd6ce780 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -379,15 +379,14 @@ func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.B return blockchain.NewBlockchain(builder.cfg.Chain, builder.cfg.Genesis, builder.cs.blockdao, factory.NewMinter(builder.cs.factory, builder.cs.actpool), chainOpts...) } -func (builder *Builder) buildNodeManager() error { +func (builder *Builder) buildNodeInfoManager() error { cs := builder.cs - protocol := staking.FindProtocol(builder.cs.Registry()) - if protocol == nil { - return errors.New("cannot find staking protocol") - } - - dm := nodeinfo.NewInfoManager(&builder.cfg.NodeInfo, builder.cs.p2pAgent, builder.cs.chain, builder.cfg.Chain.ProducerPrivateKey(), func(ctx context.Context) (state.CandidateList, error) { - return protocol.ActiveCandidates(ctx, cs.factory, 0) + dm := nodeinfo.NewInfoManager(&builder.cfg.NodeInfo, cs.p2pAgent, cs.chain, builder.cfg.Chain.ProducerPrivateKey(), func(ctx context.Context) (state.CandidateList, error) { + stk := staking.FindProtocol(cs.Registry()) + if stk == nil { + return nil, errors.New("cannot find staking protocol") + } + return stk.ActiveCandidates(ctx, cs.factory, 0) }) builder.cs.nodeInfoManager = dm builder.cs.lifecycle.Add(dm) @@ -639,7 +638,7 @@ func (builder *Builder) build(forSubChain, forTest bool) (*ChainService, error) if err := builder.buildBlockSyncer(); err != nil { return nil, err } - if err := builder.buildNodeManager(); err != nil { + if err := builder.buildNodeInfoManager(); err != nil { return nil, err } cs := builder.cs diff --git a/nodeinfo/manager.go b/nodeinfo/manager.go index e439e6af8a..9b8d88f4fc 100644 --- a/nodeinfo/manager.go +++ b/nodeinfo/manager.go @@ -102,6 +102,7 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK ), ) if !protocol.MustGetFeatureCtx(ctx).EnableNodeInfo { + log.L().Debug("nodeinfo manager feature is disabled") return } // delegates or nodes who are turned on will broadcast From 9b485f642c75f708bf7b60078c4cafecbac86e45 Mon Sep 17 00:00:00 2001 From: envestcc Date: Fri, 10 Feb 2023 17:15:48 +0800 Subject: [PATCH 5/7] fix comment --- nodeinfo/config.go | 2 -- nodeinfo/manager.go | 12 ++++++------ nodeinfo/manager_test.go | 6 +++--- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/nodeinfo/config.go b/nodeinfo/config.go index ad3351fd77..d248079a38 100644 --- a/nodeinfo/config.go +++ b/nodeinfo/config.go @@ -12,7 +12,6 @@ type Config struct { EnableBroadcastNodeInfo bool `yaml:"enableBroadcastNodeInfo"` BroadcastNodeInfoInterval time.Duration `yaml:"broadcastNodeInfoInterval"` NodeMapSize int `yaml:"nodeMapSize"` - DelegateCacheTTL time.Duration `yaml:"delegateCacheTTL"` } // DefaultConfig is the default config @@ -20,5 +19,4 @@ var DefaultConfig = Config{ EnableBroadcastNodeInfo: false, BroadcastNodeInfoInterval: 5 * time.Minute, NodeMapSize: 1000, - DelegateCacheTTL: 30 * time.Minute, } diff --git a/nodeinfo/manager.go b/nodeinfo/manager.go index 9b8d88f4fc..20c02e5e27 100644 --- a/nodeinfo/manager.go +++ b/nodeinfo/manager.go @@ -105,6 +105,11 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK log.L().Debug("nodeinfo manager feature is disabled") return } + if !dm.isDelegate.Load().(bool) { + if err := dm.updateDelegateCache(); err != nil { + log.L().Error("nodeinfo manager update delegate cache failed", zap.Error(err)) + } + } // delegates or nodes who are turned on will broadcast if cfg.EnableBroadcastNodeInfo || dm.isDelegate.Load().(bool) { if err := dm.BroadcastNodeInfo(context.Background()); err != nil { @@ -114,12 +119,7 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK log.L().Debug("nodeinfo manager general node disabled node info broadcast") } }, cfg.BroadcastNodeInfoInterval) - updateDelegateCacheTask := routine.NewRecurringTask(func() { - if err := dm.updateDelegateCache(); err != nil { - log.L().Error("nodeinfo manager update delegate cache failed", zap.Error(err)) - } - }, cfg.DelegateCacheTTL) - dm.AddModels(updateDelegateCacheTask, broadcastTask) + dm.AddModels(broadcastTask) return dm } diff --git a/nodeinfo/manager_test.go b/nodeinfo/manager_test.go index 5f53a3f6d6..05cddeee85 100644 --- a/nodeinfo/manager_test.go +++ b/nodeinfo/manager_test.go @@ -37,7 +37,7 @@ func TestNewDelegateManager(t *testing.T) { t.Run("disable_broadcast", func(t *testing.T) { hMock := mock_nodeinfo.NewMockchain(ctrl) tMock := mock_nodeinfo.NewMocktransmitter(ctrl) - cfg := Config{false, 100 * time.Millisecond, 1000, time.Minute} + cfg := Config{false, 100 * time.Millisecond, 1000} dm := NewInfoManager(&cfg, tMock, hMock, privK, getEmptyCandidates) require.NotNil(dm.nodeMap) require.Equal(tMock, dm.transmitter) @@ -59,7 +59,7 @@ func TestNewDelegateManager(t *testing.T) { t.Run("enable_broadcast", func(t *testing.T) { hMock := mock_nodeinfo.NewMockchain(ctrl) tMock := mock_nodeinfo.NewMocktransmitter(ctrl) - cfg := Config{true, 100 * time.Millisecond, 1000, time.Minute} + cfg := Config{true, 100 * time.Millisecond, 1000} dm := NewInfoManager(&cfg, tMock, hMock, privK, getEmptyCandidates) require.NotNil(dm.nodeMap) require.Equal(tMock, dm.transmitter) @@ -81,7 +81,7 @@ func TestNewDelegateManager(t *testing.T) { t.Run("delegate_broadcast", func(t *testing.T) { hMock := mock_nodeinfo.NewMockchain(ctrl) tMock := mock_nodeinfo.NewMocktransmitter(ctrl) - cfg := Config{false, 100 * time.Millisecond, 1000, 100 * time.Millisecond} + cfg := Config{false, 100 * time.Millisecond, 1000} dm := NewInfoManager(&cfg, tMock, hMock, privK, func(ctx context.Context) (state.CandidateList, error) { return state.CandidateList{ &state.Candidate{ From 20ff085ba1883ebebbe0dffa49857173a1f0c149 Mon Sep 17 00:00:00 2001 From: envestcc Date: Sun, 12 Feb 2023 21:58:30 +0800 Subject: [PATCH 6/7] fix comment --- chainservice/builder.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/chainservice/builder.go b/chainservice/builder.go index 83bd6ce780..a5afbe1c3f 100644 --- a/chainservice/builder.go +++ b/chainservice/builder.go @@ -381,11 +381,11 @@ func (builder *Builder) createBlockchain(forSubChain, forTest bool) blockchain.B func (builder *Builder) buildNodeInfoManager() error { cs := builder.cs + stk := staking.FindProtocol(cs.Registry()) + if stk == nil { + return errors.New("cannot find staking protocol") + } dm := nodeinfo.NewInfoManager(&builder.cfg.NodeInfo, cs.p2pAgent, cs.chain, builder.cfg.Chain.ProducerPrivateKey(), func(ctx context.Context) (state.CandidateList, error) { - stk := staking.FindProtocol(cs.Registry()) - if stk == nil { - return nil, errors.New("cannot find staking protocol") - } return stk.ActiveCandidates(ctx, cs.factory, 0) }) builder.cs.nodeInfoManager = dm From edb947a9e47b4e9e20b3c24089dd9869eb26c531 Mon Sep 17 00:00:00 2001 From: envestcc Date: Tue, 14 Feb 2023 15:28:28 +0800 Subject: [PATCH 7/7] fix comments --- nodeinfo/manager.go | 47 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/nodeinfo/manager.go b/nodeinfo/manager.go index 20c02e5e27..5a96690822 100644 --- a/nodeinfo/manager.go +++ b/nodeinfo/manager.go @@ -7,7 +7,6 @@ package nodeinfo import ( "context" - "sync/atomic" "time" "github.com/iotexproject/go-pkgs/cache/lru" @@ -58,14 +57,14 @@ type ( // InfoManager manage delegate node info InfoManager struct { lifecycle.Lifecycle - version string - address string - isDelegate atomic.Value // bool - nodeMap *lru.Cache - transmitter transmitter - chain chain - privKey crypto.PrivateKey - getDelegates delegatesGetFunc + version string + address string + delegateCache bool + nodeMap *lru.Cache + transmitter transmitter + chain chain + privKey crypto.PrivateKey + getDelegates delegatesGetFunc } ) @@ -82,17 +81,16 @@ func init() { } // NewInfoManager new info manager -func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateKey, fun delegatesGetFunc) *InfoManager { +func NewInfoManager(cfg *Config, t transmitter, ch chain, privKey crypto.PrivateKey, getDelegatesHandler delegatesGetFunc) *InfoManager { dm := &InfoManager{ nodeMap: lru.New(cfg.NodeMapSize), transmitter: t, - chain: h, + chain: ch, privKey: privKey, version: version.PackageVersion, address: privKey.PublicKey().Address().String(), - getDelegates: fun, + getDelegates: getDelegatesHandler, } - dm.isDelegate.Store(false) // init recurring tasks broadcastTask := routine.NewRecurringTask(func() { ctx := protocol.WithFeatureCtx( @@ -105,13 +103,9 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK log.L().Debug("nodeinfo manager feature is disabled") return } - if !dm.isDelegate.Load().(bool) { - if err := dm.updateDelegateCache(); err != nil { - log.L().Error("nodeinfo manager update delegate cache failed", zap.Error(err)) - } - } + // delegates or nodes who are turned on will broadcast - if cfg.EnableBroadcastNodeInfo || dm.isDelegate.Load().(bool) { + if cfg.EnableBroadcastNodeInfo || dm.isDelegate() { if err := dm.BroadcastNodeInfo(context.Background()); err != nil { log.L().Error("nodeinfo manager broadcast node info failed", zap.Error(err)) } @@ -119,7 +113,7 @@ func NewInfoManager(cfg *Config, t transmitter, h chain, privKey crypto.PrivateK log.L().Debug("nodeinfo manager general node disabled node info broadcast") } }, cfg.BroadcastNodeInfoInterval) - dm.AddModels(broadcastTask) + dm.Add(broadcastTask) return dm } @@ -237,15 +231,24 @@ func (dm *InfoManager) genNodeInfoMsg() (*iotextypes.NodeInfo, error) { return req, nil } +func (dm *InfoManager) isDelegate() bool { + if !dm.delegateCache { + if err := dm.updateDelegateCache(); err != nil { + log.L().Error("nodeinfo manager update delegate cache failed", zap.Error(err)) + } + } + return dm.delegateCache +} + func (dm *InfoManager) updateDelegateCache() error { candList, err := dm.getDelegates(context.Background()) if err != nil { return err } log.L().Debug("nodeinfo manager active candidates", zap.Any("candidates", candList)) - dm.isDelegate.Store(slices.ContainsFunc(candList, func(e *state.Candidate) bool { + dm.delegateCache = slices.ContainsFunc(candList, func(e *state.Candidate) bool { return dm.address == e.Address - })) + }) return nil }