From fbbe79d8122cef3d04abf42a081c7f87744309ee Mon Sep 17 00:00:00 2001 From: soonkuk Date: Fri, 1 Nov 2024 12:23:00 +0900 Subject: [PATCH 01/19] feat: memory issue patched --- base/point.go | 3 ++- network/quicmemberlist/memberlist.go | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/base/point.go b/base/point.go index 4774fefc..9b1160ca 100644 --- a/base/point.go +++ b/base/point.go @@ -310,7 +310,8 @@ func (p StagePoint) Compare(b StagePoint) int { } func (p StagePoint) Decrease() StagePoint { - return NewStagePoint(p.Point.PrevHeight(), p.stage) + //return NewStagePoint(p.Point.PrevHeight(), p.stage) + return NewStagePoint(p.Point.PrevRound(), p.stage) } func (p StagePoint) MarshalZerologObject(e *zerolog.Event) { diff --git a/network/quicmemberlist/memberlist.go b/network/quicmemberlist/memberlist.go index dc676503..d9428d81 100644 --- a/network/quicmemberlist/memberlist.go +++ b/network/quicmemberlist/memberlist.go @@ -322,14 +322,17 @@ func (srv *Memberlist) CallbackBroadcast(b []byte, id string, notifych chan stru // NOTE save b in cache first srv.cbcache.Set(id, b, srv.args.CallbackBroadcastMessageExpire) - buf := bytes.NewBuffer(callbackBroadcastMessageHeaderPrefix) - defer buf.Reset() - - switch err := srv.args.Encoder.StreamEncoder(buf).Encode( + switch i, err := srv.args.Encoder.Marshal( NewConnInfoBroadcastMessage(id, srv.local.ConnInfo())); { case err != nil: return err default: + buf := bytes.NewBuffer(nil) + defer buf.Reset() + + _, _ = buf.Write(callbackBroadcastMessageHeaderPrefix) + _, _ = buf.Write(i) + srv.Broadcast(NewBroadcast(buf.Bytes(), id, notifych)) } @@ -934,9 +937,6 @@ func (srv *Memberlist) broadcastEnsured(id string, threshold base.Threshold, exc func (srv *Memberlist) loopUserMsgs(ctx context.Context) { sem := semaphore.NewWeighted(maxHandleUserMsg) - go func() { - _ = sem.Acquire(ctx, maxHandleUserMsg+1) - }() ticker := time.NewTicker(time.Millisecond * 33) defer ticker.Stop() From 90889cac7b1b099cd2a070204ee29838c14a5437 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Wed, 23 Apr 2025 12:11:07 +0900 Subject: [PATCH 02/19] fix: add previousBlock nil checking to BlockBasedProposerSelector Select --- isaac/proposer_selector.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/isaac/proposer_selector.go b/isaac/proposer_selector.go index 119e7fea..a34c6bbf 100644 --- a/isaac/proposer_selector.go +++ b/isaac/proposer_selector.go @@ -40,6 +40,10 @@ func (BlockBasedProposerSelector) Select( var sum uint64 + if previousBlock == nil { + return nil, errors.Errorf("empty previous block") + } + for _, b := range previousBlock.Bytes() { sum += uint64(b) } From 83e5237de28b5bedcab7c873b6cf6e4a1c3783e8 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Wed, 11 Jun 2025 11:04:46 +0900 Subject: [PATCH 03/19] feat: extend MemberlistParams to support memberlist and broadcast --- go.mod | 10 ++-- go.sum | 20 ++++---- isaac/params.go | 1 + isaac/states/broadcast.go | 39 +++++++++------ isaac/states/states.go | 5 ++ launch/local_params.go | 72 ++++++++++++++++++++++++++++ launch/local_params_marshal.go | 22 +++++++++ launch/p_memberlist.go | 16 ++++--- launch/p_network_handlers.go | 4 +- launch/p_states.go | 5 ++ launch/ratelimit.go | 2 +- network/quicmemberlist/delegate.go | 3 +- network/quicmemberlist/memberlist.go | 5 +- 13 files changed, 161 insertions(+), 43 deletions(-) diff --git a/go.mod b/go.mod index befd3d0d..8ffbeeb8 100644 --- a/go.mod +++ b/go.mod @@ -28,10 +28,10 @@ require ( github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 github.com/zeebo/blake3 v0.2.3 go.uber.org/goleak v1.3.0 - golang.org/x/crypto v0.23.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 golang.org/x/mod v0.17.0 - golang.org/x/sync v0.7.0 + golang.org/x/sync v0.10.0 golang.org/x/time v0.5.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -87,9 +87,9 @@ require ( go.uber.org/mock v0.4.0 // indirect golang.org/x/arch v0.7.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect - golang.org/x/tools v0.21.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect ) replace github.com/hashicorp/memberlist => github.com/spikeekips/memberlist v0.0.0-20230626195851-39f17fa10d23 // latest fix-data-race branch diff --git a/go.sum b/go.sum index eae03888..29afb41f 100644 --- a/go.sum +++ b/go.sum @@ -362,8 +362,8 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -396,8 +396,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -438,8 +438,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -453,8 +453,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -463,8 +463,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/isaac/params.go b/isaac/params.go index 50763e30..56b288ee 100644 --- a/isaac/params.go +++ b/isaac/params.go @@ -20,6 +20,7 @@ var ( DefaultMinWaitNextBlockINITBallot = time.Second * 2 DefaultStateCacheSize = 1 << 12 DefaultOperationPoolCacheSize = 1 << 12 + DefaultBroadcastTimerMult = 5 ) type Params struct { diff --git a/isaac/states/broadcast.go b/isaac/states/broadcast.go index 0fd2a02e..71b4fbd3 100644 --- a/isaac/states/broadcast.go +++ b/isaac/states/broadcast.go @@ -21,23 +21,26 @@ var ( type ballotBroadcastTimers struct { *sync.RWMutex - timers *util.SimpleTimers - broadcast func(context.Context, base.Ballot) error - points map[util.TimerID]base.StagePoint - interval time.Duration + timers *util.SimpleTimers + broadcast func(context.Context, base.Ballot) error + points map[util.TimerID]base.StagePoint + interval time.Duration + broadcastTimerMult int } func newBallotBroadcastTimers( timers *util.SimpleTimers, broadcast func(context.Context, base.Ballot) error, interval time.Duration, + broadcastTimerMult int, ) *ballotBroadcastTimers { return &ballotBroadcastTimers{ - RWMutex: &sync.RWMutex{}, - timers: timers, - broadcast: broadcast, - points: map[util.TimerID]base.StagePoint{}, - interval: interval, + RWMutex: &sync.RWMutex{}, + timers: timers, + broadcast: broadcast, + points: map[util.TimerID]base.StagePoint{}, + interval: interval, + broadcastTimerMult: broadcastTimerMult, } } @@ -71,11 +74,12 @@ func (bbt *ballotBroadcastTimers) Clone() *ballotBroadcastTimers { defer bbt.RUnlock() return &ballotBroadcastTimers{ - RWMutex: bbt.RWMutex, - timers: bbt.timers, - broadcast: bbt.broadcast, - points: bbt.points, - interval: bbt.interval, + RWMutex: bbt.RWMutex, + timers: bbt.timers, + broadcast: bbt.broadcast, + points: bbt.points, + interval: bbt.interval, + broadcastTimerMult: bbt.broadcastTimerMult, } } @@ -165,7 +169,12 @@ func (bbt *ballotBroadcastTimers) addTimer( if _, err := bbt.timers.New( ti, - intervalf, + func(t uint64) time.Duration { + if strings.HasPrefix(ti.String(), "broadcast-init-ballot/") && !strings.Contains(ti.String(), "round=0") { + return time.Duration(bbt.broadcastTimerMult) * intervalf(t) + } + return intervalf(t) + }, func(ctx context.Context, _ uint64) (bool, error) { return true, bbt.broadcast(ctx, bl) }, diff --git a/isaac/states/states.go b/isaac/states/states.go index 5218021d..71dc0719 100644 --- a/isaac/states/states.go +++ b/isaac/states/states.go @@ -33,6 +33,7 @@ type StatesArgs struct { BallotBroadcaster BallotBroadcaster WhenStateSwitchedFunc func(StateType) IntervalBroadcastBallot func() time.Duration + BroadcastTimerMult func() int WhenNewVoteproof func(base.Voteproof) NewHandoverXBroker NewHandoverXBrokerFunc NewHandoverYBroker NewHandoverYBrokerFunc @@ -55,6 +56,9 @@ func NewStatesArgs() *StatesArgs { IntervalBroadcastBallot: func() time.Duration { return isaac.DefaultntervalBroadcastBallot }, + BroadcastTimerMult: func() int { + return isaac.DefaultBroadcastTimerMult + }, WhenNewVoteproof: func(base.Voteproof) {}, } } @@ -106,6 +110,7 @@ func NewStates(networkID base.NetworkID, local base.LocalNode, args *StatesArgs) return nil }, st.args.IntervalBroadcastBallot(), + st.args.BroadcastTimerMult(), ) if st.args.Ballotbox != nil { diff --git a/launch/local_params.go b/launch/local_params.go index 589713ab..8bb0aa8e 100644 --- a/launch/local_params.go +++ b/launch/local_params.go @@ -84,10 +84,14 @@ type MemberlistParams struct { retransmitMult int probeTimeout time.Duration probeInterval time.Duration + gossipInterval time.Duration suspicionMult int suspicionMaxTimeoutMult int udpBufferSize int extraSameMemberLimit uint64 + broadcastTimerMult int + userMsgLoopInterval time.Duration + gosshipNodes int } func defaultMemberlistParams() *MemberlistParams { @@ -99,10 +103,14 @@ func defaultMemberlistParams() *MemberlistParams { retransmitMult: config.RetransmitMult, probeTimeout: config.ProbeTimeout, probeInterval: config.ProbeInterval, + gossipInterval: config.GossipInterval, suspicionMult: config.SuspicionMult, suspicionMaxTimeoutMult: config.SuspicionMaxTimeoutMult, udpBufferSize: config.UDPBufferSize, extraSameMemberLimit: 1, //nolint:gomnd //... + broadcastTimerMult: 5, + userMsgLoopInterval: time.Millisecond * 33, + gosshipNodes: config.GossipNodes, } } @@ -174,6 +182,38 @@ func (p *MemberlistParams) SetProbeInterval(d time.Duration) error { }) } +func (p *MemberlistParams) GossipInterval() time.Duration { + return p.gossipInterval +} + +func (p *MemberlistParams) SetGossipInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.gossipInterval == d { + return false, nil + } + + p.gossipInterval = d + + return true, nil + }) +} + +func (p *MemberlistParams) GosshipNodes() int { + return p.gosshipNodes +} + +func (p *MemberlistParams) SetGosshipNodes(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.gosshipNodes == d { + return false, nil + } + + p.gosshipNodes = d + + return true, nil + }) +} + func (p *MemberlistParams) SuspicionMult() int { return p.suspicionMult } @@ -238,6 +278,38 @@ func (p *MemberlistParams) SetExtraSameMemberLimit(d uint64) error { }) } +func (p *MemberlistParams) BroadcastTimerMult() int { + return p.broadcastTimerMult +} + +func (p *MemberlistParams) SetBroadcastTimerMult(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.broadcastTimerMult == d { + return false, nil + } + + p.broadcastTimerMult = d + + return true, nil + }) +} + +func (p *MemberlistParams) UserMsgLoopInterval() time.Duration { + return p.userMsgLoopInterval +} + +func (p *MemberlistParams) SetUserMsgLoopInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.userMsgLoopInterval == d { + return false, nil + } + + p.userMsgLoopInterval = d + + return true, nil + }) +} + type MISCParams struct { *util.BaseParams syncSourceCheckerInterval time.Duration diff --git a/launch/local_params_marshal.go b/launch/local_params_marshal.go index 6f15c804..7ed9ba37 100644 --- a/launch/local_params_marshal.go +++ b/launch/local_params_marshal.go @@ -109,10 +109,14 @@ type memberlistParamsMarshaler struct { RetransmitMult int `json:"retransmit_mult,omitempty" yaml:"retransmit_mult,omitempty"` ProbeTimeout util.ReadableDuration `json:"probe_timeout,omitempty" yaml:"probe_timeout,omitempty"` ProbeInterval util.ReadableDuration `json:"probe_interval,omitempty" yaml:"probe_interval,omitempty"` + GossipInterval util.ReadableDuration `json:"gossip_interval,omitempty" yaml:"gossip_interval,omitempty"` + GosshipNodes int `json:"gossip_nodes,omitempty" yaml:"gossip_nodes,omitempty"` SuspicionMult int `json:"suspicion_mult,omitempty" yaml:"suspicion_mult,omitempty"` SuspicionMaxTimeoutMult int `json:"suspicion_max_timeout_mult,omitempty" yaml:"suspicion_max_timeout_mult,omitempty"` UDPBufferSize int `json:"udp_buffer_size,omitempty" yaml:"udp_buffer_size,omitempty"` ExtraSameMemberLimit uint64 `json:"extra_same_member_limit,omitempty" yaml:"extra_same_member_limit,omitempty"` + BroadcastTimerMult int `json:"broadcast_timer_mult,omitempty" yaml:"broadcast_timer_mult,omitempty"` + UserMsgLoopInterval util.ReadableDuration `json:"user_msg_loop_interval,omitempty" yaml:"user_msg_loop_interval,omitempty"` //revive:enable:line-length-limit } @@ -122,10 +126,14 @@ func (p *MemberlistParams) marshaler() memberlistParamsMarshaler { RetransmitMult: p.retransmitMult, ProbeTimeout: util.ReadableDuration(p.probeTimeout), ProbeInterval: util.ReadableDuration(p.probeInterval), + GossipInterval: util.ReadableDuration(p.gossipInterval), + GosshipNodes: p.gosshipNodes, SuspicionMult: p.suspicionMult, SuspicionMaxTimeoutMult: p.suspicionMaxTimeoutMult, UDPBufferSize: p.udpBufferSize, ExtraSameMemberLimit: p.extraSameMemberLimit, + BroadcastTimerMult: p.broadcastTimerMult, + UserMsgLoopInterval: util.ReadableDuration(p.userMsgLoopInterval), } } @@ -143,10 +151,14 @@ type memberlistParamsUnmarshaler struct { RetransmitMult *int `json:"retransmit_mult,omitempty" yaml:"retransmit_mult,omitempty"` ProbeTimeout *util.ReadableDuration `json:"probe_timeout,omitempty" yaml:"probe_timeout,omitempty"` ProbeInterval *util.ReadableDuration `json:"probe_interval,omitempty" yaml:"probe_interval,omitempty"` + GossipInterval *util.ReadableDuration `json:"gossip_interval,omitempty" yaml:"gossip_interval,omitempty"` + GosshipNodes *int `json:"gossip_nodes,omitempty" yaml:"gossip_nodes,omitempty"` SuspicionMult *int `json:"suspicion_mult,omitempty" yaml:"suspicion_mult,omitempty"` SuspicionMaxTimeoutMult *int `json:"suspicion_max_timeout_mult,omitempty" yaml:"suspicion_max_timeout_mult,omitempty"` UDPBufferSize *int `json:"udp_buffer_size,omitempty" yaml:"udp_buffer_size,omitempty"` ExtraSameMemberLimit *uint64 `json:"extra_same_member_limit,omitempty" yaml:"extra_same_member_limit,omitempty"` + BroadcastTimerMult *int `json:"broadcast_timer_mult,omitempty" yaml:"broadcast_timer_mult,omitempty"` + UserMsgLoopInterval *util.ReadableDuration `json:"user_msg_loop_interval,omitempty" yaml:"user_msg_loop_interval,omitempty"` //revive:enable:line-length-limit } @@ -201,10 +213,20 @@ func (p *MemberlistParams) unmarshal(u memberlistParamsUnmarshaler) error { p.extraSameMemberLimit = *u.ExtraSameMemberLimit } + if u.BroadcastTimerMult != nil { + p.broadcastTimerMult = *u.BroadcastTimerMult + } + + if u.GosshipNodes != nil { + p.gosshipNodes = *u.GosshipNodes + } + durargs := [][2]interface{}{ {u.TCPTimeout, &p.tcpTimeout}, {u.ProbeTimeout, &p.probeTimeout}, {u.ProbeInterval, &p.probeInterval}, + {u.GossipInterval, &p.gossipInterval}, + {u.UserMsgLoopInterval, &p.userMsgLoopInterval}, } for i := range durargs { diff --git a/launch/p_memberlist.go b/launch/p_memberlist.go index 8600ca22..e650d451 100644 --- a/launch/p_memberlist.go +++ b/launch/p_memberlist.go @@ -77,17 +77,19 @@ func PMemberlist(pctx context.Context) (context.Context, error) { args := quicmemberlist.NewMemberlistArgs(encs.JSON(), config) args.ExtraSameMemberLimit = params.Memberlist.ExtraSameMemberLimit args.FetchCallbackBroadcastMessageFunc = quicmemberlist.FetchCallbackBroadcastMessageFunc( - handlerPrefixMemberlistCallbackBroadcastMessage, + HandlerPrefixMemberlistCallbackBroadcastMessage, headerdial, ) args.PongEnsureBroadcastMessageFunc = quicmemberlist.PongEnsureBroadcastMessageFunc( - handlerPrefixMemberlistEnsureBroadcastMessage, + HandlerPrefixMemberlistEnsureBroadcastMessage, local.Address(), local.Privatekey(), params.ISAAC.NetworkID(), headerdial, ) + args.BroadcastTimerMult = params.Memberlist.broadcastTimerMult + args.UserMsgLoopInterval = params.Memberlist.userMsgLoopInterval m, err := quicmemberlist.NewMemberlist(localnode, args) if err != nil { @@ -377,6 +379,8 @@ func memberlistConfig( return nil, err } + params := design.LocalParams.Memberlist + transport, err := memberlistTransport(pctx, connectionPool.Dial) if err != nil { return nil, err @@ -384,7 +388,7 @@ func memberlistConfig( delegate := quicmemberlist.NewDelegate(localnode, nil, func([]byte) { panic("set notifyMsgFunc") - }) + }, params.RetransmitMult()) alive, err := memberlistAlive(pctx) if err != nil { @@ -397,8 +401,6 @@ func memberlistConfig( design.Network.Publish(), ) - params := design.LocalParams.Memberlist - config.TCPTimeout = params.TCPTimeout() config.RetransmitMult = params.RetransmitMult() config.ProbeTimeout = params.ProbeTimeout() @@ -406,7 +408,7 @@ func memberlistConfig( config.SuspicionMult = params.SuspicionMult() config.SuspicionMaxTimeoutMult = params.SuspicionMaxTimeoutMult() config.UDPBufferSize = params.UDPBufferSize() - + config.GossipNodes = params.GosshipNodes() config.Transport = transport config.Delegate = delegate config.Alive = alive @@ -493,7 +495,7 @@ func memberlistTransport( timeoutf = f } - handler := rateLimitHandlerFunc( + handler := RateLimitHandlerFunc( rateLimitHandler, func(prefix quicstream.HandlerPrefix) (string, bool) { s, found := NetworkHandlerPrefixMapRev[prefix] diff --git a/launch/p_network_handlers.go b/launch/p_network_handlers.go index eff2de9a..df235b24 100644 --- a/launch/p_network_handlers.go +++ b/launch/p_network_handlers.go @@ -37,8 +37,8 @@ var ( HandlerNameMemberlistCallbackBroadcastMessage quicstream.HandlerName = "memberlist_callback_broadcast_message" HandlerNameMemberlistEnsureBroadcastMessage quicstream.HandlerName = "memberlist_ensure_broadcast_message" - handlerPrefixMemberlistCallbackBroadcastMessage = quicstream.HashPrefix(HandlerNameMemberlistCallbackBroadcastMessage) - handlerPrefixMemberlistEnsureBroadcastMessage = quicstream.HashPrefix(HandlerNameMemberlistEnsureBroadcastMessage) + HandlerPrefixMemberlistCallbackBroadcastMessage = quicstream.HashPrefix(HandlerNameMemberlistCallbackBroadcastMessage) + HandlerPrefixMemberlistEnsureBroadcastMessage = quicstream.HashPrefix(HandlerNameMemberlistEnsureBroadcastMessage) //revive:enable:line-length-limit ) diff --git a/launch/p_states.go b/launch/p_states.go index 8d74d339..2d10168f 100644 --- a/launch/p_states.go +++ b/launch/p_states.go @@ -146,6 +146,7 @@ func PStates(pctx context.Context) (context.Context, error) { var pool *isaacdatabase.TempPool var m *quicmemberlist.Memberlist var nodeinfo *isaacnetwork.NodeInfoUpdater + var design NodeDesign if err := util.LoadFromContextOK(pctx, LoggingContextKey, &log, @@ -160,12 +161,16 @@ func PStates(pctx context.Context) (context.Context, error) { PoolDatabaseContextKey, &pool, MemberlistContextKey, &m, NodeInfoContextKey, &nodeinfo, + DesignContextKey, &design, ); err != nil { return pctx, e.Wrap(err) } args.IntervalBroadcastBallot = isaacparams.IntervalBroadcastBallot args.AllowConsensus = devflags.AllowConsensus + args.BroadcastTimerMult = func() int { + return design.LocalParams.Memberlist.broadcastTimerMult + } if vp := args.LastVoteproofsHandler.Last().Cap(); vp != nil { last := args.Ballotbox.LastPoint() diff --git a/launch/ratelimit.go b/launch/ratelimit.go index ee47643a..5b425fb1 100644 --- a/launch/ratelimit.go +++ b/launch/ratelimit.go @@ -1139,7 +1139,7 @@ func humanizeRateLimiter(limit rate.Limit, burst int) string { } } -func rateLimitHandlerFunc( +func RateLimitHandlerFunc( ratelimiter *RateLimitHandler, findPrefix func(quicstream.HandlerPrefix) (string, bool), ) func(quicstream.Handler) quicstream.Handler { diff --git a/network/quicmemberlist/delegate.go b/network/quicmemberlist/delegate.go index c3f94f4d..5afe6c68 100644 --- a/network/quicmemberlist/delegate.go +++ b/network/quicmemberlist/delegate.go @@ -34,8 +34,9 @@ func NewDelegate( local Member, numNodes func() int, notifyMsgFunc func(b []byte), + reTransmitMult int, ) *Delegate { - qu := &memberlist.TransmitLimitedQueue{NumNodes: numNodes, RetransmitMult: 2} + qu := &memberlist.TransmitLimitedQueue{NumNodes: numNodes, RetransmitMult: reTransmitMult} if notifyMsgFunc == nil { notifyMsgFunc = func([]byte) {} //revive:disable-line:modifies-parameter diff --git a/network/quicmemberlist/memberlist.go b/network/quicmemberlist/memberlist.go index d9428d81..d606b84e 100644 --- a/network/quicmemberlist/memberlist.go +++ b/network/quicmemberlist/memberlist.go @@ -54,6 +54,8 @@ type MemberlistArgs struct { FetchCallbackBroadcastMessageTimeout time.Duration PongEnsureBroadcastMessageTimeout time.Duration PongEnsureBroadcastMessageExpire time.Duration + UserMsgLoopInterval time.Duration + BroadcastTimerMult int } func NewMemberlistArgs(jsonencoder encoder.Encoder, config *memberlist.Config) *MemberlistArgs { @@ -937,8 +939,7 @@ func (srv *Memberlist) broadcastEnsured(id string, threshold base.Threshold, exc func (srv *Memberlist) loopUserMsgs(ctx context.Context) { sem := semaphore.NewWeighted(maxHandleUserMsg) - - ticker := time.NewTicker(time.Millisecond * 33) + ticker := time.NewTicker(srv.args.UserMsgLoopInterval) defer ticker.Stop() for { From 4a37189e22a1045494ed362ae2fcbd7c58c0c86f Mon Sep 17 00:00:00 2001 From: soonkuk Date: Mon, 18 Aug 2025 14:44:48 +0900 Subject: [PATCH 04/19] chore: improve error message wording --- isaac/network/quicstream_handlers.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isaac/network/quicstream_handlers.go b/isaac/network/quicstream_handlers.go index 7d35f109..7ba52bcf 100644 --- a/isaac/network/quicstream_handlers.go +++ b/isaac/network/quicstream_handlers.go @@ -93,7 +93,7 @@ func QuicstreamHandlerSendOperation( case err != nil: return ctx, e.Wrap(err) case uint64(len(i)) > max: - return ctx, e.Errorf("too big size; >= %d", max) + return ctx, e.Errorf("too big size; %d >= %d, max message size", uint64(len(i)), max) default: if err = encoder.Decode(broker.Encoder, i, &op); err != nil { return ctx, e.Wrap(err) @@ -594,7 +594,7 @@ func QuicstreamHandlerSendBallots( case err != nil: return ctx, e.Wrap(err) case uint64(len(body)) > max: - return ctx, e.Errorf("too big size; >= %d", max) + return ctx, e.Errorf("too big size; %d >= %d, max message size", uint64(len(body)), max) default: body = i } From b39e32439fb123c49eb861b41c48fdd6ccb79acf Mon Sep 17 00:00:00 2001 From: soonkuk Date: Mon, 18 Aug 2025 17:17:56 +0900 Subject: [PATCH 05/19] fix: correct user message list iteration in handleUserMsgs --- network/quicmemberlist/memberlist.go | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/network/quicmemberlist/memberlist.go b/network/quicmemberlist/memberlist.go index d606b84e..f60e0e86 100644 --- a/network/quicmemberlist/memberlist.go +++ b/network/quicmemberlist/memberlist.go @@ -965,30 +965,23 @@ func (srv *Memberlist) handleUserMsgs(ctx context.Context, sem *semaphore.Weight addBS, doneBS := util.CompactAppendSlice[[]byte](int(maxHandleUserMsg)) - var elem *list.Element + for elem := srv.usermsgs.Front(); elem != nil; { + next := elem.Next() - end: - for { - switch { - case elem == nil: - elem = srv.usermsgs.Front() - default: - elem = elem.Next() - } - - if elem == nil { - return doneBS() + val, ok := elem.Value.([]byte) + if !ok || len(val) < 1 { + elem = next + continue } srv.usermsgs.Remove(elem) - switch b, ok := elem.Value.([]byte); { - case !ok, len(b) < 1: - continue end - case addBS(b): + if addBS(val) { return doneBS() } + elem = next } + return doneBS() }() if len(bs) < 1 { @@ -1004,7 +997,6 @@ func (srv *Memberlist) handleUserMsgs(ctx context.Context, sem *semaphore.Weight go func() { defer sem.Release(1) - srv.handleUserMsg(b) }() } From f7084845a9fdb8308d945424c527e56815cb2f5d Mon Sep 17 00:00:00 2001 From: soonkuk Date: Mon, 18 Aug 2025 17:56:59 +0900 Subject: [PATCH 06/19] fix: eliminate goroutine leak in EnsureRead --- util/bytes.go | 48 +++++++++++++++++------------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/util/bytes.go b/util/bytes.go index 7a5452a6..d82ffd53 100644 --- a/util/bytes.go +++ b/util/bytes.go @@ -96,33 +96,25 @@ func EnsureRead(ctx context.Context, r io.Reader, b []byte) (n uint64, _ error) return 0, nil } - readch := make(chan [3]interface{}) - - read := func() { - l := make([]byte, uint64(len(b))-n) - - i, err := r.Read(l) - - readch <- [3]interface{}{i, err, l[:i]} - } - - go read() - - for { + type result struct { + i int + e error + } + readch := make(chan result) + + for n < uint64(len(b)) { + go func(dst []byte) { + i, err := r.Read(dst) + select { + case readch <- result{i, err}: + case <-ctx.Done(): + } + }(b[n:]) select { case <-ctx.Done(): return n, errors.WithStack(ctx.Err()) - case j, notclosed := <-readch: - if !notclosed { - return n, errors.Errorf("readch closed") - } - - var err error - if e, ok := j[1].(error); ok { - err = e - } - - i := j[0].(int) //nolint:forcetypeassert //... + case res := <-readch: + i, err := res.i, res.e iseof := errors.Is(err, io.EOF) if err != nil && !iseof { @@ -130,12 +122,7 @@ func EnsureRead(ctx context.Context, r io.Reader, b []byte) (n uint64, _ error) return n, errors.WithStack(err) } - - l := j[2].([]byte) //nolint:forcetypeassert //... - if i > 0 { - copy(b[n:], l) - n += uint64(i) } @@ -144,11 +131,10 @@ func EnsureRead(ctx context.Context, r io.Reader, b []byte) (n uint64, _ error) return n, errors.WithStack(err) case iseof: return n, errors.Errorf("insufficient read") - default: - go read() } } } + return } func EnsureWrite(w io.Writer, b []byte) (int, error) { From e5ffb2d0da644da95c3c6c5a0247bdc511d9f30d Mon Sep 17 00:00:00 2001 From: soonkuk Date: Fri, 5 Sep 2025 14:35:12 +0900 Subject: [PATCH 07/19] fix: add validation logic to LastVoteproofsHandler.Set --- isaac/last_voteproofs.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/isaac/last_voteproofs.go b/isaac/last_voteproofs.go index 5d6ffd58..59ae744b 100644 --- a/isaac/last_voteproofs.go +++ b/isaac/last_voteproofs.go @@ -142,6 +142,21 @@ func (l *LastVoteproofsHandler) Set(vp base.Voteproof) bool { lvps := l.last + if lvps.ivp == nil && lvps.avp == nil { + if vp.Point().Stage() != base.StageINIT || vp.Majority() == nil { + + return false + } + } + if lvps.ivp != nil && lvps.avp == nil { + if lvp := lvps.Cap(); lvp != nil { + if !lvp.Point().Point.Equal(vp.Point().Point) { + + return false + } + } + } + if lvp := lvps.Cap(); lvp != nil { lp, err := NewLastPointFromVoteproof(lvp) if err != nil { From a6f11c64affab7a6581ee0c0dfddb8726690745e Mon Sep 17 00:00:00 2001 From: soonkuk Date: Mon, 8 Sep 2025 09:48:08 +0900 Subject: [PATCH 08/19] fix: typo in error message --- base/point.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/point.go b/base/point.go index 9b1160ca..ec5a7b89 100644 --- a/base/point.go +++ b/base/point.go @@ -36,7 +36,7 @@ func ParseHeightString(s string) (Height, error) { i, err := strconv.ParseInt(n, 10, 64) if err != nil { - return NilHeight, errors.Wrap(err, "seHeightString") + return NilHeight, errors.Wrap(err, "ParseHeightString") } return Height(i), nil @@ -45,7 +45,7 @@ func ParseHeightString(s string) (Height, error) { func ParseHeightBytes(b []byte) (Height, error) { i, err := util.BigBytesToInt64(b) if err != nil { - return NilHeight, errors.Wrap(err, "seHeightBytes") + return NilHeight, errors.Wrap(err, "ParseHeightBytes") } return Height(i), nil From 6c97c4c93b690bf9cb8f9ffbdb6dcba7a2a07b45 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Wed, 10 Sep 2025 08:14:11 +0900 Subject: [PATCH 09/19] fix: adjust FetchCallbackBroadcastMessageTimeout value to reduce application 0x0 error --- network/quicmemberlist/memberlist.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/network/quicmemberlist/memberlist.go b/network/quicmemberlist/memberlist.go index f60e0e86..1a39ff0c 100644 --- a/network/quicmemberlist/memberlist.go +++ b/network/quicmemberlist/memberlist.go @@ -67,7 +67,7 @@ func NewMemberlistArgs(jsonencoder encoder.Encoder, config *memberlist.Config) * NotAllowedMemberExpire: time.Second * 6, //nolint:gomnd //... ChallengeExpire: defaultNodeChallengeExpire, CallbackBroadcastMessageExpire: time.Second * 30, //nolint:gomnd //... - FetchCallbackBroadcastMessageTimeout: time.Second * 6, //nolint:gomnd //... + FetchCallbackBroadcastMessageTimeout: time.Second * 30, //nolint:gomnd //... FetchCallbackBroadcastMessageFunc: func(context.Context, ConnInfoBroadcastMessage) ( []byte, encoder.Encoder, error, ) { From 17e0ede8027689c0d83f54a9ce1f1fce862d4940 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Wed, 10 Sep 2025 09:11:31 +0900 Subject: [PATCH 10/19] feat: add SyncerLastBlockMapInterval to isaac params --- go.mod | 79 +++++++++-------- go.sum | 214 ++++++++++++++++++--------------------------- isaac/params.go | 32 ++++++- launch/p_states.go | 1 + 4 files changed, 159 insertions(+), 167 deletions(-) diff --git a/go.mod b/go.mod index 8ffbeeb8..83350b0c 100644 --- a/go.mod +++ b/go.mod @@ -1,75 +1,77 @@ module github.com/ProtoconNet/mitum2 -go 1.21 +go 1.24.0 + +toolchain go1.24.7 require ( - github.com/Masterminds/semver/v3 v3.2.1 - github.com/alecthomas/kong v0.9.0 - github.com/alicebob/miniredis/v2 v2.31.0 - github.com/arl/statsviz v0.6.0 - github.com/beevik/ntp v1.3.1 + github.com/Masterminds/semver/v3 v3.4.0 + github.com/alecthomas/kong v1.12.1 + github.com/alicebob/miniredis/v2 v2.35.0 + github.com/arl/statsviz v0.7.1 + github.com/beevik/ntp v1.4.3 github.com/bluele/gcache v0.0.2 - github.com/btcsuite/btcd/btcec/v2 v2.3.2 + github.com/btcsuite/btcd/btcec/v2 v2.3.5 github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce - github.com/bytedance/sonic v1.11.3 + github.com/bytedance/sonic v1.14.1 github.com/gofrs/uuid v4.4.0+incompatible - github.com/hashicorp/consul/api v1.28.2 + github.com/hashicorp/consul/api v1.32.1 github.com/hashicorp/memberlist v0.5.1 - github.com/hashicorp/vault/api v1.12.2 + github.com/hashicorp/vault/api v1.20.0 github.com/json-iterator/go v1.1.12 github.com/mattn/go-isatty v0.0.20 - github.com/oklog/ulid/v2 v2.1.0 + github.com/oklog/ulid/v2 v2.1.1 github.com/pkg/errors v0.9.1 github.com/quic-go/quic-go v0.39.1 - github.com/redis/go-redis/v9 v9.5.1 - github.com/rs/zerolog v1.32.0 - github.com/stretchr/testify v1.9.0 + github.com/redis/go-redis/v9 v9.13.0 + github.com/rs/zerolog v1.34.0 + github.com/stretchr/testify v1.11.1 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 - github.com/zeebo/blake3 v0.2.3 + github.com/zeebo/blake3 v0.2.4 go.uber.org/goleak v1.3.0 - golang.org/x/crypto v0.31.0 - golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 - golang.org/x/mod v0.17.0 - golang.org/x/sync v0.10.0 - golang.org/x/time v0.5.0 + golang.org/x/crypto v0.42.0 + golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b + golang.org/x/mod v0.28.0 + golang.org/x/sync v0.17.0 + golang.org/x/time v0.13.0 gopkg.in/yaml.v3 v3.0.1 ) require ( - github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a // indirect github.com/armon/go-metrics v0.4.1 // indirect - github.com/cenkalti/backoff/v3 v3.2.2 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect - github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect - github.com/chenzhuoyu/iasm v0.9.1 // indirect + github.com/bytedance/gopkg v0.1.3 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.3 // indirect + github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f // indirect - github.com/gorilla/websocket v1.5.1 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-immutable-radix v1.3.1 // indirect github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/go-retryablehttp v0.7.5 // indirect + github.com/hashicorp/go-retryablehttp v0.7.7 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect github.com/hashicorp/go-sockaddr v1.0.6 // indirect github.com/hashicorp/go-version v1.6.0 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/hcl v1.0.1-vault-7 // indirect github.com/hashicorp/serf v0.10.1 // indirect - github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/miekg/dns v1.1.58 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect @@ -77,19 +79,20 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/ginkgo/v2 v2.17.1 // indirect + github.com/onsi/ginkgo/v2 v2.13.0 // indirect + github.com/onsi/gomega v1.30.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/quic-go/qtls-go1-20 v0.3.4 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/yuin/gopher-lua v1.1.0 // indirect - go.uber.org/mock v0.4.0 // indirect + github.com/yuin/gopher-lua v1.1.1 // indirect + go.uber.org/mock v0.5.0 // indirect golang.org/x/arch v0.7.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect + golang.org/x/tools v0.36.0 // indirect ) replace github.com/hashicorp/memberlist => github.com/spikeekips/memberlist v0.0.0-20230626195851-39f17fa10d23 // latest fix-data-race branch diff --git a/go.sum b/go.sum index 29afb41f..fc78a7cf 100644 --- a/go.sum +++ b/go.sum @@ -1,32 +1,29 @@ github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/DmitriyVTitov/size v1.5.0/go.mod h1:le6rNI4CoLQV1b9gzp1+3d7hMAD/uu2QcJ+aYbNgiU0= -github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= -github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= -github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= -github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= -github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA= -github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os= +github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= +github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW0= +github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= -github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= -github.com/alicebob/miniredis/v2 v2.31.0 h1:ObEFUNlJwoIiyjxdrYF0QIDE7qXcLc7D3WpSH4c22PU= -github.com/alicebob/miniredis/v2 v2.31.0/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CASoprx0wulRT6HBg= -github.com/arl/statsviz v0.6.0 h1:jbW1QJkEYQkufd//4NDYRSNBpwJNrdzPahF7ZmoGdyE= -github.com/arl/statsviz v0.6.0/go.mod h1:0toboo+YGSUXDaS4g1D5TVS4dXs7S7YYT5J/qnW2h8s= +github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= +github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= +github.com/arl/statsviz v0.7.1 h1:W32VBGV/YBTMg3sBsr1Ix1bJzrykZHWNZS7quqtDqYc= +github.com/arl/statsviz v0.7.1/go.mod h1:uFJZYUcGDeFpo/Mb9nLkq/83YUYaib2IccejOm+y1t0= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beevik/ntp v1.3.1 h1:Y/srlT8L1yQr58kyPWFPZIxRL8ttx2SRIpVYJqZIlAM= -github.com/beevik/ntp v1.3.1/go.mod h1:fT6PylBq86Tsq23ZMEe47b7QQrZfYBFPnpzt0a9kJxw= +github.com/beevik/ntp v1.4.3 h1:PlbTvE5NNy4QHmA4Mg57n7mcFTmr1W1j3gcK7L1lqho= +github.com/beevik/ntp v1.4.3/go.mod h1:Unr8Zg+2dRn7d8bHFuehIMSvvUYssHMxW3Q5Nx4RW5Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= @@ -38,8 +35,8 @@ github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdb github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= -github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= -github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= +github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU= +github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= @@ -51,27 +48,24 @@ github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVa github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= -github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= -github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= -github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= -github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= -github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs= +github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= +github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= +github.com/bytedance/sonic v1.14.1 h1:FBMC0zVz5XUmE4z9wF4Jey0An5FueFvOsTKKKtwIl7w= +github.com/bytedance/sonic v1.14.1/go.mod h1:gi6uhQLMbTdeP0muCnrjHLeCUPyb70ujhnNlhOylAFc= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= -github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= -github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= -github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= -github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -93,14 +87,14 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= -github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= +github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= @@ -111,7 +105,6 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -131,25 +124,23 @@ github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl76 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY= -github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= -github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= -github.com/hashicorp/consul/api v1.28.2 h1:mXfkRHrpHN4YY3RqL09nXU1eHKLNiuAN4kHvDQ16k/8= -github.com/hashicorp/consul/api v1.28.2/go.mod h1:KyzqzgMEya+IZPcD65YFoOVAgPpbfERu4I/tzG6/ueE= -github.com/hashicorp/consul/sdk v0.16.0 h1:SE9m0W6DEfgIVCJX7xU+iv/hUl4m/nxqMTnCdMxDpJ8= -github.com/hashicorp/consul/sdk v0.16.0/go.mod h1:7pxqqhqoaPqnBnzXD1StKed62LqJeClzVsUEy85Zr0A= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= +github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= +github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= +github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= @@ -163,8 +154,8 @@ github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= -github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= +github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc= @@ -184,17 +175,18 @@ github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I= +github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= -github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE= -github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE= +github.com/hashicorp/vault/api v1.20.0 h1:KQMHElgudOsr+IbJgmbjHnCTxEpKs9LnozA1D3nozU4= +github.com/hashicorp/vault/api v1.20.0/go.mod h1:GZ4pcjfzoOWpkJ3ijHNpEoAxKEsBJnVljyTe3jM2Sms= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -203,19 +195,15 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= -github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= -github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= @@ -254,16 +242,16 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= -github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= +github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= +github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= -github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -297,13 +285,13 @@ github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNV github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= github.com/quic-go/quic-go v0.39.1 h1:d/m3oaN/SD2c+f7/yEjZxe2zEVotXprnrCCJ2y/ZZFE= github.com/quic-go/quic-go v0.39.1/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= -github.com/redis/go-redis/v9 v9.5.1 h1:H1X4D3yHPaYrkL5X06Wh6xNVM/pX0Ft4RV0vMGvLBh8= -github.com/redis/go-redis/v9 v9.5.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= -github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/redis/go-redis/v9 v9.13.0 h1:PpmlVykE0ODh8P43U0HqC+2NXHXwG+GUtQyz+MPKGRg= +github.com/redis/go-redis/v9 v9.13.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= @@ -324,33 +312,30 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= -github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M= +github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= -github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg= -github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ= +github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= +github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= -go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -360,17 +345,13 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= -golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b h1:DXr+pvt3nC887026GRP39Ej11UATqWDmWuS99x26cD0= +golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -382,11 +363,8 @@ golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -394,15 +372,12 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -413,6 +388,7 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -424,47 +400,31 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI= +golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -492,5 +452,3 @@ gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= -rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/isaac/params.go b/isaac/params.go index 56b288ee..e5c61128 100644 --- a/isaac/params.go +++ b/isaac/params.go @@ -18,6 +18,7 @@ var ( DefaultWaitStuckInterval = time.Second * 33 DefaultTimeoutRequest = time.Second * 3 DefaultMinWaitNextBlockINITBallot = time.Second * 2 + DefaultSyncerLastBlockMapInterval = time.Second * 2 DefaultStateCacheSize = 1 << 12 DefaultOperationPoolCacheSize = 1 << 12 DefaultBroadcastTimerMult = 5 @@ -33,6 +34,7 @@ type Params struct { ballotStuckWait time.Duration ballotStuckResolveAfter time.Duration minWaitNextBlockINITBallot time.Duration + syncerLastBlockMapInterval time.Duration maxTryHandoverYBrokerSyncData uint64 stateCacheSize int operationPoolCacheSize int @@ -56,7 +58,8 @@ func DefaultParams(networkID base.NetworkID) *Params { waitPreparingINITBallot: DefaultWaitPreparingINITBallot, ballotStuckWait: time.Second * 33, //nolint:gomnd // waitPreparingINITBallot * 10 ballotStuckResolveAfter: time.Second * 66, //nolint:gomnd // ballotStuckWait * 2 - maxTryHandoverYBrokerSyncData: 33, //nolint:gomnd //... + syncerLastBlockMapInterval: DefaultSyncerLastBlockMapInterval, + maxTryHandoverYBrokerSyncData: 33, //nolint:gomnd //... minWaitNextBlockINITBallot: DefaultMinWaitNextBlockINITBallot, stateCacheSize: DefaultStateCacheSize, operationPoolCacheSize: DefaultOperationPoolCacheSize, @@ -102,6 +105,10 @@ func (p *Params) IsValid(networkID []byte) error { return e.Errorf("wrong duration; invalid minWaitNextBlockINITBallot") } + if p.syncerLastBlockMapInterval < 0 { + return e.Errorf("wrong duration; invalid syncerLastBlockMapInterval") + } + if p.stateCacheSize < 0 { return e.Errorf("wrong state cache size") } @@ -238,6 +245,25 @@ func (p *Params) SetBallotStuckResolveAfter(d time.Duration) error { }) } +func (p *Params) SyncerLastBlockMapInterval() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.syncerLastBlockMapInterval +} + +func (p *Params) SetSyncerLastBlockMapInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.syncerLastBlockMapInterval == d { + return false, nil + } + + p.syncerLastBlockMapInterval = d + + return true, nil + }) +} + func (p *Params) MaxTryHandoverYBrokerSyncData() uint64 { p.RLock() defer p.RUnlock() @@ -332,6 +358,7 @@ type paramsJSONMarshaler struct { BallotStuckWait util.ReadableDuration `json:"ballot_stuck_wait,omitempty"` BallotStuckResolveAfter util.ReadableDuration `json:"ballot_stuck_resolve_after,omitempty"` MinWaitNextBlockINITBallot util.ReadableDuration `json:"min_wait_next_block_init_ballot,omitempty"` + SyncerLastBlockMapInterval util.ReadableDuration `json:"syncer_last_block_map_interval,omitempty"` MaxTryHandoverYBrokerSyncData uint64 `json:"max_try_handover_y_broker_sync_data,omitempty"` StateCacheSize int `json:"state_cache_size,omitempty"` OperationPoolCacheSize int `json:"operation_pool_cache_size,omitempty"` @@ -347,6 +374,7 @@ func (p *Params) MarshalJSON() ([]byte, error) { BallotStuckResolveAfter: util.ReadableDuration(p.ballotStuckResolveAfter), BallotStuckWait: util.ReadableDuration(p.ballotStuckWait), MinWaitNextBlockINITBallot: util.ReadableDuration(p.minWaitNextBlockINITBallot), + SyncerLastBlockMapInterval: util.ReadableDuration(p.syncerLastBlockMapInterval), MaxTryHandoverYBrokerSyncData: p.maxTryHandoverYBrokerSyncData, StateCacheSize: p.stateCacheSize, OperationPoolCacheSize: p.operationPoolCacheSize, @@ -361,6 +389,7 @@ type paramsJSONUnmarshaler struct { BallotStuckWait *util.ReadableDuration `json:"ballot_stuck_wait,omitempty"` BallotStuckResolveAfter *util.ReadableDuration `json:"ballot_stuck_resolve_after,omitempty"` MinWaitNextBlockINITBallot *util.ReadableDuration `json:"min_wait_next_block_init_ballot,omitempty"` + SyncerLastBlockMapInterval *util.ReadableDuration `json:"syncer_last_block_map_interval,omitempty"` MaxTryHandoverYBrokerSyncData *uint64 `json:"max_try_handover_y_broker_sync_data,omitempty"` StateCacheSize *int `json:"state_cache_size,omitempty"` OperationPoolCacheSize *int `json:"operation_pool_cache_size,omitempty"` @@ -395,6 +424,7 @@ func (p *Params) UnmarshalJSON(b []byte) error { {u.BallotStuckResolveAfter, &p.ballotStuckResolveAfter}, {u.BallotStuckWait, &p.ballotStuckWait}, {u.MinWaitNextBlockINITBallot, &p.minWaitNextBlockINITBallot}, + {u.SyncerLastBlockMapInterval, &p.syncerLastBlockMapInterval}, } for i := range durargs { diff --git a/launch/p_states.go b/launch/p_states.go index 2d10168f..0e26d7c3 100644 --- a/launch/p_states.go +++ b/launch/p_states.go @@ -718,6 +718,7 @@ func newSyncerArgsFunc(pctx context.Context) (func(base.Height) (isaacstates.Syn args = isaacstates.NewSyncerArgs() args.LastBlockMapFunc = syncerLastBlockMapFunc(client, isaacparams, syncSourcePool) args.LastBlockMapTimeout = params.Network.TimeoutRequest() + args.LastBlockMapInterval = isaacparams.SyncerLastBlockMapInterval() args.BlockMapFunc = syncerBlockMapFunc(log, client, params, syncSourcePool, conninfocache, devflags.DelaySyncer) args.TempSyncPool = tempsyncpool args.WhenStoppedFunc = func() error { From b8acc19b7b30bec07ad87bcda779121b7f0c9c16 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Mon, 15 Sep 2025 19:12:50 +0900 Subject: [PATCH 11/19] fix: duplicated operation in TempPool.OperationHashes --- isaac/database/pool.go | 50 ++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/isaac/database/pool.go b/isaac/database/pool.go index b528fbd7..494df5d1 100644 --- a/isaac/database/pool.go +++ b/isaac/database/pool.go @@ -280,25 +280,24 @@ func (db *TempPool) OperationHashes( } ops := make([][2]util.Hash, limit) - removeordereds := make([][]byte, limit) - removeops := make([]util.Hash, limit) + removeOrdered := make([][]byte, limit) + removeOps := make([]util.Hash, limit) - var opsindex uint64 - var removeorderedsindex, removeopsindex uint64 - - facts := map[string]uint64{} + facts := make(map[string]struct{}) defer func() { clear(facts) facts = nil }() + var opsindex uint64 + var removeOrderedIndex, removeOpsIndex uint64 if err := pst.Iter( leveldbutil.BytesPrefix(leveldbKeyPrefixNewOperationOrdered[:]), func(k []byte, b []byte) (bool, error) { meta, err := ReadFrameHeaderOperation(b) if err != nil { - removeordereds[removeorderedsindex] = k - removeorderedsindex++ + removeOrdered[removeOrderedIndex] = k + removeOrderedIndex++ return true, nil } @@ -307,35 +306,28 @@ func (db *TempPool) OperationHashes( case err != nil: return false, err case !ok: - removeops[removeopsindex] = meta.Operation() - removeopsindex++ + removeOps[removeOpsIndex] = meta.Operation() + removeOpsIndex++ - if removeopsindex == limit { + if removeOpsIndex == limit { return false, nil } return true, nil } - // NOTE filter duplicated fact; last one will be selected - if prev, found := facts[meta.Fact().String()]; found { - removeops[removeopsindex] = meta.Operation() - removeopsindex++ - - nops := make([][2]util.Hash, len(ops)) - copy(nops, ops[:prev]) - copy(nops[prev:], ops[prev+1:]) + if _, found := facts[meta.Fact().String()]; found { - ops = nops - - opsindex-- + return true, nil + } else { + facts[meta.Fact().String()] = struct{}{} + ops[opsindex] = [2]util.Hash{meta.Operation(), meta.Fact()} + removeOps[removeOpsIndex] = meta.Operation() + removeOpsIndex++ + opsindex++ } - ops[opsindex] = [2]util.Hash{meta.Operation(), meta.Fact()} - facts[meta.Fact().String()] = opsindex - opsindex++ - - if opsindex == limit { + if removeOpsIndex == limit || opsindex == limit { return false, nil } @@ -346,11 +338,11 @@ func (db *TempPool) OperationHashes( return nil, e.Wrap(err) } - if err := db.removeNewOperationOrdereds(removeordereds[:removeorderedsindex]); err != nil { + if err := db.removeNewOperationOrdereds(removeOrdered[:removeOrderedIndex]); err != nil { return nil, e.Wrap(err) } - if err := db.setRemoveNewOperations(ctx, height, removeops[:removeopsindex]); err != nil { + if err := db.setRemoveNewOperations(ctx, height, removeOps[:removeOpsIndex]); err != nil { return nil, e.Wrap(err) } From 1e1b02cf44c4b63e96df57d8520f8ce2bf2905e4 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Tue, 16 Sep 2025 10:09:41 +0900 Subject: [PATCH 12/19] refact: add minProposerWait to isaac params --- isaac/params.go | 30 ++++++++++++++++++++++++++++++ isaac/proposal_selector.go | 4 ++-- launch/p_proposal.go | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/isaac/params.go b/isaac/params.go index e5c61128..0d9a5a3b 100644 --- a/isaac/params.go +++ b/isaac/params.go @@ -19,6 +19,7 @@ var ( DefaultTimeoutRequest = time.Second * 3 DefaultMinWaitNextBlockINITBallot = time.Second * 2 DefaultSyncerLastBlockMapInterval = time.Second * 2 + DefaultMinProposerWait = time.Second * 4 DefaultStateCacheSize = 1 << 12 DefaultOperationPoolCacheSize = 1 << 12 DefaultBroadcastTimerMult = 5 @@ -35,6 +36,7 @@ type Params struct { ballotStuckResolveAfter time.Duration minWaitNextBlockINITBallot time.Duration syncerLastBlockMapInterval time.Duration + minProposerWait time.Duration maxTryHandoverYBrokerSyncData uint64 stateCacheSize int operationPoolCacheSize int @@ -59,6 +61,7 @@ func DefaultParams(networkID base.NetworkID) *Params { ballotStuckWait: time.Second * 33, //nolint:gomnd // waitPreparingINITBallot * 10 ballotStuckResolveAfter: time.Second * 66, //nolint:gomnd // ballotStuckWait * 2 syncerLastBlockMapInterval: DefaultSyncerLastBlockMapInterval, + minProposerWait: DefaultMinProposerWait, maxTryHandoverYBrokerSyncData: 33, //nolint:gomnd //... minWaitNextBlockINITBallot: DefaultMinWaitNextBlockINITBallot, stateCacheSize: DefaultStateCacheSize, @@ -109,6 +112,10 @@ func (p *Params) IsValid(networkID []byte) error { return e.Errorf("wrong duration; invalid syncerLastBlockMapInterval") } + if p.minProposerWait < 0 { + return e.Errorf("wrong duration; invalid minProposerWait") + } + if p.stateCacheSize < 0 { return e.Errorf("wrong state cache size") } @@ -264,6 +271,25 @@ func (p *Params) SetSyncerLastBlockMapInterval(d time.Duration) error { }) } +func (p *Params) MinProposerWait() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.minProposerWait +} + +func (p *Params) SetMinProposerWait(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.minProposerWait == d { + return false, nil + } + + p.minProposerWait = d + + return true, nil + }) +} + func (p *Params) MaxTryHandoverYBrokerSyncData() uint64 { p.RLock() defer p.RUnlock() @@ -359,6 +385,7 @@ type paramsJSONMarshaler struct { BallotStuckResolveAfter util.ReadableDuration `json:"ballot_stuck_resolve_after,omitempty"` MinWaitNextBlockINITBallot util.ReadableDuration `json:"min_wait_next_block_init_ballot,omitempty"` SyncerLastBlockMapInterval util.ReadableDuration `json:"syncer_last_block_map_interval,omitempty"` + MinProposerWait util.ReadableDuration `json:"min_proposer_wait,omitempty"` MaxTryHandoverYBrokerSyncData uint64 `json:"max_try_handover_y_broker_sync_data,omitempty"` StateCacheSize int `json:"state_cache_size,omitempty"` OperationPoolCacheSize int `json:"operation_pool_cache_size,omitempty"` @@ -375,6 +402,7 @@ func (p *Params) MarshalJSON() ([]byte, error) { BallotStuckWait: util.ReadableDuration(p.ballotStuckWait), MinWaitNextBlockINITBallot: util.ReadableDuration(p.minWaitNextBlockINITBallot), SyncerLastBlockMapInterval: util.ReadableDuration(p.syncerLastBlockMapInterval), + MinProposerWait: util.ReadableDuration(p.minProposerWait), MaxTryHandoverYBrokerSyncData: p.maxTryHandoverYBrokerSyncData, StateCacheSize: p.stateCacheSize, OperationPoolCacheSize: p.operationPoolCacheSize, @@ -390,6 +418,7 @@ type paramsJSONUnmarshaler struct { BallotStuckResolveAfter *util.ReadableDuration `json:"ballot_stuck_resolve_after,omitempty"` MinWaitNextBlockINITBallot *util.ReadableDuration `json:"min_wait_next_block_init_ballot,omitempty"` SyncerLastBlockMapInterval *util.ReadableDuration `json:"syncer_last_block_map_interval,omitempty"` + MinProposerWait *util.ReadableDuration `json:"min_proposer_wait,omitempty"` MaxTryHandoverYBrokerSyncData *uint64 `json:"max_try_handover_y_broker_sync_data,omitempty"` StateCacheSize *int `json:"state_cache_size,omitempty"` OperationPoolCacheSize *int `json:"operation_pool_cache_size,omitempty"` @@ -425,6 +454,7 @@ func (p *Params) UnmarshalJSON(b []byte) error { {u.BallotStuckWait, &p.ballotStuckWait}, {u.MinWaitNextBlockINITBallot, &p.minWaitNextBlockINITBallot}, {u.SyncerLastBlockMapInterval, &p.syncerLastBlockMapInterval}, + {u.MinProposerWait, &p.minProposerWait}, } for i := range durargs { diff --git a/isaac/proposal_selector.go b/isaac/proposal_selector.go index 4cc4399c..9711d1e7 100644 --- a/isaac/proposal_selector.go +++ b/isaac/proposal_selector.go @@ -40,8 +40,8 @@ func NewBaseProposalSelectorArgs() *BaseProposalSelectorArgs { RequestFunc: func(context.Context, base.Point, base.Node, util.Hash) (base.ProposalSignFact, bool, error) { return nil, false, util.ErrNotImplemented.Errorf("request") }, - RequestProposalInterval: time.Millisecond * 666, //nolint:gomnd //... - MinProposerWait: DefaultTimeoutRequest + time.Second, //nolint:gomnd //... + RequestProposalInterval: time.Millisecond * 666, //nolint:gomnd //... + MinProposerWait: DefaultMinProposerWait, //nolint:gomnd //... TimeoutRequest: func() time.Duration { return DefaultTimeoutRequest }, diff --git a/launch/p_proposal.go b/launch/p_proposal.go index 89b2f13b..b085e2eb 100644 --- a/launch/p_proposal.go +++ b/launch/p_proposal.go @@ -517,7 +517,7 @@ func newBaseProposalSelectorArgs(pctx context.Context) (*isaac.BaseProposalSelec args.Pool = pool args.ProposerSelectFunc = proposerSelectFunc args.Maker = proposalMaker - args.MinProposerWait = params.Network.TimeoutRequest() + (time.Second * 2) + args.MinProposerWait = params.ISAAC.MinProposerWait() args.TimeoutRequest = params.Network.TimeoutRequest if err := getNodesFuncOfBaseProposalSelectorArgs(pctx, args); err != nil { From 6ebf7d897bda13cf792c0be72cc2ab13e57d687c Mon Sep 17 00:00:00 2001 From: soonkuk Date: Tue, 16 Sep 2025 22:07:22 +0900 Subject: [PATCH 13/19] fix: FindVoteResult logic --- base/vote.go | 100 +++++++++++++++++---------------------------------- 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/base/vote.go b/base/vote.go index e8d5cde4..2d92b095 100644 --- a/base/vote.go +++ b/base/vote.go @@ -1,9 +1,5 @@ package base -import ( - "sort" -) - type VoteResult string const ( @@ -47,86 +43,56 @@ func (v *VoteResult) UnmarshalText(b []byte) error { return nil } -// FindMajority finds the majority(over threshold) set between the given sets. -// The returned value means, -// 0-N: index number of set -// -1: not yet majority -// -2: draw -func FindMajority(quorum, threshold uint, set ...uint) int { +func FindVoteResult(quorum, threshold uint, s []string) (result VoteResult, key string) { th := threshold if th > quorum { th = quorum } - if len(set) < 1 { - return -1 + total := uint(len(s)) + if total == 0 { + return VoteResultNotYet, "" } - var sum uint - - for i := range set { - n := set[i] - - if n >= quorum { - return i - } - - if n >= th { - return i - } - - sum += n + var remain uint + if total < quorum { + remain = quorum - total } - sort.Slice(set, func(i, j int) bool { - return set[i] > set[j] - }) - - if quorum-sum+set[0] < th { - return -2 + count := make(map[string]uint, len(s)) + for _, v := range s { + count[v]++ } - return -1 -} - -func FindVoteResult(quorum, threshold uint, s []string) (result VoteResult, key string) { - th := threshold - if th > quorum { - th = quorum - } - - if len(s) < 1 { - return VoteResultNotYet, "" + var ( + maxCount uint + topTies uint + mhs string + ) + for hs, cn := range count { + if cn > maxCount { + maxCount = cn + mhs = hs + topTies = 1 + } else if cn == maxCount { + topTies++ + if hs < mhs { + mhs = hs + } + } } - keys := map[uint]string{} - defer clear(keys) - - count := map[string]uint{} - defer clear(count) - - for i := range s { - count[s[i]]++ + if maxCount >= th { + return VoteResultMajority, mhs } - set := make([]uint, len(count)) - var i int - - for j := range count { - c := count[j] - keys[c] = j - set[i] = c - i++ + if remain == 0 { + return VoteResultDraw, "" } - sort.Slice(set, func(i, j int) bool { return set[i] > set[j] }) - - switch index := FindMajority(quorum, th, set...); index { - case -1: - return VoteResultNotYet, "" - case -2: + if maxCount+remain < th { return VoteResultDraw, "" - default: - return VoteResultMajority, keys[set[index]] } + + return VoteResultNotYet, "" } From dd48d13009f07ffda5c0cdc705d20f17b05b9a25 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Tue, 16 Sep 2025 10:15:31 +0900 Subject: [PATCH 14/19] refact: allow RequestProposal handler to use ProposalPool anytime --- isaac/network/quicstream_handlers.go | 29 +++++++++++++--------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/isaac/network/quicstream_handlers.go b/isaac/network/quicstream_handlers.go index 7ba52bcf..a1d1a812 100644 --- a/isaac/network/quicstream_handlers.go +++ b/isaac/network/quicstream_handlers.go @@ -26,8 +26,8 @@ var ContextKeyNodeChallengedNode = util.ContextKey("node-challenge-node") func QuicstreamHandlerOperation( oppool isaac.NewOperationPool, getFromHandoverX func(context.Context, OperationRequestHeader) ( - enchint string, body []byte, found bool, _ error, - ), + enchint string, body []byte, found bool, _ error, +), ) quicstreamheader.Handler[OperationRequestHeader] { if getFromHandoverX == nil { getFromHandoverX = func(context.Context, OperationRequestHeader) ( //revive:disable-line:modifies-parameter @@ -147,9 +147,6 @@ func QuicstreamHandlerRequestProposal( base.ProposalSignFact, error, ) { proposer := header.Proposer() - if !proposer.Equal(local) { - return nil, nil - } point := header.point previousBlock := header.PreviousBlock() @@ -294,10 +291,10 @@ func QuicstreamHandlerBlockMap( func QuicstreamHandlerBlockItem( blockItemf func( - base.Height, - base.BlockItemType, - func(_ io.Reader, found bool, uri url.URL, compressFormat string) error, - ) error, + base.Height, + base.BlockItemType, + func(_ io.Reader, found bool, uri url.URL, compressFormat string) error, +) error, ) quicstreamheader.Handler[BlockItemRequestHeader] { return func(ctx context.Context, _ net.Addr, broker *quicstreamheader.HandlerBroker, header BlockItemRequestHeader, @@ -338,9 +335,9 @@ func QuicstreamHandlerBlockItem( func QuicstreamHandlerBlockItemFiles( blockItemFilesf func( - base.Height, - func(_ io.Reader, found bool) error, - ) error, + base.Height, + func(_ io.Reader, found bool) error, +) error, ) quicstreamheader.Handler[BlockItemFilesRequestHeader] { return func(ctx context.Context, _ net.Addr, broker *quicstreamheader.HandlerBroker, header BlockItemFilesRequestHeader, @@ -634,10 +631,10 @@ func QuicstreamHandlerStreamOperations( networkID base.NetworkID, limit uint64, traverse func( - _ context.Context, - offset []byte, - callback func(enchint string, meta isaacdatabase.FrameHeaderPoolOperation, body, offset []byte) (bool, error), - ) error, + _ context.Context, + offset []byte, + callback func(enchint string, meta isaacdatabase.FrameHeaderPoolOperation, body, offset []byte) (bool, error), +) error, ) quicstreamheader.Handler[StreamOperationsHeader] { return func(ctx context.Context, addr net.Addr, broker *quicstreamheader.HandlerBroker, header StreamOperationsHeader, From 0662720509c91a272652dcf664cf5ebfc4a9df4d Mon Sep 17 00:00:00 2001 From: soonkuk Date: Thu, 23 Oct 2025 12:15:02 +0900 Subject: [PATCH 15/19] feat: update quic-go from 0.39.1 to 0.54.1 --- network/quicstream/connection.go | 10 +++++----- network/quicstream/server.go | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/network/quicstream/connection.go b/network/quicstream/connection.go index 22661814..d1892ecf 100644 --- a/network/quicstream/connection.go +++ b/network/quicstream/connection.go @@ -16,17 +16,17 @@ import ( var ErrOpenStream = util.NewIDError("open stream") type Connection struct { - conn quic.EarlyConnection + conn *quic.Conn id string } func Dial( ctx context.Context, addr *net.UDPAddr, - tlsconfig *tls.Config, - quicconfig *quic.Config, + tlsConfig *tls.Config, + quicConfig *quic.Config, ) (*Connection, error) { - conn, err := quic.DialAddrEarly(ctx, addr.String(), tlsconfig, quicconfig) + conn, err := quic.DialAddrEarly(ctx, addr.String(), tlsConfig, quicConfig) if err != nil { return nil, &net.OpError{Net: "udp", Op: "dial", Err: err} } @@ -95,7 +95,7 @@ func (c *Connection) ID() string { return c.id } -func (c *Connection) openStream(ctx context.Context) (stream quic.Stream, _ error) { +func (c *Connection) openStream(ctx context.Context) (stream *quic.Stream, _ error) { if c.conn.Context().Err() != nil { return nil, errors.Wrap(c.conn.Context().Err(), "closed") } diff --git a/network/quicstream/server.go b/network/quicstream/server.go index 819375d7..ff1e965b 100644 --- a/network/quicstream/server.go +++ b/network/quicstream/server.go @@ -101,7 +101,7 @@ func (srv *Server) accept(ctx context.Context, listener *quic.EarlyListener) { } } -func (srv *Server) handleConnection(ctx context.Context, conn quic.EarlyConnection) { +func (srv *Server) handleConnection(ctx context.Context, conn *quic.Conn) { for { stream, err := conn.AcceptStream(ctx) if err != nil { @@ -155,7 +155,7 @@ func (srv *Server) handleConnection(ctx context.Context, conn quic.EarlyConnecti } } -func (srv *Server) handleStream(ctx context.Context, remoteAddr net.Addr, stream quic.Stream) { +func (srv *Server) handleStream(ctx context.Context, remoteAddr net.Addr, stream *quic.Stream) { sctx, cancel := srv.streamTimeoutContext(ctx) defer cancel() From 5535dc14779d9cb821d04a35abddf1df2a7461a8 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Thu, 23 Oct 2025 15:06:26 +0900 Subject: [PATCH 16/19] feat: add more parameters to node write and fix write design --- go.mod | 6 +- go.sum | 24 +- isaac/network/node_info.go | 75 ++- isaac/network/node_info_json.go | 104 +++- isaac/network/node_info_test.go | 4 +- isaac/network/sync.go | 4 +- isaac/network/sync_yaml.go | 44 +- isaac/params.go | 329 ++++++++++++ isaac/readers.go | 4 +- isaac/states/ballot_stuck_resolver.go | 17 +- isaac/states/broadcast.go | 22 +- isaac/states/states.go | 4 +- launch/design.go | 56 +- launch/design_test.go | 67 --- launch/event.go | 2 +- launch/flags.go | 8 +- launch/handover.go | 6 +- launch/hinters.go | 2 + launch/local_params.go | 455 +--------------- launch/local_params_marshal.go | 260 +--------- launch/p_acl.go | 2 +- launch/p_design.go | 18 +- launch/p_local.go | 9 +- launch/p_memberlist.go | 26 +- launch/p_network.go | 10 +- launch/p_network_handlers.go | 8 +- launch/p_node_rw.go | 714 +++++++++++++++++++++++--- launch/p_nodeinfo.go | 25 +- launch/p_proposal.go | 6 +- launch/p_states.go | 6 +- launch/p_storage.go | 24 +- launch/p_sync.go | 8 +- launch/ratelimit.go | 14 + network/conn.go | 33 -- network/quicmemberlist/conn.go | 8 +- network/quicmemberlist/delegate.go | 15 +- network/quicmemberlist/member.go | 4 +- network/quicmemberlist/memberlist.go | 6 +- network/quicmemberlist/params.go | 392 ++++++++++++++ network/quicstream/conn.go | 32 +- network/util.go | 24 - network/util/util.go | 57 ++ util/yaml.go | 5 +- 43 files changed, 1804 insertions(+), 1135 deletions(-) create mode 100644 network/quicmemberlist/params.go delete mode 100644 network/util.go create mode 100644 network/util/util.go diff --git a/go.mod b/go.mod index 83350b0c..1232fb0f 100644 --- a/go.mod +++ b/go.mod @@ -23,7 +23,7 @@ require ( github.com/mattn/go-isatty v0.0.20 github.com/oklog/ulid/v2 v2.1.1 github.com/pkg/errors v0.9.1 - github.com/quic-go/quic-go v0.39.1 + github.com/quic-go/quic-go v0.54.1 github.com/redis/go-redis/v9 v9.13.0 github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.11.1 @@ -51,10 +51,8 @@ require ( github.com/fatih/color v1.16.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect @@ -79,10 +77,8 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/onsi/ginkgo v1.16.5 // indirect - github.com/onsi/ginkgo/v2 v2.13.0 // indirect github.com/onsi/gomega v1.30.0 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/quic-go/qtls-go1-20 v0.3.4 // indirect github.com/ryanuber/go-glob v1.0.0 // indirect github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect diff --git a/go.sum b/go.sum index fc78a7cf..ad2a949d 100644 --- a/go.sum +++ b/go.sum @@ -59,9 +59,6 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= @@ -93,12 +90,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw= github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -114,8 +107,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -127,8 +118,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= @@ -186,7 +175,6 @@ github.com/hashicorp/vault/api v1.20.0/go.mod h1:GZ4pcjfzoOWpkJ3ijHNpEoAxKEsBJnV github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= @@ -250,8 +238,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= @@ -281,10 +267,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/quic-go/qtls-go1-20 v0.3.4 h1:MfFAPULvst4yoMgY9QmtpYmfij/em7O8UUi+bNVm7Cg= -github.com/quic-go/qtls-go1-20 v0.3.4/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k= -github.com/quic-go/quic-go v0.39.1 h1:d/m3oaN/SD2c+f7/yEjZxe2zEVotXprnrCCJ2y/ZZFE= -github.com/quic-go/quic-go v0.39.1/go.mod h1:T09QsDQWjLiQ74ZmacDfqZmhY/NLnw5BC40MANNNZ1Q= +github.com/quic-go/quic-go v0.54.1 h1:4ZAWm0AhCb6+hE+l5Q1NAL0iRn/ZrMwqHRGQiFwj2eg= +github.com/quic-go/quic-go v0.54.1/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= github.com/redis/go-redis/v9 v9.13.0 h1:PpmlVykE0ODh8P43U0HqC+2NXHXwG+GUtQyz+MPKGRg= github.com/redis/go-redis/v9 v9.13.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -311,7 +295,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -388,7 +371,6 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -435,8 +417,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= diff --git a/isaac/network/node_info.go b/isaac/network/node_info.go index 0a075cd1..9cbfbf72 100644 --- a/isaac/network/node_info.go +++ b/isaac/network/node_info.go @@ -7,6 +7,7 @@ import ( "github.com/ProtoconNet/mitum2/base" "github.com/ProtoconNet/mitum2/isaac" isaacstates "github.com/ProtoconNet/mitum2/isaac/states" + "github.com/ProtoconNet/mitum2/network/quicmemberlist" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/hint" "github.com/ProtoconNet/mitum2/util/localtime" @@ -16,16 +17,18 @@ import ( var NodeInfoHint = hint.MustNewHint("node-info-v0.0.1") type NodeInfo struct { - lastVote NodeInfoLastVote - networkID base.NetworkID - address base.Address - publickey base.Publickey - lastManifest base.Manifest - networkPolicy base.NetworkPolicy - localParams *isaac.Params - connInfo string - consensusState isaacstates.StateType - consensusNodes []base.Node + lastVote NodeInfoLastVote + networkID base.NetworkID + address base.Address + publickey base.Publickey + lastManifest base.Manifest + networkPolicy base.NetworkPolicy + isaacParams *isaac.Params + memberlistParams *quicmemberlist.MemberlistParams + miscParams *isaac.MISCParams + connInfo string + consensusState isaacstates.StateType + consensusNodes []base.Node hint.BaseHinter startedAt time.Time version util.Version @@ -40,7 +43,9 @@ func (info NodeInfo) IsValid(networkID base.NetworkID) error { info.lastManifest, info.suffrageHeight, info.networkPolicy, - info.localParams, + info.isaacParams, + info.memberlistParams, + info.miscParams, util.DummyIsValider(func([]byte) error { if len(info.connInfo) < 1 { return errors.Errorf("empty conn info") @@ -87,8 +92,16 @@ func (info NodeInfo) NetworkPolicy() base.NetworkPolicy { return info.networkPolicy } -func (info NodeInfo) LocalParams() *isaac.Params { - return info.localParams +func (info NodeInfo) IsaacParams() *isaac.Params { + return info.isaacParams +} + +func (info NodeInfo) MemberlistParams() *quicmemberlist.MemberlistParams { + return info.memberlistParams +} + +func (info NodeInfo) MISCParams() *isaac.MISCParams { + return info.miscParams } func (info NodeInfo) ConnInfo() string { @@ -200,21 +213,49 @@ func (info *NodeInfoUpdater) SetNetworkPolicy(p base.NetworkPolicy) bool { }) } -func (info *NodeInfoUpdater) SetLocalParams(p *isaac.Params) bool { +func (info *NodeInfoUpdater) SetIsaacParams(p *isaac.Params) bool { return info.set(func() bool { switch { - case info.n.localParams == nil, p == nil: - case info.n.localParams.ID() == p.ID(): + case info.n.isaacParams == nil, p == nil: + case info.n.isaacParams.ID() == p.ID(): return false } - info.n.localParams = p + info.n.isaacParams = p info.n.networkID = p.NetworkID() return true }) } +func (info *NodeInfoUpdater) SetMemberlistParams(p *quicmemberlist.MemberlistParams) bool { + return info.set(func() bool { + switch { + case info.n.memberlistParams == nil, p == nil: + case info.n.memberlistParams.ID() == p.ID(): + return false + } + + info.n.memberlistParams = p + + return true + }) +} + +func (info *NodeInfoUpdater) SetMISCParams(p *isaac.MISCParams) bool { + return info.set(func() bool { + switch { + case info.n.miscParams == nil, p == nil: + case info.n.miscParams.ID() == p.ID(): + return false + } + + info.n.miscParams = p + + return true + }) +} + func (info *NodeInfoUpdater) SetConnInfo(c string) bool { return info.set(func() bool { if info.n.connInfo == c { diff --git a/isaac/network/node_info_json.go b/isaac/network/node_info_json.go index 0c460efb..dda8c853 100644 --- a/isaac/network/node_info_json.go +++ b/isaac/network/node_info_json.go @@ -2,24 +2,28 @@ package isaacnetwork import ( "encoding/json" + "reflect" + "github.com/ProtoconNet/mitum2/base" "github.com/ProtoconNet/mitum2/isaac" isaacstates "github.com/ProtoconNet/mitum2/isaac/states" + "github.com/ProtoconNet/mitum2/network/quicmemberlist" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/ProtoconNet/mitum2/util/hint" "github.com/ProtoconNet/mitum2/util/localtime" "github.com/pkg/errors" - "reflect" ) type NodeInfoLocalJSONMarshaler struct { - Address base.Address `json:"address"` - Publickey base.Publickey `json:"publickey"` - LocalParams *isaac.Params `json:"parameters"` //nolint:tagliatelle //... - ConnInfo string `json:"conn_info"` - StartedAt localtime.Time `json:"started_at"` - Version util.Version `json:"version"` + Address base.Address `json:"address"` + Publickey base.Publickey `json:"publickey"` + IsaacParams *isaac.Params `json:"isaac_parameters"` //nolint:tagliatelle //... + MemberlistParams *quicmemberlist.MemberlistParams `json:"memberlist_parameters"` + MISCParams *isaac.MISCParams `json:"misc_parameters"` + ConnInfo string `json:"conn_info"` + StartedAt localtime.Time `json:"started_at"` + Version util.Version `json:"version"` } type NodeInfoSuffrageJSONMarshaler struct { @@ -47,12 +51,14 @@ func (info NodeInfo) JSONMarshaler() NodeInfoJSONMarshaler { BaseHinter: info.BaseHinter, NetworkID: info.networkID, Local: NodeInfoLocalJSONMarshaler{ - Address: info.address, - Publickey: info.publickey, - LocalParams: info.localParams, - ConnInfo: info.connInfo, - Version: info.version, - StartedAt: localtime.New(info.startedAt), + Address: info.address, + Publickey: info.publickey, + IsaacParams: info.isaacParams, + MemberlistParams: info.memberlistParams, + MISCParams: info.miscParams, + ConnInfo: info.connInfo, + Version: info.version, + StartedAt: localtime.New(info.startedAt), }, Consensus: NodeInfoConsensusJSONMarshaler{ State: info.consensusState, @@ -80,12 +86,14 @@ type nodeInfoJSONUnmarshaler struct { } type nodeInfoLocalJSONUnmarshaler struct { - Address string `json:"address"` - Publickey string `json:"publickey"` - ConnInfo string `json:"conn_info"` - StartedAt localtime.Time `json:"started_at"` - LocalParams json.RawMessage `json:"parameters"` //nolint:tagliatelle //... - Version util.Version `json:"version"` + Address string `json:"address"` + Publickey string `json:"publickey"` + ConnInfo string `json:"conn_info"` + StartedAt localtime.Time `json:"started_at"` + IsaacParams json.RawMessage `json:"isaac_parameters"` //nolint:tagliatelle //... + MemberlistParams json.RawMessage `json:"memberlist_parameters"` + MISCParams json.RawMessage `json:"misc_parameters"` + Version util.Version `json:"version"` } type nodeInfoConsensusJSONUnmarshaler struct { @@ -126,30 +134,70 @@ func (info *NodeInfo) DecodeJSON(b []byte, enc encoder.Encoder) error { info.publickey = i } - //params := isaac.NewParams(info.networkID) - var params *isaac.Params + //isaacParams := isaac.NewParams(info.networkID) + var isaacParams *isaac.Params - hinter, err := enc.Decode(u.Local.LocalParams) + ipHinter, err := enc.Decode(u.Local.IsaacParams) if err != nil { return e.Wrap(err) } - if hinter == nil { + if ipHinter == nil { return nil } - i, ok := hinter.(*isaac.Params) + i, ok := ipHinter.(*isaac.Params) if !ok { - return errors.Errorf("expected %v, but %T", reflect.TypeOf(params).Elem(), hinter) + return errors.Errorf("expected %v, but %T", reflect.TypeOf(isaacParams).Elem(), ipHinter) } - params = i + isaacParams = i - if err := params.SetNetworkID(info.networkID); err != nil { + if err := isaacParams.SetNetworkID(info.networkID); err != nil { return e.Wrap(err) } - info.localParams = params + info.isaacParams = isaacParams + + var memberlistParams *quicmemberlist.MemberlistParams + + mpHinter, err := enc.Decode(u.Local.MemberlistParams) + if err != nil { + return e.Wrap(err) + } + + if mpHinter == nil { + return nil + } + + j, ok := mpHinter.(*quicmemberlist.MemberlistParams) + if !ok { + return errors.Errorf("expected %v, but %T", reflect.TypeOf(memberlistParams).Elem(), mpHinter) + } + + memberlistParams = j + + info.memberlistParams = memberlistParams + + var miscParams *isaac.MISCParams + + mscHinter, err := enc.Decode(u.Local.MISCParams) + if err != nil { + return e.Wrap(err) + } + + if mscHinter == nil { + return nil + } + + k, ok := mscHinter.(*isaac.MISCParams) + if !ok { + return errors.Errorf("expected %v, but %T", reflect.TypeOf(miscParams).Elem(), mscHinter) + } + + miscParams = k + + info.miscParams = miscParams info.connInfo = u.Local.ConnInfo info.version = u.Local.Version diff --git a/isaac/network/node_info_test.go b/isaac/network/node_info_test.go index e81518d6..4eff5acf 100644 --- a/isaac/network/node_info_test.go +++ b/isaac/network/node_info_test.go @@ -51,7 +51,7 @@ func TestNodeInfoEncode(tt *testing.T) { info.SetLastManifest(base.NewDummyManifest(base.Height(33), valuehash.RandomSHA256())) info.SetSuffrageHeight(base.Height(44)) info.SetNetworkPolicy(isaac.DefaultNetworkPolicy()) - info.SetLocalParams(isaac.DefaultParams(networkID)) + info.SetIsaacParams(isaac.DefaultParams(networkID)) ci, err := quicstream.NewConnInfoFromStringAddr("1.2.3.4:4321", true) t.NoError(err) @@ -98,7 +98,7 @@ func TestNodeInfoEncode(tt *testing.T) { base.EqualManifest(t.Assert(), ah.lastManifest, bh.lastManifest) t.Equal(ah.suffrageHeight, bh.suffrageHeight) base.EqualNetworkPolicy(t.Assert(), ah.networkPolicy, bh.networkPolicy) - isaac.EqualLocalParams(t.Assert(), ah.localParams, bh.localParams) + isaac.EqualLocalParams(t.Assert(), ah.isaacParams, bh.isaacParams) t.Equal(ah.connInfo, bh.connInfo) t.Equal(len(ah.consensusNodes), len(bh.consensusNodes)) for i := range ah.consensusNodes { diff --git a/isaac/network/sync.go b/isaac/network/sync.go index 9e8b24ad..1113a3bd 100644 --- a/isaac/network/sync.go +++ b/isaac/network/sync.go @@ -12,9 +12,9 @@ import ( "github.com/ProtoconNet/mitum2/base" "github.com/ProtoconNet/mitum2/isaac" - "github.com/ProtoconNet/mitum2/network" "github.com/ProtoconNet/mitum2/network/quicmemberlist" "github.com/ProtoconNet/mitum2/network/quicstream" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/ProtoconNet/mitum2/util/logging" @@ -362,7 +362,7 @@ func (c *SyncSourceChecker) fetchFromURL(ctx context.Context, u *url.URL) ([]isa httpclient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ - InsecureSkipVerify: network.HasTLSInsecure(u.Fragment, network.DefaultTLSInsecureFlag), + InsecureSkipVerify: nutil.HasTLSInsecure(u.Fragment, nutil.DefaultTLSInsecureFlag), }, }, } diff --git a/isaac/network/sync_yaml.go b/isaac/network/sync_yaml.go index ce3c9131..c33a967e 100644 --- a/isaac/network/sync_yaml.go +++ b/isaac/network/sync_yaml.go @@ -5,14 +5,48 @@ import ( "github.com/ProtoconNet/mitum2/base" "github.com/ProtoconNet/mitum2/isaac" - "github.com/ProtoconNet/mitum2/network" "github.com/ProtoconNet/mitum2/network/quicmemberlist" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/pkg/errors" "gopkg.in/yaml.v3" ) +type syncSourceNodeMarshaler struct { + Type string `yaml:"type"` + Address string `yaml:"address"` + PublicKey string `yaml:"publickey"` + Publish string `yaml:"publish"` + TLSInsecure bool `yaml:"tls_insecure"` +} + +type syncSourceConnInfoMarshaler struct { + Type string `yaml:"type"` + Publish string `yaml:"publish"` + TLSInsecure bool `yaml:"tls_insecure"` +} + +func (p *SyncSource) MarshalYAML() (interface{}, error) { + switch t := p.Source.(type) { + case isaac.NodeConnInfo: + return syncSourceNodeMarshaler{ + Type: string(p.Type), + Address: t.Address().String(), + PublicKey: t.Publickey().String(), + Publish: t.ConnInfo().UDPAddr().String(), + TLSInsecure: t.TLSInsecure(), + }, nil + case quicmemberlist.NamedConnInfo: + return syncSourceConnInfoMarshaler{ + Type: string(p.Type), + Publish: t.ConnInfo().UDPAddr().String(), + TLSInsecure: t.TLSInsecure(), + }, nil + } + return nil, errors.Errorf("Unknown type: %s", p.Type) +} + func (d *SyncSource) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { e := util.StringError("decode SyncSource") @@ -107,8 +141,8 @@ func (d *SyncSource) decodeYAMLMap(t string, b []byte, jsonencoder encoder.Encod } type syncSourceNodeUnmarshaler struct { - Address string - Publickey string + Address string `yaml:"address"` + Publickey string `yaml:"publickey"` Publish string `yaml:"publish"` TLSInsecure bool `yaml:"tls_insecure"` } @@ -132,7 +166,7 @@ func (SyncSource) decodeYAMLNodeConnInfo(b []byte, jsonencoder encoder.Encoder) return nil, e.Wrap(err) } - if err := network.IsValidAddr(u.Publish); err != nil { + if err := nutil.IsValidAddr(u.Publish); err != nil { return nil, e.Wrap(err) } @@ -155,7 +189,7 @@ func (SyncSource) decodeYAMLConnInfo( return ci, e.Wrap(err) } - if err := network.IsValidAddr(u.Publish); err != nil { + if err := nutil.IsValidAddr(u.Publish); err != nil { return ci, e.Wrap(err) } diff --git a/isaac/params.go b/isaac/params.go index 0d9a5a3b..3d1afeb4 100644 --- a/isaac/params.go +++ b/isaac/params.go @@ -8,6 +8,7 @@ import ( "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/hint" "github.com/pkg/errors" + "gopkg.in/yaml.v3" ) var ParamsHint = hint.MustNewHint("isaac-params-v0.0.1") @@ -483,3 +484,331 @@ func (p *Params) UnmarshalJSON(b []byte) error { return nil } + +var MiscParamsHint = hint.MustNewHint("misc-params-v0.0.1") + +type MISCParams struct { + *util.BaseParams + hint.BaseHinter + syncSourceCheckerInterval time.Duration + validProposalOperationExpire time.Duration + validProposalSuffrageOperationsExpire time.Duration + blockItemReadersRemoveEmptyAfter time.Duration + blockItemReadersRemoveEmptyInterval time.Duration + maxMessageSize uint64 + objectCacheSize uint64 +} + +func DefaultMISCParams() *MISCParams { + return &MISCParams{ + BaseParams: util.NewBaseParams(), + BaseHinter: hint.NewBaseHinter(MiscParamsHint), + syncSourceCheckerInterval: time.Second * 30, //nolint:gomnd //... + validProposalOperationExpire: time.Hour * 24, //nolint:gomnd //... + validProposalSuffrageOperationsExpire: time.Hour * 2, + blockItemReadersRemoveEmptyAfter: DefaultBlockItemReadersRemoveEmptyAfter, + blockItemReadersRemoveEmptyInterval: DefaultBlockItemReadersRemoveEmptyInterval, + maxMessageSize: 1 << 18, //nolint:gomnd //... + objectCacheSize: 1 << 13, //nolint:gomnd // big enough + } +} + +func (p *MISCParams) IsValid([]byte) error { + e := util.ErrInvalid.Errorf("invalid MISCParams") + + if err := p.BaseParams.IsValid(nil); err != nil { + return e.Wrap(err) + } + + if p.syncSourceCheckerInterval < 0 { + return e.Errorf("wrong duration; invalid syncSourceCheckerInterval") + } + + if p.validProposalOperationExpire < 0 { + return e.Errorf("wrong duration; invalid validProposalOperationExpire") + } + + if p.validProposalSuffrageOperationsExpire < 0 { + return e.Errorf("wrong duration; invalid validProposalSuffrageOperationsExpire") + } + + if p.blockItemReadersRemoveEmptyAfter < 0 { + return e.Errorf("wrong duration; invalid blockItemReadersRemoveEmptyAfter") + } + + if p.blockItemReadersRemoveEmptyInterval < 0 { + return e.Errorf("wrong duration; invalid blockItemReadersRemoveEmptyInterval") + } + + if p.maxMessageSize < 1 { + return e.Errorf("wrong maxMessageSize") + } + + if p.objectCacheSize < 1 { + return e.Errorf("wrong objectCacheSize") + } + + return nil +} + +// SyncSourceCheckerInterval is the interval to check the liveness of sync +// sources. +func (p *MISCParams) SyncSourceCheckerInterval() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.syncSourceCheckerInterval +} + +func (p *MISCParams) SetSyncSourceCheckerInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.syncSourceCheckerInterval == d { + return false, nil + } + + p.syncSourceCheckerInterval = d + + return true, nil + }) +} + +// ValidProposalOperationExpire is the maximum creation time for valid +// operation. If the creation time of operation is older than +// ValidProposalOperationExpire, it will be ignored. +func (p *MISCParams) ValidProposalOperationExpire() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.validProposalOperationExpire +} + +func (p *MISCParams) SetValidProposalOperationExpire(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.validProposalOperationExpire == d { + return false, nil + } + + p.validProposalOperationExpire = d + + return true, nil + }) +} + +// ValidProposalSuffrageOperationsExpire is the maximum creation time for valid +// suffrage operations like isaacoperation.SuffrageCandidate operation. If the +// creation time of suffrage operation is older than +// ValidProposalSuffrageOperationsExpire, it will be ignored. +func (p *MISCParams) ValidProposalSuffrageOperationsExpire() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.validProposalSuffrageOperationsExpire +} + +func (p *MISCParams) SetValidProposalSuffrageOperationsExpire(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.validProposalSuffrageOperationsExpire == d { + return false, nil + } + + p.validProposalSuffrageOperationsExpire = d + + return true, nil + }) +} + +// BlockItemReadersRemoveEmptyAfter removes empty block item directory after +// the duration. Zero duration not allowed. +func (p *MISCParams) BlockItemReadersRemoveEmptyAfter() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.blockItemReadersRemoveEmptyAfter +} + +func (p *MISCParams) SetBlockItemReadersRemoveEmptyAfter(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.blockItemReadersRemoveEmptyAfter == d { + return false, nil + } + + p.blockItemReadersRemoveEmptyAfter = d + + return true, nil + }) +} + +// BlockItemReadersRemoveEmptyInterval is the interval to remove empty block +// item directory after BlockItemReadersRemoveEmptyAfter. Zero duration not +// allowed. +func (p *MISCParams) BlockItemReadersRemoveEmptyInterval() time.Duration { + p.RLock() + defer p.RUnlock() + + return p.blockItemReadersRemoveEmptyInterval +} + +func (p *MISCParams) SetBlockItemReadersRemoveEmptyInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.blockItemReadersRemoveEmptyInterval == d { + return false, nil + } + + p.blockItemReadersRemoveEmptyInterval = d + + return true, nil + }) +} + +// MaxMessageSize is the maximum size of incoming messages like ballot or +// operation. If message size is over, it will be ignored. +func (p *MISCParams) MaxMessageSize() uint64 { + p.RLock() + defer p.RUnlock() + + return p.maxMessageSize +} + +func (p *MISCParams) SetMaxMessageSize(d uint64) error { + return p.SetUint64(d, func(d uint64) (bool, error) { + if p.maxMessageSize == d { + return false, nil + } + + p.maxMessageSize = d + + return true, nil + }) +} + +// ObjectCacheSize is the cache size for various internal objects like address +// or keypair. +func (p *MISCParams) ObjectCacheSize() uint64 { + p.RLock() + defer p.RUnlock() + + return p.objectCacheSize +} + +func (p *MISCParams) SetObjectCacheSize(d uint64) error { + return p.SetUint64(d, func(d uint64) (bool, error) { + if p.objectCacheSize == d { + return false, nil + } + + p.objectCacheSize = d + + return true, nil + }) +} + +type miscParamsYAMLMarshaler struct { + //revive:disable:line-length-limit + hint.BaseHinter + SyncSourceCheckerInterval util.ReadableDuration `json:"sync_source_checker_interval,omitempty" yaml:"sync_source_checker_interval,omitempty"` + ValidProposalOperationExpire util.ReadableDuration `json:"valid_proposal_operation_expire,omitempty" yaml:"valid_proposal_operation_expire,omitempty"` + ValidProposalSuffrageOperationsExpire util.ReadableDuration `json:"valid_proposal_suffrage_operations_expire,omitempty" yaml:"valid_proposal_suffrage_operations_expire,omitempty"` + BlockItemReadersRemoveEmptyAfter util.ReadableDuration `json:"block_item_readers_remove_empty_after,omitempty" yaml:"block_item_readers_remove_empty_after,omitempty"` + BlockItemReadersRemoveEmptyInterval util.ReadableDuration `json:"block_item_readers_remove_empty_interval,omitempty" yaml:"block_item_readers_remove_empty_interval,omitempty"` + MaxMessageSize uint64 `json:"max_message_size,omitempty" yaml:"max_message_size,omitempty"` + ObjectCacheSize uint64 `json:"object_cache_size,omitempty" yaml:"object_cache_size,omitempty"` + //revive:enable:line-length-limit +} + +func (p *MISCParams) marshaler() miscParamsYAMLMarshaler { + return miscParamsYAMLMarshaler{ + BaseHinter: p.BaseHinter, + SyncSourceCheckerInterval: util.ReadableDuration(p.syncSourceCheckerInterval), + ValidProposalOperationExpire: util.ReadableDuration(p.validProposalOperationExpire), + ValidProposalSuffrageOperationsExpire: util.ReadableDuration(p.validProposalSuffrageOperationsExpire), + BlockItemReadersRemoveEmptyAfter: util.ReadableDuration(p.blockItemReadersRemoveEmptyAfter), + BlockItemReadersRemoveEmptyInterval: util.ReadableDuration(p.blockItemReadersRemoveEmptyInterval), + MaxMessageSize: p.maxMessageSize, + ObjectCacheSize: p.objectCacheSize, + } +} + +func (p *MISCParams) MarshalJSON() ([]byte, error) { + return util.MarshalJSON(p.marshaler()) +} + +func (p *MISCParams) MarshalYAML() (interface{}, error) { + return p.marshaler(), nil +} + +type miscParamsYAMLUnmarshaler struct { + //revive:disable:line-length-limit + SyncSourceCheckerInterval *util.ReadableDuration `json:"sync_source_checker_interval,omitempty" yaml:"sync_source_checker_interval,omitempty"` + ValidProposalOperationExpire *util.ReadableDuration `json:"valid_proposal_operation_expire,omitempty" yaml:"valid_proposal_operation_expire,omitempty"` + ValidProposalSuffrageOperationsExpire *util.ReadableDuration `json:"valid_proposal_suffrage_operations_expire,omitempty" yaml:"valid_proposal_suffrage_operations_expire,omitempty"` + BlockItemReadersRemoveEmptyAfter *util.ReadableDuration `json:"block_item_readers_remove_empty_after,omitempty" yaml:"block_item_readers_remove_empty_after,omitempty"` + BlockItemReadersRemoveEmptyInterval *util.ReadableDuration `json:"block_item_readers_remove_empty_interval,omitempty" yaml:"block_item_readers_remove_empty_interval,omitempty"` + MaxMessageSize *uint64 `json:"max_message_size,omitempty" yaml:"max_message_size,omitempty"` + ObjectCacheSize *uint64 `json:"object_cache_size,omitempty" yaml:"object_cache_size,omitempty"` + hint.BaseHinter + //revive:enable:line-length-limit +} + +func (p *MISCParams) UnmarshalJSON(b []byte) error { + d := DefaultMISCParams() + *p = *d + + e := util.StringError("decode MISCParams") + + var u miscParamsYAMLUnmarshaler + + if err := util.UnmarshalJSON(b, &u); err != nil { + return e.Wrap(err) + } + p.BaseHinter = u.BaseHinter + + return e.Wrap(p.unmarshal(u)) +} + +func (p *MISCParams) UnmarshalYAML(y *yaml.Node) error { + d := DefaultMISCParams() + *p = *d + + e := util.StringError("decode MISCParams") + + var u miscParamsYAMLUnmarshaler + + if err := y.Decode(&u); err != nil { + return e.Wrap(err) + } + + return e.Wrap(p.unmarshal(u)) +} + +func (p *MISCParams) unmarshal(u miscParamsYAMLUnmarshaler) error { + if u.MaxMessageSize != nil { + p.maxMessageSize = *u.MaxMessageSize + } + + if u.ObjectCacheSize != nil { + p.objectCacheSize = *u.ObjectCacheSize + } + + durargs := [][2]interface{}{ + {u.SyncSourceCheckerInterval, &p.syncSourceCheckerInterval}, + {u.ValidProposalOperationExpire, &p.validProposalOperationExpire}, + {u.ValidProposalSuffrageOperationsExpire, &p.validProposalSuffrageOperationsExpire}, + {u.BlockItemReadersRemoveEmptyAfter, &p.blockItemReadersRemoveEmptyAfter}, + {u.BlockItemReadersRemoveEmptyInterval, &p.blockItemReadersRemoveEmptyInterval}, + } + + for i := range durargs { + v := durargs[i][0].(*util.ReadableDuration) //nolint:forcetypeassert //... + t := durargs[i][1].(*time.Duration) //nolint:forcetypeassert //... + + if reflect.ValueOf(v).IsZero() { + continue + } + + if err := util.SetInterfaceValue(time.Duration(*v), t); err != nil { + return err + } + } + + return nil +} diff --git a/isaac/readers.go b/isaac/readers.go index 91dea761..4e7341bb 100644 --- a/isaac/readers.go +++ b/isaac/readers.go @@ -16,7 +16,7 @@ import ( "time" "github.com/ProtoconNet/mitum2/base" - "github.com/ProtoconNet/mitum2/network" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/ProtoconNet/mitum2/util/hint" @@ -1116,7 +1116,7 @@ func HTTPBlockItemReadFunc() RemoteBlockItemReadFunc { insecure := httpBlockItemReadFunc(true) return func(ctx context.Context, uri url.URL, callback func(io.Reader) error) (bool, error) { - if uri.Scheme == "https" && network.HasTLSInsecure(uri.Fragment, HTTPInsecureFlag) { + if uri.Scheme == "https" && nutil.HasTLSInsecure(uri.Fragment, HTTPInsecureFlag) { return insecure(ctx, uri, callback) } diff --git a/isaac/states/ballot_stuck_resolver.go b/isaac/states/ballot_stuck_resolver.go index 6be86994..6a849b13 100644 --- a/isaac/states/ballot_stuck_resolver.go +++ b/isaac/states/ballot_stuck_resolver.go @@ -32,14 +32,15 @@ type DefaultBallotStuckResolver struct { voteSuffrageVotingf func(context.Context, base.StagePoint, []base.Address) (base.Voteproof, error) vpch chan base.Voteproof point base.StagePoint - initialWait time.Duration + initialWait func() time.Duration interval time.Duration - resolveAfter time.Duration + resolveAfter func() time.Duration } func NewDefaultBallotStuckResolver( - initialWait, - interval, resolveAfter time.Duration, + initialWait func() time.Duration, + interval time.Duration, + resolveAfter func() time.Duration, findMissingBallotsf func(context.Context, base.StagePoint, bool) ([]base.Address, bool, error), requestMissingBallotsf func(context.Context, base.StagePoint, []base.Address) error, voteSuffrageVotingf func(context.Context, base.StagePoint, []base.Address) (base.Voteproof, error), @@ -76,7 +77,7 @@ func (c *DefaultBallotStuckResolver) NewPoint(ctx context.Context, point base.St previous() // NOTE cancel previous wait } - wctx, wcancel := context.WithTimeout(ctx, c.initialWait) + wctx, wcancel := context.WithTimeout(ctx, c.initialWait()) sctx, cancel := context.WithCancel(ctx) l := c.Log().With().Interface("point", point).Logger() @@ -151,8 +152,8 @@ func (c *DefaultBallotStuckResolver) SetLogging(l *logging.Logging) *logging.Log _ = c.Logging.SetLogging(l) c.Log().Debug(). - Stringer("initial_wait", c.initialWait). - Stringer("resolve_after", c.resolveAfter). + Stringer("initial_wait", c.initialWait()). + Stringer("resolve_after", c.resolveAfter()). Stringer("interval", c.interval). Msg("resolver started") @@ -167,7 +168,7 @@ func (c *DefaultBallotStuckResolver) start(ctx context.Context, point base.Stage ticker := time.NewTicker(c.interval) defer ticker.Stop() - resolveAfterch := time.After(c.resolveAfter) + resolveAfterch := time.After(c.resolveAfter()) var count int diff --git a/isaac/states/broadcast.go b/isaac/states/broadcast.go index 71b4fbd3..fc71e3c1 100644 --- a/isaac/states/broadcast.go +++ b/isaac/states/broadcast.go @@ -24,15 +24,15 @@ type ballotBroadcastTimers struct { timers *util.SimpleTimers broadcast func(context.Context, base.Ballot) error points map[util.TimerID]base.StagePoint - interval time.Duration - broadcastTimerMult int + interval func() time.Duration + broadcastTimerMult func() int } func newBallotBroadcastTimers( timers *util.SimpleTimers, broadcast func(context.Context, base.Ballot) error, - interval time.Duration, - broadcastTimerMult int, + interval func() time.Duration, + broadcastTimerMult func() int, ) *ballotBroadcastTimers { return &ballotBroadcastTimers{ RWMutex: &sync.RWMutex{}, @@ -94,7 +94,7 @@ func (bbt *ballotBroadcastTimers) SetBroadcasterFunc( return bbt } -func (bbt *ballotBroadcastTimers) SetInterval(i time.Duration) *ballotBroadcastTimers { +func (bbt *ballotBroadcastTimers) SetInterval(i func() time.Duration) *ballotBroadcastTimers { bbt.Lock() defer bbt.Unlock() @@ -171,7 +171,7 @@ func (bbt *ballotBroadcastTimers) addTimer( ti, func(t uint64) time.Duration { if strings.HasPrefix(ti.String(), "broadcast-init-ballot/") && !strings.Contains(ti.String(), "round=0") { - return time.Duration(bbt.broadcastTimerMult) * intervalf(t) + return time.Duration(bbt.broadcastTimerMult()) * intervalf(t) } return intervalf(t) }, @@ -260,17 +260,17 @@ func (*ballotBroadcastTimers) timerID(bl base.Ballot) util.TimerID { } func (bbt *ballotBroadcastTimers) defaultInterval(initial time.Duration) func(uint64) time.Duration { - ninitial := initial - if ninitial < 1 { - ninitial = time.Nanosecond + nInitial := initial + if nInitial < 1 { + nInitial = time.Nanosecond } return func(i uint64) time.Duration { if i < 1 { - return ninitial + return nInitial } - return bbt.interval + return bbt.interval() } } diff --git a/isaac/states/states.go b/isaac/states/states.go index 71dc0719..ac7ffec6 100644 --- a/isaac/states/states.go +++ b/isaac/states/states.go @@ -109,8 +109,8 @@ func NewStates(networkID base.NetworkID, local base.LocalNode, args *StatesArgs) return nil }, - st.args.IntervalBroadcastBallot(), - st.args.BroadcastTimerMult(), + st.args.IntervalBroadcastBallot, + st.args.BroadcastTimerMult, ) if st.args.Ballotbox != nil { diff --git a/launch/design.go b/launch/design.go index a31b1f4b..b232a702 100644 --- a/launch/design.go +++ b/launch/design.go @@ -229,11 +229,11 @@ func (d NodeDesign) MarshalJSON() ([]byte, error) { return util.MarshalJSON(d.marshaler()) } -func (d NodeDesign) MarshalYAML() (interface{}, error) { - return d.marshaler(), nil +func (d NodeDesign) MarshalYAML() interface{} { + return d.marshaler() } -func (d *NodeDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { +func (d *NodeDesign) DecodeYAML(b []byte, enc encoder.Encoder) error { e := util.StringError("decode NodeDesign") nb, err := util.ReplaceEnvVariables(b) @@ -246,14 +246,14 @@ func (d *NodeDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { return e.Wrap(err) } - switch address, err := base.DecodeAddress(u.Address, jsonencoder); { + switch address, err := base.DecodeAddress(u.Address, enc); { case err != nil: return e.WithMessage(err, "invalid address") default: d.Address = address } - switch priv, err := base.DecodePrivatekeyFromString(u.Privatekey, jsonencoder); { + switch priv, err := base.DecodePrivatekeyFromString(u.Privatekey, enc); { case err != nil: return e.WithMessage(err, "invalid privatekey") default: @@ -262,14 +262,14 @@ func (d *NodeDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { d.NetworkID = base.NetworkID([]byte(u.NetworkID)) - switch i, err := u.Network.Decode(jsonencoder); { + switch i, err := u.Network.Decode(enc); { case err != nil: return e.Wrap(err) default: d.Network = i } - switch i, err := u.Storage.Decode(jsonencoder); { + switch i, err := u.Storage.Decode(enc); { case err != nil: return e.Wrap(err) default: @@ -281,7 +281,7 @@ func (d *NodeDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { return e.Wrap(err) default: d.SyncSources = NewSyncSourcesDesign(nil) - if err := d.SyncSources.DecodeYAML(sb, jsonencoder); err != nil { + if err := d.SyncSources.DecodeYAML(sb, enc); err != nil { return e.Wrap(err) } } @@ -292,7 +292,7 @@ func (d *NodeDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { case err != nil: return e.Wrap(err) default: - if err := d.LocalParams.DecodeYAML(lb, jsonencoder); err != nil { + if err := d.LocalParams.DecodeYAML(lb, enc); err != nil { return e.Wrap(err) } } @@ -504,7 +504,7 @@ func (d NodeStorageDesign) MarshalYAML() (interface{}, error) { return d.marshaler(), nil } -func (d *NodeStorageDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { +func (d *NodeStorageDesign) DecodeYAML(b []byte, enc encoder.Encoder) error { e := util.StringError("decode NodeStorageDesign") nb, err := util.ReplaceEnvVariables(b) @@ -518,7 +518,7 @@ func (d *NodeStorageDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) er return e.Wrap(err) } - switch i, err := u.Decode(jsonencoder); { + switch i, err := u.Decode(enc); { case err != nil: return err default: @@ -560,7 +560,7 @@ func (*GenesisDesign) IsValid([]byte) error { } func (d *GenesisDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { - e := util.StringError("decode GenesisOpertionsDesign") + e := util.StringError("decode GenesisDesign") nb, err := util.ReplaceEnvVariables(b) if err != nil { @@ -666,10 +666,19 @@ func (d *SyncSourcesDesign) MarshalJSON() ([]byte, error) { } func (d *SyncSourcesDesign) MarshalYAML() (interface{}, error) { - return d.l, nil + var syncSources []interface{} + for i := range d.l { + y, err := d.l[i].MarshalYAML() + if err != nil { + return nil, err + } + syncSources = append(syncSources, y) + } + + return syncSources, nil } -func (d *SyncSourcesDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { +func (d *SyncSourcesDesign) DecodeYAML(b []byte, enc encoder.Encoder) error { e := util.StringError("decode SyncSourcesDesign") nb, err := util.ReplaceEnvVariables(b) @@ -692,7 +701,7 @@ func (d *SyncSourcesDesign) DecodeYAML(b []byte, jsonencoder encoder.Encoder) er var s isaacnetwork.SyncSource - switch err := s.DecodeYAML(vb, jsonencoder); { + switch err := s.DecodeYAML(vb, enc); { case err != nil: return e.Wrap(err) default: @@ -740,11 +749,24 @@ func getFromConsul(addr, key string) ([]byte, error) { } } -func getFromHTTP(u string, tlsinsecure bool) ([]byte, error) { +func WriteConsul(b []byte, flag DesignFlag) error { + switch client, err := consulClient(flag.URL().Host); { + case err != nil: + return err + default: + kv := &consulapi.KVPair{Key: flag.URL().Path, Value: b} + + _, err = client.KV().Put(kv, nil) + + return errors.WithStack(err) + } +} + +func getFromHTTP(u string, tlsInsecure bool) ([]byte, error) { httpclient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ - InsecureSkipVerify: tlsinsecure, + InsecureSkipVerify: tlsInsecure, }, }, } diff --git a/launch/design_test.go b/launch/design_test.go index 30235a5b..67515dc1 100644 --- a/launch/design_test.go +++ b/launch/design_test.go @@ -1139,73 +1139,6 @@ parameters: }) } -func (t *testNodeDesign) TestUpdateYAML() { - b := []byte(` -address: no0sas -privatekey: 58d2671582e7866ab98bc0024c4c474b81b2f0846f06c0696ebd50a9dd3127e0mpr -network_id: hehe 1 2 3 4 -network: - bind: 0.0.0.0:1234 - publish: 1.2.3.4:4321 - tls_insecure: true -storage: - base: /tmp/a/b/c - database: redis:// -parameters: - isaac: - threshold: - decimal: 66 -`) - - var m *util.YAMLOrderedMap - t.NoError(yaml.Unmarshal(b, &m)) - - { - t.T().Logf("before marshaled:\n%s", string(b)) - - b, err := yaml.Marshal(m) - t.NoError(err) - t.T().Logf("after marshaled:\n%s", string(b)) - } - - t.Run("ok", func() { - t.NoError(updateDesignMap(m, "parameters.isaac.threshold.decimal", 77)) - - v, _ := m.Get("parameters") - v, _ = v.(*util.YAMLOrderedMap).Get("isaac") - v, _ = v.(*util.YAMLOrderedMap).Get("threshold") - v, found := v.(*util.YAMLOrderedMap).Get("decimal") - t.True(found) - t.Equal(77, v) - - b, err := yaml.Marshal(m) - t.NoError(err) - t.T().Logf("after updated:\n%s", string(b)) - }) - - t.Run("wrong key", func() { - t.Error(updateDesignMap(m, "", 88)) - t.Error(updateDesignMap(m, ".", 88)) - t.Error(updateDesignMap(m, ". ", 88)) - t.Error(updateDesignMap(m, ". .", 88)) - }) - - t.Run("add new", func() { - t.NoError(updateDesignMap(m, "parameters.a.b.c", 88)) - - v, _ := m.Get("parameters") - v, _ = v.(*util.YAMLOrderedMap).Get("a") - v, _ = v.(*util.YAMLOrderedMap).Get("b") - v, found := v.(*util.YAMLOrderedMap).Get("c") - t.True(found) - t.Equal(88, v) - - b, err := yaml.Marshal(m) - t.NoError(err) - t.T().Logf("after updated:\n%s", string(b)) - }) -} - func TestNodeDesign(t *testing.T) { suite.Run(t, new(testNodeDesign)) } diff --git a/launch/event.go b/launch/event.go index abba4256..837fad9b 100644 --- a/launch/event.go +++ b/launch/event.go @@ -572,7 +572,7 @@ func PEventLoggingNetworkHandlers(pctx context.Context) (context.Context, error) var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return pctx, err default: diff --git a/launch/flags.go b/launch/flags.go index 351b6898..c2d50e74 100644 --- a/launch/flags.go +++ b/launch/flags.go @@ -10,8 +10,8 @@ import ( "time" "github.com/ProtoconNet/mitum2/base" - "github.com/ProtoconNet/mitum2/network" "github.com/ProtoconNet/mitum2/network/quicstream" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/pkg/errors" @@ -57,11 +57,11 @@ func (f *ConnInfoFlag) UnmarshalText(b []byte) error { s := string(b) - if err := network.IsValidAddr(s); err != nil { + if err := nutil.IsValidAddr(s); err != nil { return e.Wrap(err) } - f.addr, f.tlsinsecure = network.ParseTLSInsecure(s) + f.addr, f.tlsinsecure = nutil.ParseTLSInsecure(s) switch ci, err := quicstream.NewConnInfoFromStringAddr(f.addr, f.tlsinsecure); { case err != nil: @@ -74,7 +74,7 @@ func (f *ConnInfoFlag) UnmarshalText(b []byte) error { } func (f ConnInfoFlag) String() string { - return network.ConnInfoToString(f.addr, f.tlsinsecure) + return nutil.ConnInfoToString(f.addr, f.tlsinsecure) } func (f ConnInfoFlag) MarshalText() ([]byte, error) { diff --git a/launch/handover.go b/launch/handover.go index 8bc11c8e..53a273ee 100644 --- a/launch/handover.go +++ b/launch/handover.go @@ -352,7 +352,7 @@ func attachStartHandoverHandler( var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return err default: @@ -420,7 +420,7 @@ func attachCancelHandoverHandler( var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return err default: @@ -481,7 +481,7 @@ func attachCheckHandoverHandler( var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return err default: diff --git a/launch/hinters.go b/launch/hinters.go index 57e53ea2..2a5a5c71 100644 --- a/launch/hinters.go +++ b/launch/hinters.go @@ -43,6 +43,7 @@ var Hinters = []encoder.DecodeDetail{ {Hint: isaac.INITExpelVoteproofHint, Instance: isaac.INITExpelVoteproof{}}, {Hint: isaac.INITStuckVoteproofHint, Instance: isaac.INITStuckVoteproof{}}, {Hint: isaac.ParamsHint, Instance: &isaac.Params{}}, + {Hint: isaac.MiscParamsHint, Instance: &isaac.MISCParams{}}, {Hint: isaac.ManifestHint, Instance: isaac.Manifest{}}, {Hint: isaac.NetworkPolicyHint, Instance: isaac.NetworkPolicy{}}, {Hint: isaac.NetworkPolicyStateValueHint, Instance: isaac.NetworkPolicyStateValue{}}, @@ -128,6 +129,7 @@ var Hinters = []encoder.DecodeDetail{ Instance: quicmemberlist.EnsureBroadcastMessageHeader{}, }, {Hint: quicmemberlist.MemberHint, Instance: quicmemberlist.BaseMember{}}, + {Hint: quicmemberlist.ParamsHint, Instance: &quicmemberlist.MemberlistParams{}}, {Hint: quicstreamheader.DefaultResponseHeaderHint, Instance: quicstreamheader.DefaultResponseHeader{}}, {Hint: ReadNodeHeaderHint, Instance: ReadNodeHeader{}}, diff --git a/launch/local_params.go b/launch/local_params.go index 8bb0aa8e..7c2012be 100644 --- a/launch/local_params.go +++ b/launch/local_params.go @@ -39,25 +39,25 @@ type LocalParams struct { // Memberlist sets the memberlist parameters. memberlist handles the // connections of suffrage nodes. For details, see // https://pkg.go.dev/github.com/hashicorp/memberlist#Config . - Memberlist *MemberlistParams `yaml:"memberlist,omitempty" json:"memberlist,omitempty"` + Memberlist *quicmemberlist.MemberlistParams `yaml:"memberlist,omitempty" json:"memberlist,omitempty"` // Network sets the network related parameters. For details, see // https://pkg.go.dev/github.com/quic-go/quic-go#Config . Network *NetworkParams `yaml:"network,omitempty" json:"network,omitempty"` // MISC sets misc parameters. - MISC *MISCParams `yaml:"misc,omitempty" json:"misc,omitempty"` + MISC *isaac.MISCParams `yaml:"misc,omitempty" json:"misc,omitempty"` } func defaultLocalParams(networkID base.NetworkID) *LocalParams { return &LocalParams{ ISAAC: isaac.DefaultParams(networkID), - Memberlist: defaultMemberlistParams(), - MISC: defaultMISCParams(), + Memberlist: quicmemberlist.DefaultMemberlistParams(), + MISC: isaac.DefaultMISCParams(), Network: defaultNetworkParams(), } } func (p *LocalParams) IsValid(networkID base.NetworkID) error { - e := util.ErrInvalid.Errorf("invalid LocalParams") + e := util.ErrInvalid.Errorf("invalid IsaacParams") if p.ISAAC == nil { return e.Errorf("empty ISAAC") @@ -78,451 +78,6 @@ func (p *LocalParams) IsValid(networkID base.NetworkID) error { return nil } -type MemberlistParams struct { - *util.BaseParams - tcpTimeout time.Duration - retransmitMult int - probeTimeout time.Duration - probeInterval time.Duration - gossipInterval time.Duration - suspicionMult int - suspicionMaxTimeoutMult int - udpBufferSize int - extraSameMemberLimit uint64 - broadcastTimerMult int - userMsgLoopInterval time.Duration - gosshipNodes int -} - -func defaultMemberlistParams() *MemberlistParams { - config := quicmemberlist.BasicMemberlistConfig() - - return &MemberlistParams{ - BaseParams: util.NewBaseParams(), - tcpTimeout: config.TCPTimeout, - retransmitMult: config.RetransmitMult, - probeTimeout: config.ProbeTimeout, - probeInterval: config.ProbeInterval, - gossipInterval: config.GossipInterval, - suspicionMult: config.SuspicionMult, - suspicionMaxTimeoutMult: config.SuspicionMaxTimeoutMult, - udpBufferSize: config.UDPBufferSize, - extraSameMemberLimit: 1, //nolint:gomnd //... - broadcastTimerMult: 5, - userMsgLoopInterval: time.Millisecond * 33, - gosshipNodes: config.GossipNodes, - } -} - -func (*MemberlistParams) IsValid([]byte) error { - return nil -} - -func (p *MemberlistParams) TCPTimeout() time.Duration { - return p.tcpTimeout -} - -func (p *MemberlistParams) SetTCPTimeout(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.tcpTimeout == d { - return false, nil - } - - p.tcpTimeout = d - - return true, nil - }) -} - -func (p *MemberlistParams) RetransmitMult() int { - return p.retransmitMult -} - -func (p *MemberlistParams) SetRetransmitMult(d int) error { - return p.SetOverZeroInt(d, func(d int) (bool, error) { - if p.retransmitMult == d { - return false, nil - } - - p.retransmitMult = d - - return true, nil - }) -} - -func (p *MemberlistParams) ProbeTimeout() time.Duration { - return p.probeTimeout -} - -func (p *MemberlistParams) SetProbeTimeout(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.probeTimeout == d { - return false, nil - } - - p.probeTimeout = d - - return true, nil - }) -} - -func (p *MemberlistParams) ProbeInterval() time.Duration { - return p.probeInterval -} - -func (p *MemberlistParams) SetProbeInterval(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.probeInterval == d { - return false, nil - } - - p.probeInterval = d - - return true, nil - }) -} - -func (p *MemberlistParams) GossipInterval() time.Duration { - return p.gossipInterval -} - -func (p *MemberlistParams) SetGossipInterval(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.gossipInterval == d { - return false, nil - } - - p.gossipInterval = d - - return true, nil - }) -} - -func (p *MemberlistParams) GosshipNodes() int { - return p.gosshipNodes -} - -func (p *MemberlistParams) SetGosshipNodes(d int) error { - return p.SetOverZeroInt(d, func(d int) (bool, error) { - if p.gosshipNodes == d { - return false, nil - } - - p.gosshipNodes = d - - return true, nil - }) -} - -func (p *MemberlistParams) SuspicionMult() int { - return p.suspicionMult -} - -func (p *MemberlistParams) SetSuspicionMult(d int) error { - return p.SetOverZeroInt(d, func(d int) (bool, error) { - if p.suspicionMult == d { - return false, nil - } - - p.suspicionMult = d - - return true, nil - }) -} - -func (p *MemberlistParams) SuspicionMaxTimeoutMult() int { - return p.suspicionMaxTimeoutMult -} - -func (p *MemberlistParams) SetSuspicionMaxTimeoutMult(d int) error { - return p.SetOverZeroInt(d, func(d int) (bool, error) { - if p.suspicionMaxTimeoutMult == d { - return false, nil - } - - p.suspicionMaxTimeoutMult = d - - return true, nil - }) -} - -func (p *MemberlistParams) UDPBufferSize() int { - return p.udpBufferSize -} - -func (p *MemberlistParams) SetUDPBufferSize(d int) error { - return p.SetOverZeroInt(d, func(d int) (bool, error) { - if p.udpBufferSize == d { - return false, nil - } - - p.udpBufferSize = d - - return true, nil - }) -} - -func (p *MemberlistParams) ExtraSameMemberLimit() uint64 { - return p.extraSameMemberLimit -} - -func (p *MemberlistParams) SetExtraSameMemberLimit(d uint64) error { - return p.SetUint64(d, func(d uint64) (bool, error) { - if p.extraSameMemberLimit == d { - return false, nil - } - - p.extraSameMemberLimit = d - - return true, nil - }) -} - -func (p *MemberlistParams) BroadcastTimerMult() int { - return p.broadcastTimerMult -} - -func (p *MemberlistParams) SetBroadcastTimerMult(d int) error { - return p.SetOverZeroInt(d, func(d int) (bool, error) { - if p.broadcastTimerMult == d { - return false, nil - } - - p.broadcastTimerMult = d - - return true, nil - }) -} - -func (p *MemberlistParams) UserMsgLoopInterval() time.Duration { - return p.userMsgLoopInterval -} - -func (p *MemberlistParams) SetUserMsgLoopInterval(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.userMsgLoopInterval == d { - return false, nil - } - - p.userMsgLoopInterval = d - - return true, nil - }) -} - -type MISCParams struct { - *util.BaseParams - syncSourceCheckerInterval time.Duration - validProposalOperationExpire time.Duration - validProposalSuffrageOperationsExpire time.Duration - blockItemReadersRemoveEmptyAfter time.Duration - blockItemReadersRemoveEmptyInterval time.Duration - maxMessageSize uint64 - objectCacheSize uint64 -} - -func defaultMISCParams() *MISCParams { - return &MISCParams{ - BaseParams: util.NewBaseParams(), - syncSourceCheckerInterval: time.Second * 30, //nolint:gomnd //... - validProposalOperationExpire: time.Hour * 24, //nolint:gomnd //... - validProposalSuffrageOperationsExpire: time.Hour * 2, - blockItemReadersRemoveEmptyAfter: isaac.DefaultBlockItemReadersRemoveEmptyAfter, - blockItemReadersRemoveEmptyInterval: isaac.DefaultBlockItemReadersRemoveEmptyInterval, - maxMessageSize: 1 << 18, //nolint:gomnd //... - objectCacheSize: 1 << 13, //nolint:gomnd // big enough - } -} - -func (p *MISCParams) IsValid([]byte) error { - e := util.ErrInvalid.Errorf("invalid MISCParams") - - if err := p.BaseParams.IsValid(nil); err != nil { - return e.Wrap(err) - } - - if p.syncSourceCheckerInterval < 0 { - return e.Errorf("wrong duration; invalid syncSourceCheckerInterval") - } - - if p.validProposalOperationExpire < 0 { - return e.Errorf("wrong duration; invalid validProposalOperationExpire") - } - - if p.validProposalSuffrageOperationsExpire < 0 { - return e.Errorf("wrong duration; invalid validProposalSuffrageOperationsExpire") - } - - if p.blockItemReadersRemoveEmptyAfter < 0 { - return e.Errorf("wrong duration; invalid blockItemReadersRemoveEmptyAfter") - } - - if p.blockItemReadersRemoveEmptyInterval < 0 { - return e.Errorf("wrong duration; invalid blockItemReadersRemoveEmptyInterval") - } - - if p.maxMessageSize < 1 { - return e.Errorf("wrong maxMessageSize") - } - - if p.objectCacheSize < 1 { - return e.Errorf("wrong objectCacheSize") - } - - return nil -} - -// SyncSourceCheckerInterval is the interval to check the liveness of sync -// sources. -func (p *MISCParams) SyncSourceCheckerInterval() time.Duration { - p.RLock() - defer p.RUnlock() - - return p.syncSourceCheckerInterval -} - -func (p *MISCParams) SetSyncSourceCheckerInterval(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.syncSourceCheckerInterval == d { - return false, nil - } - - p.syncSourceCheckerInterval = d - - return true, nil - }) -} - -// ValidProposalOperationExpire is the maximum creation time for valid -// operation. If the creation time of operation is older than -// ValidProposalOperationExpire, it will be ignored. -func (p *MISCParams) ValidProposalOperationExpire() time.Duration { - p.RLock() - defer p.RUnlock() - - return p.validProposalOperationExpire -} - -func (p *MISCParams) SetValidProposalOperationExpire(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.validProposalOperationExpire == d { - return false, nil - } - - p.validProposalOperationExpire = d - - return true, nil - }) -} - -// ValidProposalSuffrageOperationsExpire is the maximum creation time for valid -// suffrage operations like isaacoperation.SuffrageCandidate operation. If the -// creation time of suffrage operation is older than -// ValidProposalSuffrageOperationsExpire, it will be ignored. -func (p *MISCParams) ValidProposalSuffrageOperationsExpire() time.Duration { - p.RLock() - defer p.RUnlock() - - return p.validProposalSuffrageOperationsExpire -} - -func (p *MISCParams) SetValidProposalSuffrageOperationsExpire(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.validProposalSuffrageOperationsExpire == d { - return false, nil - } - - p.validProposalSuffrageOperationsExpire = d - - return true, nil - }) -} - -// BlockItemReadersRemoveEmptyAfter removes empty block item directory after -// the duration. Zero duration not allowed. -func (p *MISCParams) BlockItemReadersRemoveEmptyAfter() time.Duration { - p.RLock() - defer p.RUnlock() - - return p.blockItemReadersRemoveEmptyAfter -} - -func (p *MISCParams) SetBlockItemReadersRemoveEmptyAfter(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.blockItemReadersRemoveEmptyAfter == d { - return false, nil - } - - p.blockItemReadersRemoveEmptyAfter = d - - return true, nil - }) -} - -// BlockItemReadersRemoveEmptyInterval is the interval to remove empty block -// item directory after BlockItemReadersRemoveEmptyAfter. Zero duration not -// allowed. -func (p *MISCParams) BlockItemReadersRemoveEmptyInterval() time.Duration { - p.RLock() - defer p.RUnlock() - - return p.blockItemReadersRemoveEmptyInterval -} - -func (p *MISCParams) SetBlockItemReadersRemoveEmptyInterval(d time.Duration) error { - return p.SetDuration(d, func(d time.Duration) (bool, error) { - if p.blockItemReadersRemoveEmptyInterval == d { - return false, nil - } - - p.blockItemReadersRemoveEmptyInterval = d - - return true, nil - }) -} - -// MaxMessageSize is the maximum size of incoming messages like ballot or -// operation. If message size is over, it will be ignored. -func (p *MISCParams) MaxMessageSize() uint64 { - p.RLock() - defer p.RUnlock() - - return p.maxMessageSize -} - -func (p *MISCParams) SetMaxMessageSize(d uint64) error { - return p.SetUint64(d, func(d uint64) (bool, error) { - if p.maxMessageSize == d { - return false, nil - } - - p.maxMessageSize = d - - return true, nil - }) -} - -// ObjectCacheSize is the cache size for various internal objects like address -// or keypair. -func (p *MISCParams) ObjectCacheSize() uint64 { - p.RLock() - defer p.RUnlock() - - return p.objectCacheSize -} - -func (p *MISCParams) SetObjectCacheSize(d uint64) error { - return p.SetUint64(d, func(d uint64) (bool, error) { - if p.objectCacheSize == d { - return false, nil - } - - p.objectCacheSize = d - - return true, nil - }) -} - type NetworkParams struct { *util.BaseParams rateLimit *NetworkRateLimitParams diff --git a/launch/local_params_marshal.go b/launch/local_params_marshal.go index 7ed9ba37..8b39178f 100644 --- a/launch/local_params_marshal.go +++ b/launch/local_params_marshal.go @@ -5,6 +5,7 @@ import ( "time" "github.com/ProtoconNet/mitum2/isaac" + "github.com/ProtoconNet/mitum2/network/quicmemberlist" "github.com/ProtoconNet/mitum2/network/quicstream" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" @@ -49,10 +50,10 @@ func (p *LocalParams) MarshalYAML() (interface{}, error) { } type LocalParamsYAMLUnmarshaler struct { - ISAAC map[string]interface{} `yaml:"isaac"` - Memberlist *MemberlistParams `yaml:"memberlist,omitempty"` - MISC *MISCParams `yaml:"misc,omitempty"` - Network *NetworkParams `yaml:"network,omitempty"` + ISAAC map[string]interface{} `yaml:"isaac"` + Memberlist *quicmemberlist.MemberlistParams `yaml:"memberlist,omitempty"` + MISC *isaac.MISCParams `yaml:"misc,omitempty"` + Network *NetworkParams `yaml:"network,omitempty"` } func (p *LocalParams) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { @@ -60,7 +61,7 @@ func (p *LocalParams) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { return nil } - e := util.StringError("decode LocalParams") + e := util.StringError("decode IsaacParams") nb, err := util.ReplaceEnvVariables(b) if err != nil { @@ -103,255 +104,6 @@ func (p *LocalParams) DecodeYAML(b []byte, jsonencoder encoder.Encoder) error { return nil } -type memberlistParamsMarshaler struct { - //revive:disable:line-length-limit - TCPTimeout util.ReadableDuration `json:"tcp_timeout,omitempty" yaml:"tcp_timeout,omitempty"` - RetransmitMult int `json:"retransmit_mult,omitempty" yaml:"retransmit_mult,omitempty"` - ProbeTimeout util.ReadableDuration `json:"probe_timeout,omitempty" yaml:"probe_timeout,omitempty"` - ProbeInterval util.ReadableDuration `json:"probe_interval,omitempty" yaml:"probe_interval,omitempty"` - GossipInterval util.ReadableDuration `json:"gossip_interval,omitempty" yaml:"gossip_interval,omitempty"` - GosshipNodes int `json:"gossip_nodes,omitempty" yaml:"gossip_nodes,omitempty"` - SuspicionMult int `json:"suspicion_mult,omitempty" yaml:"suspicion_mult,omitempty"` - SuspicionMaxTimeoutMult int `json:"suspicion_max_timeout_mult,omitempty" yaml:"suspicion_max_timeout_mult,omitempty"` - UDPBufferSize int `json:"udp_buffer_size,omitempty" yaml:"udp_buffer_size,omitempty"` - ExtraSameMemberLimit uint64 `json:"extra_same_member_limit,omitempty" yaml:"extra_same_member_limit,omitempty"` - BroadcastTimerMult int `json:"broadcast_timer_mult,omitempty" yaml:"broadcast_timer_mult,omitempty"` - UserMsgLoopInterval util.ReadableDuration `json:"user_msg_loop_interval,omitempty" yaml:"user_msg_loop_interval,omitempty"` - //revive:enable:line-length-limit -} - -func (p *MemberlistParams) marshaler() memberlistParamsMarshaler { - return memberlistParamsMarshaler{ - TCPTimeout: util.ReadableDuration(p.tcpTimeout), - RetransmitMult: p.retransmitMult, - ProbeTimeout: util.ReadableDuration(p.probeTimeout), - ProbeInterval: util.ReadableDuration(p.probeInterval), - GossipInterval: util.ReadableDuration(p.gossipInterval), - GosshipNodes: p.gosshipNodes, - SuspicionMult: p.suspicionMult, - SuspicionMaxTimeoutMult: p.suspicionMaxTimeoutMult, - UDPBufferSize: p.udpBufferSize, - ExtraSameMemberLimit: p.extraSameMemberLimit, - BroadcastTimerMult: p.broadcastTimerMult, - UserMsgLoopInterval: util.ReadableDuration(p.userMsgLoopInterval), - } -} - -func (p *MemberlistParams) MarshalJSON() ([]byte, error) { - return util.MarshalJSON(p.marshaler()) -} - -func (p *MemberlistParams) MarshalYAML() (interface{}, error) { - return p.marshaler(), nil -} - -type memberlistParamsUnmarshaler struct { - //revive:disable:line-length-limit - TCPTimeout *util.ReadableDuration `json:"tcp_timeout,omitempty" yaml:"tcp_timeout,omitempty"` - RetransmitMult *int `json:"retransmit_mult,omitempty" yaml:"retransmit_mult,omitempty"` - ProbeTimeout *util.ReadableDuration `json:"probe_timeout,omitempty" yaml:"probe_timeout,omitempty"` - ProbeInterval *util.ReadableDuration `json:"probe_interval,omitempty" yaml:"probe_interval,omitempty"` - GossipInterval *util.ReadableDuration `json:"gossip_interval,omitempty" yaml:"gossip_interval,omitempty"` - GosshipNodes *int `json:"gossip_nodes,omitempty" yaml:"gossip_nodes,omitempty"` - SuspicionMult *int `json:"suspicion_mult,omitempty" yaml:"suspicion_mult,omitempty"` - SuspicionMaxTimeoutMult *int `json:"suspicion_max_timeout_mult,omitempty" yaml:"suspicion_max_timeout_mult,omitempty"` - UDPBufferSize *int `json:"udp_buffer_size,omitempty" yaml:"udp_buffer_size,omitempty"` - ExtraSameMemberLimit *uint64 `json:"extra_same_member_limit,omitempty" yaml:"extra_same_member_limit,omitempty"` - BroadcastTimerMult *int `json:"broadcast_timer_mult,omitempty" yaml:"broadcast_timer_mult,omitempty"` - UserMsgLoopInterval *util.ReadableDuration `json:"user_msg_loop_interval,omitempty" yaml:"user_msg_loop_interval,omitempty"` - //revive:enable:line-length-limit -} - -func (p *MemberlistParams) UnmarshalJSON(b []byte) error { - d := defaultMemberlistParams() - *p = *d - - e := util.StringError("unmarshal MemberlistParams") - - var u memberlistParamsUnmarshaler - - if err := util.UnmarshalJSON(b, &u); err != nil { - return e.Wrap(err) - } - - return e.Wrap(p.unmarshal(u)) -} - -func (p *MemberlistParams) UnmarshalYAML(y *yaml.Node) error { - d := defaultMemberlistParams() - *p = *d - - e := util.StringError("unmarshal MemberlistParams") - - var u memberlistParamsUnmarshaler - - if err := y.Decode(&u); err != nil { - return e.Wrap(err) - } - - return e.Wrap(p.unmarshal(u)) -} - -func (p *MemberlistParams) unmarshal(u memberlistParamsUnmarshaler) error { - if u.RetransmitMult != nil { - p.retransmitMult = *u.RetransmitMult - } - - if u.SuspicionMult != nil { - p.suspicionMult = *u.SuspicionMult - } - - if u.SuspicionMaxTimeoutMult != nil { - p.suspicionMaxTimeoutMult = *u.SuspicionMaxTimeoutMult - } - - if u.UDPBufferSize != nil { - p.udpBufferSize = *u.UDPBufferSize - } - - if u.ExtraSameMemberLimit != nil { - p.extraSameMemberLimit = *u.ExtraSameMemberLimit - } - - if u.BroadcastTimerMult != nil { - p.broadcastTimerMult = *u.BroadcastTimerMult - } - - if u.GosshipNodes != nil { - p.gosshipNodes = *u.GosshipNodes - } - - durargs := [][2]interface{}{ - {u.TCPTimeout, &p.tcpTimeout}, - {u.ProbeTimeout, &p.probeTimeout}, - {u.ProbeInterval, &p.probeInterval}, - {u.GossipInterval, &p.gossipInterval}, - {u.UserMsgLoopInterval, &p.userMsgLoopInterval}, - } - - for i := range durargs { - v := durargs[i][0].(*util.ReadableDuration) //nolint:forcetypeassert //... - t := durargs[i][1].(*time.Duration) //nolint:forcetypeassert //... - - if reflect.ValueOf(v).IsZero() { - continue - } - - if err := util.SetInterfaceValue[time.Duration](time.Duration(*v), t); err != nil { - return err - } - } - - return nil -} - -type miscParamsYAMLMarshaler struct { - //revive:disable:line-length-limit - SyncSourceCheckerInterval util.ReadableDuration `json:"sync_source_checker_interval,omitempty" yaml:"sync_source_checker_interval,omitempty"` - ValidProposalOperationExpire util.ReadableDuration `json:"valid_proposal_operation_expire,omitempty" yaml:"valid_proposal_operation_expire,omitempty"` - ValidProposalSuffrageOperationsExpire util.ReadableDuration `json:"valid_proposal_suffrage_operations_expire,omitempty" yaml:"valid_proposal_suffrage_operations_expire,omitempty"` - BlockItemReadersRemoveEmptyAfter util.ReadableDuration `json:"block_item_readers_remove_empty_after,omitempty" yaml:"block_item_readers_remove_empty_after,omitempty"` - BlockItemReadersRemoveEmptyInterval util.ReadableDuration `json:"block_item_readers_remove_empty_interval,omitempty" yaml:"block_item_readers_remove_empty_interval,omitempty"` - MaxMessageSize uint64 `json:"max_message_size,omitempty" yaml:"max_message_size,omitempty"` - ObjectCacheSize uint64 `json:"object_cache_size,omitempty" yaml:"object_cache_size,omitempty"` - //revive:enable:line-length-limit -} - -func (p *MISCParams) marshaler() miscParamsYAMLMarshaler { - return miscParamsYAMLMarshaler{ - SyncSourceCheckerInterval: util.ReadableDuration(p.syncSourceCheckerInterval), - ValidProposalOperationExpire: util.ReadableDuration(p.validProposalOperationExpire), - ValidProposalSuffrageOperationsExpire: util.ReadableDuration(p.validProposalSuffrageOperationsExpire), - BlockItemReadersRemoveEmptyAfter: util.ReadableDuration(p.blockItemReadersRemoveEmptyAfter), - BlockItemReadersRemoveEmptyInterval: util.ReadableDuration(p.blockItemReadersRemoveEmptyInterval), - MaxMessageSize: p.maxMessageSize, - ObjectCacheSize: p.objectCacheSize, - } -} - -func (p *MISCParams) MarshalJSON() ([]byte, error) { - return util.MarshalJSON(p.marshaler()) -} - -func (p *MISCParams) MarshalYAML() (interface{}, error) { - return p.marshaler(), nil -} - -type miscParamsYAMLUnmarshaler struct { - //revive:disable:line-length-limit - SyncSourceCheckerInterval *util.ReadableDuration `json:"sync_source_checker_interval,omitempty" yaml:"sync_source_checker_interval,omitempty"` - ValidProposalOperationExpire *util.ReadableDuration `json:"valid_proposal_operation_expire,omitempty" yaml:"valid_proposal_operation_expire,omitempty"` - ValidProposalSuffrageOperationsExpire *util.ReadableDuration `json:"valid_proposal_suffrage_operations_expire,omitempty" yaml:"valid_proposal_suffrage_operations_expire,omitempty"` - BlockItemReadersRemoveEmptyAfter *util.ReadableDuration `json:"block_item_readers_remove_empty_after,omitempty" yaml:"block_item_readers_remove_empty_after,omitempty"` - BlockItemReadersRemoveEmptyInterval *util.ReadableDuration `json:"block_item_readers_remove_empty_interval,omitempty" yaml:"block_item_readers_remove_empty_interval,omitempty"` - MaxMessageSize *uint64 `json:"max_message_size,omitempty" yaml:"max_message_size,omitempty"` - ObjectCacheSize *uint64 `json:"object_cache_size,omitempty" yaml:"object_cache_size,omitempty"` - //revive:enable:line-length-limit -} - -func (p *MISCParams) UnmarshalJSON(b []byte) error { - d := defaultMISCParams() - *p = *d - - e := util.StringError("decode MISCParams") - - var u miscParamsYAMLUnmarshaler - - if err := util.UnmarshalJSON(b, &u); err != nil { - return e.Wrap(err) - } - - return e.Wrap(p.unmarshal(u)) -} - -func (p *MISCParams) UnmarshalYAML(y *yaml.Node) error { - d := defaultMISCParams() - *p = *d - - e := util.StringError("decode MISCParams") - - var u miscParamsYAMLUnmarshaler - - if err := y.Decode(&u); err != nil { - return e.Wrap(err) - } - - return e.Wrap(p.unmarshal(u)) -} - -func (p *MISCParams) unmarshal(u miscParamsYAMLUnmarshaler) error { - if u.MaxMessageSize != nil { - p.maxMessageSize = *u.MaxMessageSize - } - - if u.ObjectCacheSize != nil { - p.objectCacheSize = *u.ObjectCacheSize - } - - durargs := [][2]interface{}{ - {u.SyncSourceCheckerInterval, &p.syncSourceCheckerInterval}, - {u.ValidProposalOperationExpire, &p.validProposalOperationExpire}, - {u.ValidProposalSuffrageOperationsExpire, &p.validProposalSuffrageOperationsExpire}, - {u.BlockItemReadersRemoveEmptyAfter, &p.blockItemReadersRemoveEmptyAfter}, - {u.BlockItemReadersRemoveEmptyInterval, &p.blockItemReadersRemoveEmptyInterval}, - } - - for i := range durargs { - v := durargs[i][0].(*util.ReadableDuration) //nolint:forcetypeassert //... - t := durargs[i][1].(*time.Duration) //nolint:forcetypeassert //... - - if reflect.ValueOf(v).IsZero() { - continue - } - - if err := util.SetInterfaceValue(time.Duration(*v), t); err != nil { - return err - } - } - - return nil -} - type networkParamsYAMLMarshaler struct { //revive:disable:line-length-limit RateLimit *NetworkRateLimitParams `json:"ratelimit,omitempty" yaml:"ratelimit,omitempty"` //nolint:tagliatelle //... diff --git a/launch/p_acl.go b/launch/p_acl.go index f9f8a6c5..bce6efdf 100644 --- a/launch/p_acl.go +++ b/launch/p_acl.go @@ -53,7 +53,7 @@ func PLoadACL(pctx context.Context) (context.Context, error) { return context.WithValue(pctx, ACLContextKey, acl), nil } -func pACLAllowFunc(pctx context.Context) (ACLAllowFunc, error) { +func PACLAllowFunc(pctx context.Context) (ACLAllowFunc, error) { var acl *YAMLACL var eventLogging *EventLogging diff --git a/launch/p_design.go b/launch/p_design.go index 08dcc314..395acd4b 100644 --- a/launch/p_design.go +++ b/launch/p_design.go @@ -2,6 +2,7 @@ package launch import ( "context" + "github.com/ProtoconNet/mitum2/base" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" @@ -41,7 +42,7 @@ func PLoadDesign(pctx context.Context) (context.Context, error) { return pctx, e.Wrap(err) } - jsonencoder := encs.JSON() + jsonEncoder := encs.JSON() var design NodeDesign var designString string @@ -50,7 +51,7 @@ func PLoadDesign(pctx context.Context) (context.Context, error) { case "file": f := flag.URL().Path - switch d, b, err := NodeDesignFromFile(f, jsonencoder); { + switch d, b, err := NodeDesignFromFile(f, jsonEncoder); { case err != nil: return pctx, e.Wrap(err) default: @@ -58,7 +59,7 @@ func PLoadDesign(pctx context.Context) (context.Context, error) { designString = string(b) } case "http", "https": - switch d, b, err := NodeDesignFromHTTP(flag.URL().String(), flag.Properties().HTTPSTLSInsecure, jsonencoder); { + switch d, b, err := NodeDesignFromHTTP(flag.URL().String(), flag.Properties().HTTPSTLSInsecure, jsonEncoder); { case err != nil: return pctx, e.Wrap(err) default: @@ -66,7 +67,7 @@ func PLoadDesign(pctx context.Context) (context.Context, error) { designString = string(b) } case "consul": - switch d, b, err := NodeDesignFromConsul(flag.URL().Host, flag.URL().Path, jsonencoder); { + switch d, b, err := NodeDesignFromConsul(flag.URL().Host, flag.URL().Path, jsonEncoder); { case err != nil: return pctx, e.Wrap(err) default: @@ -80,7 +81,7 @@ func PLoadDesign(pctx context.Context) (context.Context, error) { log.Log().Debug().Interface("design", design).Msg("design loaded") if len(privstring) > 0 { - priv, err := base.DecodePrivatekeyFromString(privstring, jsonencoder) + priv, err := base.DecodePrivatekeyFromString(privstring, jsonEncoder) if err != nil { return pctx, e.Wrap(err) } @@ -152,9 +153,10 @@ func PCheckDesign(pctx context.Context) (context.Context, error) { log.Log().Debug().Interface("design", design).Msg("design checked") return util.ContextWithValues(pctx, map[util.ContextKey]interface{}{ - DesignContextKey: design, - LocalParamsContextKey: design.LocalParams, - ISAACParamsContextKey: design.LocalParams.ISAAC, + DesignContextKey: design, + LocalParamsContextKey: design.LocalParams, + ISAACParamsContextKey: design.LocalParams.ISAAC, + MemberlistParamsContextKey: design.LocalParams.Memberlist, }), nil } diff --git a/launch/p_local.go b/launch/p_local.go index b59f491f..f478c3e7 100644 --- a/launch/p_local.go +++ b/launch/p_local.go @@ -11,10 +11,11 @@ import ( ) var ( - PNameLocal = ps.Name("local") - LocalContextKey = util.ContextKey("local") - LocalParamsContextKey = util.ContextKey("local-params") - ISAACParamsContextKey = util.ContextKey("isaac-params") + PNameLocal = ps.Name("local") + LocalContextKey = util.ContextKey("local") + LocalParamsContextKey = util.ContextKey("local-params") + ISAACParamsContextKey = util.ContextKey("isaac-params") + MemberlistParamsContextKey = util.ContextKey("memberlist-params") ) func PLocal(pctx context.Context) (context.Context, error) { diff --git a/launch/p_memberlist.go b/launch/p_memberlist.go index e650d451..840894ab 100644 --- a/launch/p_memberlist.go +++ b/launch/p_memberlist.go @@ -43,7 +43,7 @@ func PMemberlist(pctx context.Context) (context.Context, error) { var log *logging.Logging var encs *encoder.Encoders var local base.LocalNode - var params *LocalParams + var design NodeDesign var client *isaacnetwork.BaseClient var connectionPool *quicstream.ConnectionPool @@ -51,7 +51,7 @@ func PMemberlist(pctx context.Context) (context.Context, error) { LoggingContextKey, &log, EncodersContextKey, &encs, LocalContextKey, &local, - LocalParamsContextKey, ¶ms, + DesignContextKey, &design, QuicstreamClientContextKey, &client, ConnectionPoolContextKey, &connectionPool, ); err != nil { @@ -64,18 +64,18 @@ func PMemberlist(pctx context.Context) (context.Context, error) { encs.Default(), ) - localnode, err := memberlistLocalNode(pctx) + localNode, err := memberlistLocalNode(pctx) if err != nil { return pctx, e.Wrap(err) } - config, err := memberlistConfig(pctx, localnode, connectionPool) + config, err := memberlistConfig(pctx, localNode, connectionPool) if err != nil { return pctx, e.Wrap(err) } args := quicmemberlist.NewMemberlistArgs(encs.JSON(), config) - args.ExtraSameMemberLimit = params.Memberlist.ExtraSameMemberLimit + args.ExtraSameMemberLimit = design.LocalParams.Memberlist.ExtraSameMemberLimit args.FetchCallbackBroadcastMessageFunc = quicmemberlist.FetchCallbackBroadcastMessageFunc( HandlerPrefixMemberlistCallbackBroadcastMessage, headerdial, @@ -85,13 +85,13 @@ func PMemberlist(pctx context.Context) (context.Context, error) { HandlerPrefixMemberlistEnsureBroadcastMessage, local.Address(), local.Privatekey(), - params.ISAAC.NetworkID(), + design.LocalParams.ISAAC.NetworkID(), headerdial, ) - args.BroadcastTimerMult = params.Memberlist.broadcastTimerMult - args.UserMsgLoopInterval = params.Memberlist.userMsgLoopInterval + args.BroadcastTimerMult = design.LocalParams.Memberlist.BroadcastTimerMult + args.UserMsgLoopInterval = design.LocalParams.Memberlist.UserMsgLoopInterval - m, err := quicmemberlist.NewMemberlist(localnode, args) + m, err := quicmemberlist.NewMemberlist(localNode, args) if err != nil { return pctx, e.Wrap(err) } @@ -174,7 +174,7 @@ func PPatchMemberlist(pctx context.Context) (ctx context.Context, err error) { func patchMemberlistNotifyMsg(pctx context.Context) (context.Context, error) { var log *logging.Logging - var isaacparams *isaac.Params + var design NodeDesign var ballotbox *isaacstates.Ballotbox var m *quicmemberlist.Memberlist var client *isaacnetwork.BaseClient @@ -184,7 +184,7 @@ func patchMemberlistNotifyMsg(pctx context.Context) (context.Context, error) { if err := util.LoadFromContextOK(pctx, LoggingContextKey, &log, - ISAACParamsContextKey, &isaacparams, + DesignContextKey, &design, BallotboxContextKey, &ballotbox, QuicstreamClientContextKey, &client, MemberlistContextKey, &m, @@ -223,7 +223,7 @@ func patchMemberlistNotifyMsg(pctx context.Context) (context.Context, error) { Stringer("node", t.SignFact().Node()). Msg("ballot notified") - if err := t.IsValid(isaacparams.NetworkID()); err != nil { + if err := t.IsValid(design.LocalParams.ISAAC.NetworkID()); err != nil { l.Trace().Err(err).Interface("ballot", t).Msg("new ballot; failed to vote") return @@ -388,7 +388,7 @@ func memberlistConfig( delegate := quicmemberlist.NewDelegate(localnode, nil, func([]byte) { panic("set notifyMsgFunc") - }, params.RetransmitMult()) + }, params.RetransmitMult) alive, err := memberlistAlive(pctx) if err != nil { diff --git a/launch/p_network.go b/launch/p_network.go index 204037b4..25a46513 100644 --- a/launch/p_network.go +++ b/launch/p_network.go @@ -64,12 +64,10 @@ func PNetwork(pctx context.Context) (context.Context, error) { var log *logging.Logging var design NodeDesign - var params *LocalParams if err := util.LoadFromContextOK(pctx, LoggingContextKey, &log, DesignContextKey, &design, - LocalParamsContextKey, ¶ms, ); err != nil { return pctx, e.Wrap(err) } @@ -77,15 +75,15 @@ func PNetwork(pctx context.Context) (context.Context, error) { handlers := quicstream.NewPrefixHandler(nil) _ = handlers.SetLogging(log) - quicconfig := ServerQuicConfig(params.Network) + quicConfig := ServerQuicConfig(design.LocalParams.Network) server, err := quicstream.NewServer( design.Network.Bind, - GenerateNewTLSConfig(params.ISAAC.NetworkID()), - quicconfig, + GenerateNewTLSConfig(design.LocalParams.ISAAC.NetworkID()), + quicConfig, handlers.Handler, func() time.Duration { - return params.Network.MaxStreamTimeout() + return design.LocalParams.Network.MaxStreamTimeout() }, ) if err != nil { diff --git a/launch/p_network_handlers.go b/launch/p_network_handlers.go index df235b24..43c33060 100644 --- a/launch/p_network_handlers.go +++ b/launch/p_network_handlers.go @@ -51,7 +51,6 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { var encs *encoder.Encoders var design NodeDesign var local base.LocalNode - var params *LocalParams var db isaac.Database var pool *isaacdatabase.TempPool var proposalMaker *isaac.ProposalMaker @@ -68,7 +67,6 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { EncodersContextKey, &encs, DesignContextKey, &design, LocalContextKey, &local, - LocalParamsContextKey, ¶ms, CenterDatabaseContextKey, &db, PoolDatabaseContextKey, &pool, ProposalMakerContextKey, &proposalMaker, @@ -83,7 +81,7 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { return pctx, e.Wrap(err) } - isaacparams := params.ISAAC + isaacparams := design.LocalParams.ISAAC lastBlockMapf := QuicstreamHandlerLastBlockMapFunc(db) suffrageNodeConnInfof := QuicstreamHandlerSuffrageNodeConnInfoFunc(db, m) @@ -194,7 +192,7 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { return err }, - params.MISC.MaxMessageSize, + design.LocalParams.MISC.MaxMessageSize, ), nil) if gerror != nil { @@ -543,7 +541,7 @@ func AttachBlockItemsNetworkHandlers(pctx context.Context) error { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return err default: diff --git a/launch/p_node_rw.go b/launch/p_node_rw.go index 03492d19..8cd7f089 100644 --- a/launch/p_node_rw.go +++ b/launch/p_node_rw.go @@ -18,15 +18,15 @@ import ( "github.com/ProtoconNet/mitum2/isaac" isaacnetwork "github.com/ProtoconNet/mitum2/isaac/network" isaacstates "github.com/ProtoconNet/mitum2/isaac/states" - "github.com/ProtoconNet/mitum2/network" + "github.com/ProtoconNet/mitum2/network/quicmemberlist" "github.com/ProtoconNet/mitum2/network/quicstream" quicstreamheader "github.com/ProtoconNet/mitum2/network/quicstream/header" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/ProtoconNet/mitum2/util/hint" "github.com/ProtoconNet/mitum2/util/logging" "github.com/ProtoconNet/mitum2/util/ps" - consulapi "github.com/hashicorp/consul/api" "github.com/pkg/errors" "github.com/rs/zerolog" "gopkg.in/yaml.v3" @@ -94,13 +94,13 @@ var ( func PNetworkHandlersReadWriteNode(pctx context.Context) (context.Context, error) { var design NodeDesign - var params *LocalParams + //var params *LocalParams var local base.LocalNode var eventLogging *EventLogging if err := util.LoadFromContextOK(pctx, DesignContextKey, &design, - LocalParamsContextKey, ¶ms, + //LocalParamsContextKey, ¶ms, LocalContextKey, &local, EventLoggingContextKey, &eventLogging, ); err != nil { @@ -133,11 +133,11 @@ func PNetworkHandlersReadWriteNode(pctx context.Context) (context.Context, error EnsureHandlerAdd(pctx, &gerror, HandlerNameNodeRead, - networkHandlerNodeRead(params.ISAAC.NetworkID(), rf, rl), nil) + networkHandlerNodeRead(design.LocalParams.ISAAC.NetworkID(), rf, rl), nil) EnsureHandlerAdd(pctx, &gerror, HandlerNameNodeWrite, - networkHandlerNodeWrite(params.ISAAC.NetworkID(), wf, wl), nil) + networkHandlerNodeWrite(design.LocalParams.ISAAC.NetworkID(), wf, wl), nil) return pctx, gerror } @@ -225,19 +225,21 @@ func writeStates(pctx context.Context) (writeNodeValueFunc, error) { func writeDesign(pctx context.Context) (writeNodeValueFunc, error) { var log *logging.Logging var flag DesignFlag + var encs *encoder.Encoders if err := util.LoadFromContextOK(pctx, LoggingContextKey, &log, DesignFlagContextKey, &flag, + EncodersContextKey, &encs, ); err != nil { return nil, err } - defaultReadDesignFileF := func() ([]byte, error) { return nil, errors.Errorf("design file; can not read") } + defaultReadDesignFileF := func() (*NodeDesign, error) { return nil, errors.Errorf("design file; can not read") } readDesignFileF := defaultReadDesignFileF writeDesignFileF := func([]byte) error { return errors.Errorf("design file; can not write") } - switch i, err := readDesignFileFunc(flag); { + switch i, err := readDesignFileFunc(flag, encs); { case err != nil: return nil, err case i == nil: @@ -268,7 +270,7 @@ func writeDesign(pctx context.Context) (writeNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -322,8 +324,14 @@ func writeDesignMap(pctx context.Context) (map[string]writeNodeValueFunc, error) "parameters.isaac.threshold": writeLocalParamISAACThreshold(params.ISAAC), "parameters.isaac.interval_broadcast_ballot": writeLocalParamISAACIntervalBroadcastBallot(params.ISAAC), "parameters.isaac.wait_preparing_init_ballot": writeLocalParamISAACWaitPreparingINITBallot(params.ISAAC), + "parameters.isaac.ballot_stuck_wait": writeLocalParamISAACBallotStuckWait(params.ISAAC), + "parameters.isaac.ballot_stuck_resolve_after": writeLocalParamISAACBallotStuckResolveAfter(params.ISAAC), "parameters.isaac.min_wait_next_block_init_ballot": writeLocalParamISAACMinWaitNextBlockINITBallot(params.ISAAC), + "parameters.isaac.syncer_last_block_map_interval": writeLocalParamISAACSyncerLastBlockMapInterval(params.ISAAC), + "parameters.isaac.min_proposer_wait": writeLocalParamISAACMinProposerWait(params.ISAAC), "parameters.isaac.max_try_handover_y_broker_sync_data": writeLocalParamISAACMaxTryHandoverYBrokerSyncData(params.ISAAC), + "parameters.isaac.state_cache_size": writeLocalParamISAACStateCacheSize(params.ISAAC), + "parameters.isaac.operation_pool_cache_size": writeLocalParamISAACOperationPoolCacheSize(params.ISAAC), "parameters.misc.sync_source_checker_interval": writeLocalParamMISCSyncSourceCheckerInterval(params.MISC), "parameters.misc.valid_proposal_operation_expire": writeLocalParamMISCValidProposalOperationExpire(params.MISC), @@ -332,7 +340,18 @@ func writeDesignMap(pctx context.Context) (map[string]writeNodeValueFunc, error) "parameters.misc.block_item_readers_remove_empty_interval": writeLocalParamMISCBlockItemReadersRemoveEmptyInterval(params.MISC), "parameters.misc.max_message_size": writeLocalParamMISCMaxMessageSize(params.MISC), - "parameters.memberlist.extra_same_member_limit": writeLocalParamExtraSameMemberLimit(params.Memberlist), + "parameters.memberlist.extra_same_member_limit": writeLocalParamMemberlistExtraSameMemberLimit(params.Memberlist), + "parameters.memberlist.tcp_timeout": writeLocalParamMemberlistTcpTimeout(params.Memberlist), + "parameters.memberlist.retransmit_mult": writeLocalParamMemberlistRetransmitMult(params.Memberlist), + "parameters.memberlist.probe_timeout": writeLocalParamMemberlistProbeTimeout(params.Memberlist), + "parameters.memberlist.probe_interval": writeLocalParamMemberlistProbeInterval(params.Memberlist), + "parameters.memberlist.gossip_interval": writeLocalParamMemberlistGossipInterval(params.Memberlist), + "parameters.memberlist.gossip_nodes": writeLocalParamMemberlistGossipNodes(params.Memberlist), + "parameters.memberlist.suspicion_mult": writeLocalParamMemberlistSuspicionMult(params.Memberlist), + "parameters.memberlist.suspicion_max_timeout_mult": writeLocalParamMemberlistSuspicionMaxTimeoutMult(params.Memberlist), + "parameters.memberlist.udp_buffer_size": writeLocalParamMemberlistUdpBufferSize(params.Memberlist), + "parameters.memberlist.broadcast_timer_mult": writeLocalParamMemberlistBroadcastTimerMult(params.Memberlist), + "parameters.memberlist.user_msg_loop_interval": writeLocalParamMemberlistUserMsgLoopInterval(params.Memberlist), "parameters.network.timeout_request": writeLocalParamNetworkTimeoutRequest(params.Network), "parameters.network.ratelimit.node": writeLocalParamNetworkRateLimit(params.Network.RateLimit(), "node"), @@ -424,6 +443,54 @@ func writeLocalParamISAACWaitPreparingINITBallot( }) } +func writeLocalParamISAACBallotStuckWait( + params *isaac.Params, +) writeNodeValueFunc { + return writeNodeKey(func( + _ context.Context, _, _, value, _ string, + ) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.BallotStuckWait() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetBallotStuckWait(d); err != nil { + return nil, nil, false, err + } + + return prev, params.BallotStuckWait(), true, nil + }) +} + +func writeLocalParamISAACBallotStuckResolveAfter( + params *isaac.Params, +) writeNodeValueFunc { + return writeNodeKey(func( + _ context.Context, _, _, value, _ string, + ) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.BallotStuckResolveAfter() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetBallotStuckResolveAfter(d); err != nil { + return nil, nil, false, err + } + + return prev, params.BallotStuckResolveAfter(), true, nil + }) +} + func writeLocalParamISAACMinWaitNextBlockINITBallot( params *isaac.Params, ) writeNodeValueFunc { @@ -448,6 +515,54 @@ func writeLocalParamISAACMinWaitNextBlockINITBallot( }) } +func writeLocalParamISAACSyncerLastBlockMapInterval( + params *isaac.Params, +) writeNodeValueFunc { + return writeNodeKey(func( + _ context.Context, _, _, value, _ string, + ) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.SyncerLastBlockMapInterval() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetSyncerLastBlockMapInterval(d); err != nil { + return nil, nil, false, err + } + + return prev, params.SyncerLastBlockMapInterval(), true, nil + }) +} + +func writeLocalParamISAACMinProposerWait( + params *isaac.Params, +) writeNodeValueFunc { + return writeNodeKey(func( + _ context.Context, _, _, value, _ string, + ) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.MinProposerWait() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetMinProposerWait(d); err != nil { + return nil, nil, false, err + } + + return prev, params.MinProposerWait(), true, nil + }) +} + func writeLocalParamISAACMaxTryHandoverYBrokerSyncData( params *isaac.Params, ) writeNodeValueFunc { @@ -472,8 +587,65 @@ func writeLocalParamISAACMaxTryHandoverYBrokerSyncData( }) } +func writeLocalParamISAACStateCacheSize( + params *isaac.Params, +) writeNodeValueFunc { + return writeNodeKey(func( + _ context.Context, _, _, value, _ string, + ) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + prev = params.StateCacheSize() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetStateCacheSize(i); err != nil { + return nil, nil, false, err + } + + return prev, params.StateCacheSize(), true, nil + }) +} + +func writeLocalParamISAACOperationPoolCacheSize( + params *isaac.Params, +) writeNodeValueFunc { + return writeNodeKey(func( + _ context.Context, _, _, value, _ string, + ) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.OperationPoolCacheSize() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetOperationPoolCacheSize(i); err != nil { + return nil, nil, false, err + } + + return prev, params.OperationPoolCacheSize(), true, nil + }) +} + func writeLocalParamMISCSyncSourceCheckerInterval( - params *MISCParams, + params *isaac.MISCParams, ) writeNodeValueFunc { return writeNodeKey(func( _ context.Context, _, _, value, _ string, @@ -497,7 +669,7 @@ func writeLocalParamMISCSyncSourceCheckerInterval( } func writeLocalParamMISCValidProposalOperationExpire( - params *MISCParams, + params *isaac.MISCParams, ) writeNodeValueFunc { return writeNodeKey(func( _ context.Context, _, _, value, _ string, @@ -521,7 +693,7 @@ func writeLocalParamMISCValidProposalOperationExpire( } func writeLocalParamMISCValidProposalSuffrageOperationsExpire( - params *MISCParams, + params *isaac.MISCParams, ) writeNodeValueFunc { return writeNodeKey(func( _ context.Context, _, _, value, _ string, @@ -545,7 +717,7 @@ func writeLocalParamMISCValidProposalSuffrageOperationsExpire( } func writeLocalParamMISCBlockItemReadersRemoveEmptyAfter( - params *MISCParams, + params *isaac.MISCParams, ) writeNodeValueFunc { return writeNodeKey(func( _ context.Context, _, _, value, _ string, @@ -569,7 +741,7 @@ func writeLocalParamMISCBlockItemReadersRemoveEmptyAfter( } func writeLocalParamMISCBlockItemReadersRemoveEmptyInterval( - params *MISCParams, + params *isaac.MISCParams, ) writeNodeValueFunc { return writeNodeKey(func( _ context.Context, _, _, value, _ string, @@ -593,7 +765,7 @@ func writeLocalParamMISCBlockItemReadersRemoveEmptyInterval( } func writeLocalParamMISCMaxMessageSize( - params *MISCParams, + params *isaac.MISCParams, ) writeNodeValueFunc { return writeNodeKey(func( _ context.Context, _, _, value, _ string, @@ -616,8 +788,8 @@ func writeLocalParamMISCMaxMessageSize( }) } -func writeLocalParamExtraSameMemberLimit( - params *MemberlistParams, +func writeLocalParamMemberlistExtraSameMemberLimit( + params *quicmemberlist.MemberlistParams, ) writeNodeValueFunc { return writeNodeKey( func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { @@ -639,6 +811,289 @@ func writeLocalParamExtraSameMemberLimit( }) } +func writeLocalParamMemberlistTcpTimeout( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.TCPTimeout() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetTCPTimeout(d); err != nil { + return nil, nil, false, err + } + + return prev, params.TCPTimeout(), true, nil + }) +} + +func writeLocalParamMemberlistRetransmitMult( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.RetransmitMult() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetRetransmitMult(i); err != nil { + return nil, nil, false, err + } + + return prev, params.RetransmitMult(), true, nil + }) +} + +func writeLocalParamMemberlistProbeTimeout( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.ProbeTimeout() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetProbeTimeout(d); err != nil { + return nil, nil, false, err + } + + return prev, params.ProbeTimeout(), true, nil + }) +} + +func writeLocalParamMemberlistProbeInterval( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.ProbeInterval() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetProbeInterval(d); err != nil { + return nil, nil, false, err + } + + return prev, params.ProbeInterval(), true, nil + }) +} + +func writeLocalParamMemberlistGossipInterval( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.GossipInterval() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetGossipInterval(d); err != nil { + return nil, nil, false, err + } + + return prev, params.GossipInterval(), true, nil + }) +} + +func writeLocalParamMemberlistGossipNodes( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.GosshipNodes() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetGosshipNodes(i); err != nil { + return nil, nil, false, err + } + + return prev, params.GosshipNodes(), true, nil + }) +} + +func writeLocalParamMemberlistSuspicionMult( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.SuspicionMult() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetSuspicionMult(i); err != nil { + return nil, nil, false, err + } + + return prev, params.SuspicionMult(), true, nil + }) +} + +func writeLocalParamMemberlistSuspicionMaxTimeoutMult( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.SuspicionMaxTimeoutMult() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetSuspicionMaxTimeoutMult(i); err != nil { + return nil, nil, false, err + } + + return prev, params.SuspicionMaxTimeoutMult(), true, nil + }) +} + +func writeLocalParamMemberlistUdpBufferSize( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.UDPBufferSize() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetUDPBufferSize(i); err != nil { + return nil, nil, false, err + } + + return prev, params.UDPBufferSize(), true, nil + }) +} + +func writeLocalParamMemberlistBroadcastTimerMult( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + var s string + if err := yaml.Unmarshal([]byte(value), &s); err != nil { + return nil, nil, false, errors.WithStack(err) + } + + i, err := strconv.Atoi(s) + if err != nil { + return nil, nil, false, errors.WithStack(err) + } + + prev = params.BroadcastTimerMult() + if prev == i { + return prev, nil, false, nil + } + + if err := params.SetBroadcastTimerMult(i); err != nil { + return nil, nil, false, err + } + + return prev, params.BroadcastTimerMult(), true, nil + }) +} + +func writeLocalParamMemberlistUserMsgLoopInterval( + params *quicmemberlist.MemberlistParams, +) writeNodeValueFunc { + return writeNodeKey( + func(_ context.Context, _, _, value, _ string) (prev, next interface{}, updated bool, _ error) { + d, err := parseNodeValueDuration(value) + if err != nil { + return nil, nil, false, err + } + + prev = params.UserMsgLoopInterval() + if prev == d { + return prev, nil, false, nil + } + + if err := params.SetUserMsgLoopInterval(d); err != nil { + return nil, nil, false, err + } + + return prev, params.UserMsgLoopInterval(), true, nil + }) +} + func writeLocalParamNetworkTimeoutRequest( params *NetworkParams, ) writeNodeValueFunc { @@ -705,7 +1160,7 @@ func writeDiscovery(pctx context.Context) (writeNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -727,11 +1182,11 @@ func writeDiscovery(pctx context.Context) (writeNodeValueFunc, error) { cis := make([]quicstream.ConnInfo, len(sl)) for i := range sl { - if err := network.IsValidAddr(sl[i]); err != nil { + if err := nutil.IsValidAddr(sl[i]); err != nil { return nil, nil, false, e.Wrap(err) } - addr, tlsinsecure := network.ParseTLSInsecure(sl[i]) + addr, tlsinsecure := nutil.ParseTLSInsecure(sl[i]) ci, err := quicstream.NewConnInfoFromStringAddr(addr, tlsinsecure) if err != nil { @@ -819,7 +1274,7 @@ func writeAllowConsensus(pctx context.Context) (writeNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -866,7 +1321,7 @@ func writeACL(pctx context.Context) (writeNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -916,7 +1371,7 @@ func writeBlockItemFiles(pctx context.Context) (writeNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -1393,27 +1848,29 @@ func readDesign(pctx context.Context) (readNodeValueFunc, error) { var design NodeDesign var log *logging.Logging var flag DesignFlag + var encs *encoder.Encoders if err := util.LoadFromContextOK(pctx, DesignContextKey, &design, LoggingContextKey, &log, DesignFlagContextKey, &flag, + EncodersContextKey, &encs, ); err != nil { return nil, err } var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: aclallow = i } - readDesignFileF := func() ([]byte, error) { return nil, errors.Errorf("design file; can not read") } + readDesignFileF := func() (*NodeDesign, error) { return nil, errors.Errorf("design file; can not read") } - switch i, err := readDesignFileFunc(flag); { + switch i, err := readDesignFileFunc(flag, encs); { case err != nil: return nil, err case i == nil: @@ -1464,7 +1921,7 @@ func readAllowConsensus(pctx context.Context) (readNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -1494,7 +1951,7 @@ func readDiscovery(pctx context.Context) (readNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -1524,7 +1981,7 @@ func readACL(pctx context.Context) (readNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -1561,7 +2018,7 @@ func readBlockItemFiles(pctx context.Context) (readNodeValueFunc, error) { var aclallow ACLAllowFunc - switch i, err := pACLAllowFunc(pctx); { + switch i, err := PACLAllowFunc(pctx); { case err != nil: return nil, err default: @@ -1861,63 +2318,195 @@ func omapFromDesignMap(m *util.YAMLOrderedMap, key string) (lastkey string, _ *u return l[len(l)-1], p, nil } -func updateDesignMap(m *util.YAMLOrderedMap, key string, value interface{}) error { - switch k, i, err := omapFromDesignMap(m, key); { - case err != nil: - return err +func updateDesignMap(src []byte, dotPath string, newVal any) ([]byte, error) { + var root yaml.Node + if err := yaml.Unmarshal(src, &root); err != nil { + return nil, fmt.Errorf("unmarshal: %w", err) + } + if len(root.Content) == 0 { + return nil, fmt.Errorf("empty YAML document") + } + doc := root.Content[0] + if doc.Kind != yaml.MappingNode { + return nil, fmt.Errorf("top-level must be a mapping node") + } + + parts := strings.Split(dotPath, ".") + cur := doc + + for i, p := range parts { + last := i == len(parts)-1 + ki, val, found := findPair(cur, p) + + if !found { + return nil, fmt.Errorf("key %q not found in path %q", p, dotPath) + } + + if last { + nv, err := coerceForExistingScalar(val, newVal) + if err != nil { + return nil, fmt.Errorf("set %q: %w", p, err) + } + cur.Content[ki+1] = nv + break + } + + if val.Kind != yaml.MappingNode { + val.Kind, val.Tag, val.Content = yaml.MappingNode, "!!map", nil + } + cur = val + } + + var out bytes.Buffer + enc := yaml.NewEncoder(&out) + enc.SetIndent(2) + if err := enc.Encode(&root); err != nil { + return nil, fmt.Errorf("encode: %w", err) + } + _ = enc.Close() + return out.Bytes(), nil +} + +func findPair(m *yaml.Node, key string) (int, *yaml.Node, bool) { + if m.Kind != yaml.MappingNode { + return -1, nil, false + } + for i := 0; i+1 < len(m.Content); i += 2 { + if k := m.Content[i]; k.Kind == yaml.ScalarNode && k.Value == key { + return i, m.Content[i+1], true + } + } + return -1, nil, false +} + +func coerceForExistingScalar(existing *yaml.Node, v any) (*yaml.Node, error) { + if existing.Kind != yaml.ScalarNode { + return nil, fmt.Errorf("existing value is not a scalar (kind=%v)", existing.Kind) + } + switch existing.Tag { + case "!!str": + return &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: fmt.Sprint(v)}, nil + case "!!int": + iv, err := coerceInt(v) + if err != nil { + return nil, err + } + return &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!int", Value: strconv.Itoa(iv)}, nil + case "!!bool": + bv, err := coerceBool(v) + if err != nil { + return nil, err + } + return &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!bool", Value: strconv.FormatBool(bv)}, nil default: - _ = i.Set(k, value) + return nil, fmt.Errorf("unsupported scalar tag %q at value %q", existing.Tag, existing.Value) + } +} - return nil +func coerceInt(v any) (int, error) { + switch t := v.(type) { + case int: + return t, nil + case int64: + return int(t), nil + case string: + x, err := strconv.Atoi(t) + if err != nil { + return 0, fmt.Errorf("cannot parse %q as int", t) + } + return x, nil + default: + return 0, fmt.Errorf("type %T cannot be coerced to int", v) + } +} + +func coerceBool(v any) (bool, error) { + switch t := v.(type) { + case bool: + return t, nil + case string: + switch strings.ToLower(t) { + case "true": + return true, nil + case "false": + return false, nil + default: + return false, fmt.Errorf("cannot parse %q as bool", t) + } + default: + return false, fmt.Errorf("type %T cannot be coerced to bool", v) } } func writeDesignFile( key string, value interface{}, - read func() ([]byte, error), + read func() (*NodeDesign, error), write func([]byte) error, ) error { - var m *util.YAMLOrderedMap + var nd *NodeDesign + var b []byte + var err error - switch b, err := read(); { + switch nd, err = read(); { case err != nil: return err default: - if err := yaml.Unmarshal(b, &m); err != nil { + if b, err = yaml.Marshal(nd.MarshalYAML()); err != nil { return errors.WithStack(err) } } - if err := updateDesignMap(m, key, value); err != nil { + nb, err := updateDesignMap(b, key, value) + if err != nil { return err } - switch b, err := yaml.Marshal(m); { - case err != nil: - return errors.WithStack(err) - default: - return write(b) - } + return write(nb) } -func readDesignFileFunc(flag DesignFlag) (func() ([]byte, error), error) { +func readDesignFileFunc(flag DesignFlag, encs *encoder.Encoders) (func() (*NodeDesign, error), error) { + var design NodeDesign + var rErr error + switch flag.Scheme() { case "file": - return func() ([]byte, error) { - b, err := os.ReadFile(flag.URL().Path) + return func() (*NodeDesign, error) { + switch d, _, err := NodeDesignFromFile(flag.URL().Path, encs.JSON()); { + case err != nil: + return nil, errors.WithStack(err) + default: + design = d + rErr = nil + } - return b, errors.WithStack(err) + return &design, errors.WithStack(rErr) }, nil + case "http", "https": - return func() ([]byte, error) { - return getFromHTTP(flag.URL().String(), flag.Properties().HTTPSTLSInsecure) + return func() (*NodeDesign, error) { + switch d, _, err := NodeDesignFromHTTP(flag.URL().Path, flag.Properties().HTTPSTLSInsecure, encs.JSON()); { + case err != nil: + return nil, errors.WithStack(err) + default: + design = d + } + + return &design, errors.WithStack(rErr) }, nil case "consul": - return func() ([]byte, error) { - return getFromConsul(flag.URL().Host, flag.URL().Path) + return func() (*NodeDesign, error) { + switch d, _, err := NodeDesignFromConsul(flag.URL().Host, flag.URL().Path, encs.JSON()); { + case err != nil: + return nil, errors.WithStack(err) + default: + design = d + } + + return &design, errors.WithStack(rErr) }, nil + default: - return nil, errors.Errorf("unknown design uri, %q", flag.URL()) + return nil, errors.WithStack(errors.Errorf("unknown design uri, %q", flag.URL())) } } @@ -1951,16 +2540,7 @@ func writeDesignFileFunc(flag DesignFlag) (func([]byte) error, error) { return nil, nil case "consul": return func(b []byte) error { - switch client, err := consulClient(flag.URL().Host); { - case err != nil: - return err - default: - kv := &consulapi.KVPair{Key: flag.URL().Path, Value: b} - - _, err = client.KV().Put(kv, nil) - - return errors.WithStack(err) - } + return WriteConsul(b, flag) }, nil default: return nil, errors.Errorf("unknown design uri, %q", flag.URL()) diff --git a/launch/p_nodeinfo.go b/launch/p_nodeinfo.go index f4f27b1e..0a5b2305 100644 --- a/launch/p_nodeinfo.go +++ b/launch/p_nodeinfo.go @@ -7,7 +7,7 @@ import ( "github.com/ProtoconNet/mitum2/isaac" isaacnetwork "github.com/ProtoconNet/mitum2/isaac/network" isaacstates "github.com/ProtoconNet/mitum2/isaac/states" - "github.com/ProtoconNet/mitum2/network" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/logging" "github.com/ProtoconNet/mitum2/util/ps" @@ -20,12 +20,11 @@ var ( ) func PNodeInfo(pctx context.Context) (context.Context, error) { - e := util.StringError("prepare nodeinfo") + e := util.StringError("prepare nodeInfo") var log *logging.Logging var version util.Version var local base.LocalNode - var isaacparams *isaac.Params var design NodeDesign var db isaac.Database @@ -34,28 +33,30 @@ func PNodeInfo(pctx context.Context) (context.Context, error) { VersionContextKey, &version, DesignContextKey, &design, LocalContextKey, &local, - ISAACParamsContextKey, &isaacparams, CenterDatabaseContextKey, &db, ); err != nil { return pctx, e.Wrap(err) } - nodeinfo := isaacnetwork.NewNodeInfoUpdater(design.NetworkID, local, version) - _ = nodeinfo.SetConsensusState(isaacstates.StateBooting) - _ = nodeinfo.SetConnInfo(network.ConnInfoToString( + nodeInfo := isaacnetwork.NewNodeInfoUpdater(design.NetworkID, local, version) + _ = nodeInfo.SetConsensusState(isaacstates.StateBooting) + _ = nodeInfo.SetConnInfo(nutil.ConnInfoToString( design.Network.PublishString, design.Network.TLSInsecure, )) - _ = nodeinfo.SetLocalParams(isaacparams) - nctx := context.WithValue(pctx, NodeInfoContextKey, nodeinfo) + _ = nodeInfo.SetIsaacParams(design.LocalParams.ISAAC) + _ = nodeInfo.SetMemberlistParams(design.LocalParams.Memberlist) + _ = nodeInfo.SetMISCParams(design.LocalParams.MISC) - switch err := UpdateNodeInfoWithNewBlock(db, nodeinfo); { + nctx := context.WithValue(pctx, NodeInfoContextKey, nodeInfo) + + switch err := UpdateNodeInfoWithNewBlock(db, nodeInfo); { case err == nil: case errors.Is(err, util.ErrNotFound): - log.Log().Debug().Err(err).Msg("nodeinfo not updated") + log.Log().Debug().Err(err).Msg("nodeInfo not updated") default: - log.Log().Error().Err(err).Msg("failed to update nodeinfo") + log.Log().Error().Err(err).Msg("failed to update nodeInfo") } return nctx, nil diff --git a/launch/p_proposal.go b/launch/p_proposal.go index b085e2eb..3bec65dc 100644 --- a/launch/p_proposal.go +++ b/launch/p_proposal.go @@ -73,7 +73,6 @@ func newProposalProcessorFunc(pctx context.Context) ( var encs *encoder.Encoders var design NodeDesign var local base.LocalNode - var isaacparams *isaac.Params var db isaac.Database var oprs *hint.CompatibleSet[isaac.NewOperationProcessorInternalFunc] @@ -81,7 +80,6 @@ func newProposalProcessorFunc(pctx context.Context) ( EncodersContextKey, &encs, DesignContextKey, &design, LocalContextKey, &local, - ISAACParamsContextKey, &isaacparams, CenterDatabaseContextKey, &db, OperationProcessorsMapContextKey, &oprs, ); err != nil { @@ -100,13 +98,13 @@ func newProposalProcessorFunc(pctx context.Context) ( args.MaxWorkerSize = math.MaxInt16 args.NewWriterFunc = NewBlockWriterFunc( local, - isaacparams.NetworkID(), + design.LocalParams.ISAAC.NetworkID(), LocalFSDataDirectory(design.Storage.Base), encs.JSON(), encs.Default(), db, args.MaxWorkerSize, - isaacparams.StateCacheSize(), + design.LocalParams.ISAAC.StateCacheSize(), ) args.GetStateFunc = db.State args.GetOperationFunc = getProposalOperationFuncf(proposal) diff --git a/launch/p_states.go b/launch/p_states.go index 0e26d7c3..c4baf274 100644 --- a/launch/p_states.go +++ b/launch/p_states.go @@ -119,9 +119,9 @@ func PBallotStuckResolver(pctx context.Context) (context.Context, error) { } r := isaacstates.NewDefaultBallotStuckResolver( - isaacparams.BallotStuckWait(), + isaacparams.BallotStuckWait, time.Second, - isaacparams.BallotStuckResolveAfter(), + isaacparams.BallotStuckResolveAfter, findMissingBallotsf, requestMissingBallotsf, voteSuffrageVotingf, @@ -169,7 +169,7 @@ func PStates(pctx context.Context) (context.Context, error) { args.IntervalBroadcastBallot = isaacparams.IntervalBroadcastBallot args.AllowConsensus = devflags.AllowConsensus args.BroadcastTimerMult = func() int { - return design.LocalParams.Memberlist.broadcastTimerMult + return design.LocalParams.Memberlist.BroadcastTimerMult() } if vp := args.LastVoteproofsHandler.Last().Cap(); vp != nil { diff --git a/launch/p_storage.go b/launch/p_storage.go index df9888d7..afd97225 100644 --- a/launch/p_storage.go +++ b/launch/p_storage.go @@ -312,20 +312,18 @@ func PCreateLocalFS(pctx context.Context) (context.Context, error) { var design NodeDesign var encs *encoder.Encoders - var isaacparams *isaac.Params var version util.Version if err := util.LoadFromContextOK(pctx, DesignContextKey, &design, EncodersContextKey, &encs, - ISAACParamsContextKey, &isaacparams, VersionContextKey, &version, ); err != nil { return pctx, e.Wrap(err) } fsnodeinfo, err := CreateLocalFS( - CreateDefaultNodeInfo(isaacparams.NetworkID(), version), design.Storage.Base, encs.Default()) + CreateDefaultNodeInfo(design.LocalParams.ISAAC.NetworkID(), version), design.Storage.Base, encs.Default()) if err != nil { return pctx, e.Wrap(err) } @@ -337,18 +335,16 @@ func PCheckLocalFS(pctx context.Context) (context.Context, error) { e := util.StringError("check local fs") var design NodeDesign - var isaacparams *isaac.Params var encs *encoder.Encoders if err := util.LoadFromContextOK(pctx, DesignContextKey, &design, EncodersContextKey, &encs, - ISAACParamsContextKey, &isaacparams, ); err != nil { return pctx, e.Wrap(err) } - fsnodeinfo, err := CheckLocalFS(isaacparams.NetworkID(), design.Storage.Base, encs.Default()) + fsnodeinfo, err := CheckLocalFS(design.LocalParams.ISAAC.NetworkID(), design.Storage.Base, encs.Default()) switch { case err == nil: @@ -369,19 +365,17 @@ func PCheckAndCreateLocalFS(pctx context.Context) (context.Context, error) { var version util.Version var design NodeDesign - var isaacparams *isaac.Params var encs *encoder.Encoders if err := util.LoadFromContextOK(pctx, VersionContextKey, &version, DesignContextKey, &design, EncodersContextKey, &encs, - ISAACParamsContextKey, &isaacparams, ); err != nil { return pctx, e.Wrap(err) } - fsnodeinfo, err := CheckLocalFS(isaacparams.NetworkID(), design.Storage.Base, encs.Default()) + fsnodeinfo, err := CheckLocalFS(design.LocalParams.ISAAC.NetworkID(), design.Storage.Base, encs.Default()) switch { case err == nil: @@ -391,7 +385,7 @@ func PCheckAndCreateLocalFS(pctx context.Context) (context.Context, error) { case errors.Is(err, os.ErrNotExist): // NOTE database will be no cleaned. fsnodeinfo, err = CreateLocalFS( - CreateDefaultNodeInfo(isaacparams.NetworkID(), version), design.Storage.Base, encs.Default()) + CreateDefaultNodeInfo(design.LocalParams.ISAAC.NetworkID(), version), design.Storage.Base, encs.Default()) if err != nil { return pctx, e.Wrap(err) } @@ -407,14 +401,12 @@ func PLoadDatabase(pctx context.Context) (context.Context, error) { var log *logging.Logging var design NodeDesign - var isaacparams *isaac.Params var encs *encoder.Encoders var fsnodeinfo NodeInfo if err := util.LoadFromContextOK(pctx, LoggingContextKey, &log, DesignContextKey, &design, - ISAACParamsContextKey, &isaacparams, EncodersContextKey, &encs, FSNodeInfoContextKey, &fsnodeinfo, ); err != nil { @@ -427,8 +419,8 @@ func PLoadDatabase(pctx context.Context) (context.Context, error) { design.Storage.Base, encs, encs.Default(), - isaacparams.StateCacheSize(), - isaacparams.OperationPoolCacheSize(), + design.LocalParams.ISAAC.StateCacheSize(), + design.LocalParams.ISAAC.OperationPoolCacheSize(), ) if err != nil { return pctx, e.Wrap(err) @@ -453,7 +445,6 @@ func PCheckBlocksOfStorage(pctx context.Context) (context.Context, error) { var log *logging.Logging var design NodeDesign var encs *encoder.Encoders - var isaacparams *isaac.Params var db isaac.Database var newReaders func(context.Context, string, *isaac.BlockItemReadersArgs) (*isaac.BlockItemReaders, error) var fromRemotes isaac.RemotesBlockItemReadFunc @@ -462,7 +453,6 @@ func PCheckBlocksOfStorage(pctx context.Context) (context.Context, error) { LoggingContextKey, &log, DesignContextKey, &design, EncodersContextKey, &encs, - ISAACParamsContextKey, &isaacparams, CenterDatabaseContextKey, &db, NewBlockItemReadersFuncContextKey, &newReaders, RemotesBlockItemReaderFuncContextKey, &fromRemotes, @@ -481,7 +471,7 @@ func PCheckBlocksOfStorage(pctx context.Context) (context.Context, error) { readers = i } - if err := isaacblock.IsValidLastBlocks(readers, fromRemotes, db, isaacparams.NetworkID()); err != nil { + if err := isaacblock.IsValidLastBlocks(readers, fromRemotes, db, design.LocalParams.ISAAC.NetworkID()); err != nil { var derr isaacblock.ErrValidatedDifferentHeightBlockMaps if errors.As(err, &derr) { l := log.Log().With().Err(err). diff --git a/launch/p_sync.go b/launch/p_sync.go index 12f5ba4d..01cd21b5 100644 --- a/launch/p_sync.go +++ b/launch/p_sync.go @@ -27,7 +27,6 @@ func PSyncSourceChecker(pctx context.Context) (context.Context, error) { var encs *encoder.Encoders var design NodeDesign var local base.LocalNode - var params *LocalParams var client isaac.NetworkClient if err := util.LoadFromContextOK(pctx, @@ -35,7 +34,6 @@ func PSyncSourceChecker(pctx context.Context) (context.Context, error) { EncodersContextKey, &encs, DesignContextKey, &design, LocalContextKey, &local, - LocalParamsContextKey, ¶ms, QuicstreamClientContextKey, &client, ); err != nil { return pctx, e.Wrap(err) @@ -54,9 +52,9 @@ func PSyncSourceChecker(pctx context.Context) (context.Context, error) { syncSourceChecker := isaacnetwork.NewSyncSourceChecker( local, - params.ISAAC.NetworkID(), + design.LocalParams.ISAAC.NetworkID(), client, - params.MISC.SyncSourceCheckerInterval(), + design.LocalParams.MISC.SyncSourceCheckerInterval(), encs.Default(), sources, func(ncis []isaac.NodeConnInfo, _ error) { @@ -66,7 +64,7 @@ func PSyncSourceChecker(pctx context.Context) (context.Context, error) { Interface("node_conninfo", ncis). Msg("sync sources updated") }, - params.Network.TimeoutRequest, + design.LocalParams.Network.TimeoutRequest, ) _ = syncSourceChecker.SetLogging(log) diff --git a/launch/ratelimit.go b/launch/ratelimit.go index 5b425fb1..d57cdd01 100644 --- a/launch/ratelimit.go +++ b/launch/ratelimit.go @@ -657,6 +657,10 @@ func (m RateLimiterRuleMap) IsEmpty() bool { return m.d == nil && len(m.m) < 1 } +func (m *RateLimiterRuleMap) RuleMap() map[string]RateLimiterRule { + return m.m +} + func (m RateLimiterRuleMap) Rule(handler string) (rule RateLimiterRule, found bool) { switch i, found := m.rule(handler); { case found: @@ -712,6 +716,8 @@ func (rs NetRateLimiterRuleSet) IsValid([]byte) error { return nil } +func (rs *NetRateLimiterRuleSet) Rules() map[string]RateLimiterRuleMap { return rs.rules } + func (rs NetRateLimiterRuleSet) UpdatedAt() int64 { return rs.updatedAt } @@ -803,6 +809,10 @@ func NewNodeRateLimiterRuleSet( return NodeRateLimiterRuleSet{StringKeyRateLimiterRuleSet: newStringKeyRateLimiterRuleSet(rules)} } +func (rs *NodeRateLimiterRuleSet) Rules() map[string]RateLimiterRuleMap { + return rs.rules +} + func (rs NodeRateLimiterRuleSet) Rule( _ net.Addr, handler string, hint RateLimitRuleHint, ) (_ string, _ RateLimiterRule, _ string, _ bool) { @@ -843,6 +853,10 @@ func (rs *SuffrageRateLimiterRuleSet) UpdatedAt() int64 { return rs.updatedAt } +func (rs *SuffrageRateLimiterRuleSet) Rules() RateLimiterRuleMap { + return rs.rules +} + func (rs *SuffrageRateLimiterRuleSet) Rule( _ net.Addr, handler string, hint RateLimitRuleHint, ) (statehash string, rule RateLimiterRule, _ string, _ bool) { diff --git a/network/conn.go b/network/conn.go index 61216743..02d52136 100644 --- a/network/conn.go +++ b/network/conn.go @@ -3,8 +3,6 @@ package network import ( "fmt" "net" - "net/url" - "strings" "github.com/ProtoconNet/mitum2/util" "github.com/rs/zerolog" @@ -17,37 +15,6 @@ type ConnInfo interface { TLSInsecure() bool } -var DefaultTLSInsecureFlag = "tls_insecure" - -func HasTLSInsecure(s, flag string) bool { - v, err := url.ParseQuery(s) - if err != nil { - return false - } - - return v.Has(flag) -} - -func ParseTLSInsecure(s string) (string, bool) { - switch i := strings.Index(s, "#"); { - case i < 0: - return s, false - case len(s[i:]) > 0: - return s[:i], HasTLSInsecure(s[i+1:], DefaultTLSInsecureFlag) - default: - return s[:i], false - } -} - -func ConnInfoToString(addr string, tlsinsecure bool) string { // revive:disable-line:flag-parameter - ti := "" - if tlsinsecure { - ti = "#tls_insecure" - } - - return addr + ti -} - func EqualConnInfo(a, b ConnInfo) bool { switch { case a == nil, b == nil: diff --git a/network/quicmemberlist/conn.go b/network/quicmemberlist/conn.go index 47705287..c8b971fa 100644 --- a/network/quicmemberlist/conn.go +++ b/network/quicmemberlist/conn.go @@ -8,8 +8,8 @@ import ( "sync" "time" - "github.com/ProtoconNet/mitum2/network" "github.com/ProtoconNet/mitum2/network/quicstream" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/pkg/errors" ) @@ -65,7 +65,7 @@ func (c NamedConnInfo) TLSInsecure() bool { func (c NamedConnInfo) IsValid([]byte) error { e := util.ErrInvalid.Errorf("invalid NamedConnInfo") - if err := network.IsValidAddr(c.addr.String()); err != nil { + if err := nutil.IsValidAddr(c.addr.String()); err != nil { return e.Wrap(err) } @@ -77,7 +77,7 @@ func (c NamedConnInfo) IsValid([]byte) error { } func (c NamedConnInfo) String() string { - return network.ConnInfoToString(c.addr.String(), c.TLSInsecure()) + return nutil.ConnInfoToString(c.addr.String(), c.TLSInsecure()) } func (c NamedConnInfo) MarshalText() ([]byte, error) { @@ -85,7 +85,7 @@ func (c NamedConnInfo) MarshalText() ([]byte, error) { } func (c *NamedConnInfo) UnmarshalText(b []byte) error { - addr, tlsinsecure := network.ParseTLSInsecure(string(b)) + addr, tlsinsecure := nutil.ParseTLSInsecure(string(b)) nci, err := NewNamedConnInfo(addr, tlsinsecure) if err != nil { diff --git a/network/quicmemberlist/delegate.go b/network/quicmemberlist/delegate.go index 5afe6c68..338282f3 100644 --- a/network/quicmemberlist/delegate.go +++ b/network/quicmemberlist/delegate.go @@ -27,16 +27,17 @@ type Delegate struct { local Member notifyMsgFunc func(b []byte) *logging.Logging - qu *memberlist.TransmitLimitedQueue + qu *memberlist.TransmitLimitedQueue + reTransmitMult func() int } func NewDelegate( local Member, numNodes func() int, notifyMsgFunc func(b []byte), - reTransmitMult int, + reTransmitMult func() int, ) *Delegate { - qu := &memberlist.TransmitLimitedQueue{NumNodes: numNodes, RetransmitMult: reTransmitMult} + qu := &memberlist.TransmitLimitedQueue{NumNodes: numNodes, RetransmitMult: reTransmitMult()} if notifyMsgFunc == nil { notifyMsgFunc = func([]byte) {} //revive:disable-line:modifies-parameter @@ -46,9 +47,10 @@ func NewDelegate( Logging: logging.NewLogging(func(zctx zerolog.Context) zerolog.Context { return zctx.Str("module", "memberlist-delegate") }), - local: local, - qu: qu, - notifyMsgFunc: notifyMsgFunc, + local: local, + qu: qu, + notifyMsgFunc: notifyMsgFunc, + reTransmitMult: reTransmitMult, } } @@ -67,6 +69,7 @@ func (d *Delegate) QueueBroadcast(b memberlist.Broadcast) { } func (d *Delegate) GetBroadcasts(overhead, limit int) [][]byte { + d.qu.RetransmitMult = d.reTransmitMult() return d.qu.GetBroadcasts(overhead, limit) } diff --git a/network/quicmemberlist/member.go b/network/quicmemberlist/member.go index 010b45d9..352bec2e 100644 --- a/network/quicmemberlist/member.go +++ b/network/quicmemberlist/member.go @@ -6,8 +6,8 @@ import ( "time" "github.com/ProtoconNet/mitum2/base" - "github.com/ProtoconNet/mitum2/network" "github.com/ProtoconNet/mitum2/network/quicstream" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/encoder" "github.com/ProtoconNet/mitum2/util/hint" @@ -262,7 +262,7 @@ func (n memberMeta) IsValid([]byte) error { n.address, n.publickey, util.DummyIsValider(func([]byte) error { - return network.IsValidAddr(n.publish) + return nutil.IsValidAddr(n.publish) }), ); err != nil { return e.Wrap(err) diff --git a/network/quicmemberlist/memberlist.go b/network/quicmemberlist/memberlist.go index 1a39ff0c..0faa3231 100644 --- a/network/quicmemberlist/memberlist.go +++ b/network/quicmemberlist/memberlist.go @@ -54,8 +54,8 @@ type MemberlistArgs struct { FetchCallbackBroadcastMessageTimeout time.Duration PongEnsureBroadcastMessageTimeout time.Duration PongEnsureBroadcastMessageExpire time.Duration - UserMsgLoopInterval time.Duration - BroadcastTimerMult int + UserMsgLoopInterval func() time.Duration + BroadcastTimerMult func() int } func NewMemberlistArgs(jsonencoder encoder.Encoder, config *memberlist.Config) *MemberlistArgs { @@ -939,7 +939,7 @@ func (srv *Memberlist) broadcastEnsured(id string, threshold base.Threshold, exc func (srv *Memberlist) loopUserMsgs(ctx context.Context) { sem := semaphore.NewWeighted(maxHandleUserMsg) - ticker := time.NewTicker(srv.args.UserMsgLoopInterval) + ticker := time.NewTicker(srv.args.UserMsgLoopInterval()) defer ticker.Stop() for { diff --git a/network/quicmemberlist/params.go b/network/quicmemberlist/params.go new file mode 100644 index 00000000..05059fd1 --- /dev/null +++ b/network/quicmemberlist/params.go @@ -0,0 +1,392 @@ +package quicmemberlist + +import ( + "reflect" + "time" + + "github.com/ProtoconNet/mitum2/util" + "github.com/ProtoconNet/mitum2/util/hint" + "gopkg.in/yaml.v3" +) + +var ParamsHint = hint.MustNewHint("memberlist-params-v0.0.1") + +type MemberlistParams struct { + *util.BaseParams + hint.BaseHinter + tcpTimeout time.Duration + retransmitMult int + probeTimeout time.Duration + probeInterval time.Duration + gossipInterval time.Duration + suspicionMult int + suspicionMaxTimeoutMult int + udpBufferSize int + extraSameMemberLimit uint64 + broadcastTimerMult int + userMsgLoopInterval time.Duration + gosshipNodes int +} + +func DefaultMemberlistParams() *MemberlistParams { + config := BasicMemberlistConfig() + + return &MemberlistParams{ + BaseParams: util.NewBaseParams(), + BaseHinter: hint.NewBaseHinter(ParamsHint), + tcpTimeout: config.TCPTimeout, + retransmitMult: config.RetransmitMult, + probeTimeout: config.ProbeTimeout, + probeInterval: config.ProbeInterval, + gossipInterval: config.GossipInterval, + suspicionMult: config.SuspicionMult, + suspicionMaxTimeoutMult: config.SuspicionMaxTimeoutMult, + udpBufferSize: config.UDPBufferSize, + extraSameMemberLimit: 1, //nolint:gomnd //... + broadcastTimerMult: 5, + userMsgLoopInterval: time.Millisecond * 33, + gosshipNodes: config.GossipNodes, + } +} + +func (*MemberlistParams) IsValid([]byte) error { + return nil +} + +func (p *MemberlistParams) TCPTimeout() time.Duration { + return p.tcpTimeout +} + +func (p *MemberlistParams) SetTCPTimeout(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.tcpTimeout == d { + return false, nil + } + + p.tcpTimeout = d + + return true, nil + }) +} + +func (p *MemberlistParams) RetransmitMult() int { + return p.retransmitMult +} + +func (p *MemberlistParams) SetRetransmitMult(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.retransmitMult == d { + return false, nil + } + + p.retransmitMult = d + + return true, nil + }) +} + +func (p *MemberlistParams) ProbeTimeout() time.Duration { + return p.probeTimeout +} + +func (p *MemberlistParams) SetProbeTimeout(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.probeTimeout == d { + return false, nil + } + + p.probeTimeout = d + + return true, nil + }) +} + +func (p *MemberlistParams) ProbeInterval() time.Duration { + return p.probeInterval +} + +func (p *MemberlistParams) SetProbeInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.probeInterval == d { + return false, nil + } + + p.probeInterval = d + + return true, nil + }) +} + +func (p *MemberlistParams) GossipInterval() time.Duration { + return p.gossipInterval +} + +func (p *MemberlistParams) SetGossipInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.gossipInterval == d { + return false, nil + } + + p.gossipInterval = d + + return true, nil + }) +} + +func (p *MemberlistParams) GosshipNodes() int { + return p.gosshipNodes +} + +func (p *MemberlistParams) SetGosshipNodes(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.gosshipNodes == d { + return false, nil + } + + p.gosshipNodes = d + + return true, nil + }) +} + +func (p *MemberlistParams) SuspicionMult() int { + return p.suspicionMult +} + +func (p *MemberlistParams) SetSuspicionMult(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.suspicionMult == d { + return false, nil + } + + p.suspicionMult = d + + return true, nil + }) +} + +func (p *MemberlistParams) SuspicionMaxTimeoutMult() int { + return p.suspicionMaxTimeoutMult +} + +func (p *MemberlistParams) SetSuspicionMaxTimeoutMult(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.suspicionMaxTimeoutMult == d { + return false, nil + } + + p.suspicionMaxTimeoutMult = d + + return true, nil + }) +} + +func (p *MemberlistParams) UDPBufferSize() int { + return p.udpBufferSize +} + +func (p *MemberlistParams) SetUDPBufferSize(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.udpBufferSize == d { + return false, nil + } + + p.udpBufferSize = d + + return true, nil + }) +} + +func (p *MemberlistParams) ExtraSameMemberLimit() uint64 { + return p.extraSameMemberLimit +} + +func (p *MemberlistParams) SetExtraSameMemberLimit(d uint64) error { + return p.SetUint64(d, func(d uint64) (bool, error) { + if p.extraSameMemberLimit == d { + return false, nil + } + + p.extraSameMemberLimit = d + + return true, nil + }) +} + +func (p *MemberlistParams) BroadcastTimerMult() int { + return p.broadcastTimerMult +} + +func (p *MemberlistParams) SetBroadcastTimerMult(d int) error { + return p.SetOverZeroInt(d, func(d int) (bool, error) { + if p.broadcastTimerMult == d { + return false, nil + } + + p.broadcastTimerMult = d + + return true, nil + }) +} + +func (p *MemberlistParams) UserMsgLoopInterval() time.Duration { + return p.userMsgLoopInterval +} + +func (p *MemberlistParams) SetUserMsgLoopInterval(d time.Duration) error { + return p.SetDuration(d, func(d time.Duration) (bool, error) { + if p.userMsgLoopInterval == d { + return false, nil + } + + p.userMsgLoopInterval = d + + return true, nil + }) +} + +type memberlistParamsMarshaler struct { + //revive:disable:line-length-limit + hint.BaseHinter + TCPTimeout util.ReadableDuration `json:"tcp_timeout,omitempty" yaml:"tcp_timeout,omitempty"` + RetransmitMult int `json:"retransmit_mult,omitempty" yaml:"retransmit_mult,omitempty"` + ProbeTimeout util.ReadableDuration `json:"probe_timeout,omitempty" yaml:"probe_timeout,omitempty"` + ProbeInterval util.ReadableDuration `json:"probe_interval,omitempty" yaml:"probe_interval,omitempty"` + GossipInterval util.ReadableDuration `json:"gossip_interval,omitempty" yaml:"gossip_interval,omitempty"` + GosshipNodes int `json:"gossip_nodes,omitempty" yaml:"gossip_nodes,omitempty"` + SuspicionMult int `json:"suspicion_mult,omitempty" yaml:"suspicion_mult,omitempty"` + SuspicionMaxTimeoutMult int `json:"suspicion_max_timeout_mult,omitempty" yaml:"suspicion_max_timeout_mult,omitempty"` + UDPBufferSize int `json:"udp_buffer_size,omitempty" yaml:"udp_buffer_size,omitempty"` + ExtraSameMemberLimit uint64 `json:"extra_same_member_limit,omitempty" yaml:"extra_same_member_limit,omitempty"` + BroadcastTimerMult int `json:"broadcast_timer_mult,omitempty" yaml:"broadcast_timer_mult,omitempty"` + UserMsgLoopInterval util.ReadableDuration `json:"user_msg_loop_interval,omitempty" yaml:"user_msg_loop_interval,omitempty"` + //revive:enable:line-length-limit +} + +func (p *MemberlistParams) marshaler() memberlistParamsMarshaler { + return memberlistParamsMarshaler{ + BaseHinter: p.BaseHinter, + TCPTimeout: util.ReadableDuration(p.tcpTimeout), + RetransmitMult: p.retransmitMult, + ProbeTimeout: util.ReadableDuration(p.probeTimeout), + ProbeInterval: util.ReadableDuration(p.probeInterval), + GossipInterval: util.ReadableDuration(p.gossipInterval), + GosshipNodes: p.gosshipNodes, + SuspicionMult: p.suspicionMult, + SuspicionMaxTimeoutMult: p.suspicionMaxTimeoutMult, + UDPBufferSize: p.udpBufferSize, + ExtraSameMemberLimit: p.extraSameMemberLimit, + BroadcastTimerMult: p.broadcastTimerMult, + UserMsgLoopInterval: util.ReadableDuration(p.userMsgLoopInterval), + } +} + +func (p *MemberlistParams) MarshalJSON() ([]byte, error) { + return util.MarshalJSON(p.marshaler()) +} + +func (p *MemberlistParams) MarshalYAML() (interface{}, error) { + return p.marshaler(), nil +} + +type memberlistParamsUnmarshaler struct { + //revive:disable:line-length-limit + TCPTimeout *util.ReadableDuration `json:"tcp_timeout,omitempty" yaml:"tcp_timeout,omitempty"` + RetransmitMult *int `json:"retransmit_mult,omitempty" yaml:"retransmit_mult,omitempty"` + ProbeTimeout *util.ReadableDuration `json:"probe_timeout,omitempty" yaml:"probe_timeout,omitempty"` + ProbeInterval *util.ReadableDuration `json:"probe_interval,omitempty" yaml:"probe_interval,omitempty"` + GossipInterval *util.ReadableDuration `json:"gossip_interval,omitempty" yaml:"gossip_interval,omitempty"` + GosshipNodes *int `json:"gossip_nodes,omitempty" yaml:"gossip_nodes,omitempty"` + SuspicionMult *int `json:"suspicion_mult,omitempty" yaml:"suspicion_mult,omitempty"` + SuspicionMaxTimeoutMult *int `json:"suspicion_max_timeout_mult,omitempty" yaml:"suspicion_max_timeout_mult,omitempty"` + UDPBufferSize *int `json:"udp_buffer_size,omitempty" yaml:"udp_buffer_size,omitempty"` + ExtraSameMemberLimit *uint64 `json:"extra_same_member_limit,omitempty" yaml:"extra_same_member_limit,omitempty"` + BroadcastTimerMult *int `json:"broadcast_timer_mult,omitempty" yaml:"broadcast_timer_mult,omitempty"` + UserMsgLoopInterval *util.ReadableDuration `json:"user_msg_loop_interval,omitempty" yaml:"user_msg_loop_interval,omitempty"` + hint.BaseHinter + //revive:enable:line-length-limit +} + +func (p *MemberlistParams) UnmarshalJSON(b []byte) error { + d := DefaultMemberlistParams() + *p = *d + + e := util.StringError("unmarshal MemberlistParams") + + var u memberlistParamsUnmarshaler + + if err := util.UnmarshalJSON(b, &u); err != nil { + return e.Wrap(err) + } + p.BaseHinter = u.BaseHinter + + return e.Wrap(p.unmarshal(u)) +} + +func (p *MemberlistParams) UnmarshalYAML(y *yaml.Node) error { + d := DefaultMemberlistParams() + *p = *d + + e := util.StringError("unmarshal MemberlistParams") + + var u memberlistParamsUnmarshaler + + if err := y.Decode(&u); err != nil { + return e.Wrap(err) + } + + return e.Wrap(p.unmarshal(u)) +} + +func (p *MemberlistParams) unmarshal(u memberlistParamsUnmarshaler) error { + if u.RetransmitMult != nil { + p.retransmitMult = *u.RetransmitMult + } + + if u.SuspicionMult != nil { + p.suspicionMult = *u.SuspicionMult + } + + if u.SuspicionMaxTimeoutMult != nil { + p.suspicionMaxTimeoutMult = *u.SuspicionMaxTimeoutMult + } + + if u.UDPBufferSize != nil { + p.udpBufferSize = *u.UDPBufferSize + } + + if u.ExtraSameMemberLimit != nil { + p.extraSameMemberLimit = *u.ExtraSameMemberLimit + } + + if u.BroadcastTimerMult != nil { + p.broadcastTimerMult = *u.BroadcastTimerMult + } + + if u.GosshipNodes != nil { + p.gosshipNodes = *u.GosshipNodes + } + + durargs := [][2]interface{}{ + {u.TCPTimeout, &p.tcpTimeout}, + {u.ProbeTimeout, &p.probeTimeout}, + {u.ProbeInterval, &p.probeInterval}, + {u.GossipInterval, &p.gossipInterval}, + {u.UserMsgLoopInterval, &p.userMsgLoopInterval}, + } + + for i := range durargs { + v := durargs[i][0].(*util.ReadableDuration) //nolint:forcetypeassert //... + t := durargs[i][1].(*time.Duration) //nolint:forcetypeassert //... + + if reflect.ValueOf(v).IsZero() { + continue + } + + if err := util.SetInterfaceValue[time.Duration](time.Duration(*v), t); err != nil { + return err + } + } + + return nil +} diff --git a/network/quicstream/conn.go b/network/quicstream/conn.go index 33763137..91f3ae3f 100644 --- a/network/quicstream/conn.go +++ b/network/quicstream/conn.go @@ -3,28 +3,28 @@ package quicstream import ( "net" - "github.com/ProtoconNet/mitum2/network" + nutil "github.com/ProtoconNet/mitum2/network/util" "github.com/ProtoconNet/mitum2/util" "github.com/pkg/errors" ) type ConnInfo struct { addr *net.UDPAddr - tlsinsecure bool + tlsInsecure bool } -func UnsafeConnInfo(addr *net.UDPAddr, tlsinsecure bool) ConnInfo { - return ConnInfo{addr: addr, tlsinsecure: tlsinsecure} +func UnsafeConnInfo(addr *net.UDPAddr, tlsInsecure bool) ConnInfo { + return ConnInfo{addr: addr, tlsInsecure: tlsInsecure} } -func NewConnInfo(addr *net.UDPAddr, tlsinsecure bool) (ConnInfo, error) { - ci := UnsafeConnInfo(addr, tlsinsecure) +func NewConnInfo(addr *net.UDPAddr, tlsInsecure bool) (ConnInfo, error) { + ci := UnsafeConnInfo(addr, tlsInsecure) return ci, ci.IsValid(nil) } -func MustConnInfo(addr *net.UDPAddr, tlsinsecure bool) ConnInfo { - ci, err := NewConnInfo(addr, tlsinsecure) +func MustConnInfo(addr *net.UDPAddr, tlsInsecure bool) ConnInfo { + ci, err := NewConnInfo(addr, tlsInsecure) if err != nil { panic(err) } @@ -33,15 +33,15 @@ func MustConnInfo(addr *net.UDPAddr, tlsinsecure bool) ConnInfo { } func NewConnInfoFromFullString(s string) (ConnInfo, error) { - as, tlsinsecure := network.ParseTLSInsecure(s) + as, tlsInsecure := nutil.ParseTLSInsecure(s) - return NewConnInfoFromStringAddr(as, tlsinsecure) + return NewConnInfoFromStringAddr(as, tlsInsecure) } func MustNewConnInfoFromFullString(s string) ConnInfo { - as, tlsinsecure := network.ParseTLSInsecure(s) + as, tlsInsecure := nutil.ParseTLSInsecure(s) - ci, err := NewConnInfoFromStringAddr(as, tlsinsecure) + ci, err := NewConnInfoFromStringAddr(as, tlsInsecure) if err != nil { panic(err) } @@ -49,10 +49,10 @@ func MustNewConnInfoFromFullString(s string) ConnInfo { return ci } -func NewConnInfoFromStringAddr(s string, tlsinsecure bool) (ci ConnInfo, _ error) { +func NewConnInfoFromStringAddr(s string, tlsInsecure bool) (ci ConnInfo, _ error) { addr, err := net.ResolveUDPAddr("udp", s) if err == nil { - return NewConnInfo(addr, tlsinsecure) + return NewConnInfo(addr, tlsInsecure) } var dnserr *net.DNSError @@ -84,7 +84,7 @@ func (c ConnInfo) Addr() net.Addr { } func (c ConnInfo) TLSInsecure() bool { - return c.tlsinsecure + return c.tlsInsecure } func (c ConnInfo) String() string { @@ -93,7 +93,7 @@ func (c ConnInfo) String() string { addr = c.addr.String() } - return network.ConnInfoToString(addr, c.tlsinsecure) + return nutil.ConnInfoToString(addr, c.tlsInsecure) } func (c ConnInfo) UDPAddr() *net.UDPAddr { diff --git a/network/util.go b/network/util.go deleted file mode 100644 index 07956d10..00000000 --- a/network/util.go +++ /dev/null @@ -1,24 +0,0 @@ -package network - -import ( - "net" - - "github.com/pkg/errors" -) - -func IsValidAddr(s string) error { - if len(s) < 1 { - return errors.Errorf("empty publish") - } - - switch host, port, err := net.SplitHostPort(s); { - case err != nil: - return errors.WithStack(err) - case len(host) < 1: - return errors.Errorf("empty host") - case len(port) < 1: - return errors.Errorf("empty port") - } - - return nil -} diff --git a/network/util/util.go b/network/util/util.go new file mode 100644 index 00000000..54355b47 --- /dev/null +++ b/network/util/util.go @@ -0,0 +1,57 @@ +package util + +import ( + "net" + "net/url" + "strings" + + "github.com/pkg/errors" +) + +func IsValidAddr(s string) error { + if len(s) < 1 { + return errors.Errorf("empty publish") + } + + switch host, port, err := net.SplitHostPort(s); { + case err != nil: + return errors.WithStack(err) + case len(host) < 1: + return errors.Errorf("empty host") + case len(port) < 1: + return errors.Errorf("empty port") + } + + return nil +} + +var DefaultTLSInsecureFlag = "tls_insecure" + +func HasTLSInsecure(s, flag string) bool { + v, err := url.ParseQuery(s) + if err != nil { + return false + } + + return v.Has(flag) +} + +func ParseTLSInsecure(s string) (string, bool) { + switch i := strings.Index(s, "#"); { + case i < 0: + return s, false + case len(s[i:]) > 0: + return s[:i], HasTLSInsecure(s[i+1:], DefaultTLSInsecureFlag) + default: + return s[:i], false + } +} + +func ConnInfoToString(addr string, tlsinsecure bool) string { // revive:disable-line:flag-parameter + ti := "" + if tlsinsecure { + ti = "#tls_insecure" + } + + return addr + ti +} diff --git a/util/yaml.go b/util/yaml.go index 43496bfc..be1b4243 100644 --- a/util/yaml.go +++ b/util/yaml.go @@ -3,11 +3,12 @@ package util import ( "bytes" "fmt" - "github.com/pkg/errors" - "gopkg.in/yaml.v3" "os" "reflect" "regexp" + + "github.com/pkg/errors" + "gopkg.in/yaml.v3" ) type YAMLOrderedMap struct { From 01636c528af32fe67f2a4f75de2a16f07b464c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=ED=95=98=EC=98=81?= Date: Mon, 18 Aug 2025 14:17:29 +0900 Subject: [PATCH 17/19] fix: reduce memory by avoiding clone in Iter and adapt callers --- isaac/database/center.go | 99 ++++++++++++++++-------------------- isaac/database/pool.go | 102 ++++++++++++++++++++++---------------- storage/leveldb/db.go | 64 +++++++++--------------- storage/leveldb/prefix.go | 26 ++++++++-- 4 files changed, 148 insertions(+), 143 deletions(-) diff --git a/isaac/database/center.go b/isaac/database/center.go index ecb6251d..54124b32 100644 --- a/isaac/database/center.go +++ b/isaac/database/center.go @@ -777,34 +777,65 @@ func (db *Center) cleanRemoved(limit int) error { func loadTemp( st *leveldbstorage.Storage, - height base.Height, + h base.Height, encs *encoder.Encoders, enc encoder.Encoder, ) (isaac.TempDatabase, error) { + r := &leveldbutil.Range{ - Start: emptyPrefixStoragePrefixByHeight(leveldbLabelBlockWrite, height), //nolint:gomnd //... - Limit: emptyPrefixStoragePrefixByHeight(leveldbLabelBlockWrite, height+1), //nolint:gomnd //... + Start: emptyPrefixStoragePrefixByHeight(leveldbLabelBlockWrite, h), + Limit: emptyPrefixStoragePrefixByHeight(leveldbLabelBlockWrite, h+1), + } + + const delBufCap = 128 + var delBuf = make([][]byte, 0, delBufCap) + + flushDeletes := func() error { + if len(delBuf) == 0 { + return nil + } + for _, p := range delBuf { + if err := leveldbstorage.RemoveByPrefix(st, p); err != nil { + return err + } + } + delBuf = delBuf[:0] + return nil } - var lastprefix []byte - var prefixes [][]byte + var lastPrefix []byte + var found isaac.TempDatabase if err := st.Iter( r, - func(key, _ []byte) (bool, error) { - k, err := prefixStoragePrefixFromKey(key) - if err != nil { + func(k, _ []byte) (bool, error) { + prefix, err := prefixStoragePrefixFromKey(k) + if err != nil || bytes.Equal(prefix, lastPrefix) { return true, nil } + lastPrefix = prefix - if bytes.Equal(k, lastprefix) { + if found != nil { + delBuf = append(delBuf, prefix) + if len(delBuf) >= delBufCap { + return false, flushDeletes() + } return true, nil } - lastprefix = k + temp, err := NewTempLeveldbFromPrefix(st, prefix, encs, enc) + if err != nil { + delBuf = append(delBuf, prefix) + return true, nil + } - prefixes = append(prefixes, k) + isMerged, err := temp.isMerged() + if err != nil || !isMerged { + delBuf = append(delBuf, prefix) + return true, nil + } + found = temp return true, nil }, false, @@ -812,50 +843,8 @@ func loadTemp( return nil, err } - if len(prefixes) < 1 { - return nil, nil - } - - var useless [][]byte - - var found isaac.TempDatabase - - for i := range prefixes { - prefix := prefixes[i] - - if found != nil { - useless = append(useless, prefix) - - continue - } - - temp, err := NewTempLeveldbFromPrefix(st, prefix, encs, enc) - if err != nil { - useless = append(useless, prefix) - - continue - } - - switch ismerged, err := temp.isMerged(); { - case err != nil: - useless = append(useless, prefix) - - continue - case !ismerged: - useless = append(useless, prefix) - - continue - default: - found = temp - } - } - - if len(useless) > 0 { - for i := range useless { - if err := leveldbstorage.RemoveByPrefix(st, useless[i]); err != nil { - return nil, err - } - } + if err := flushDeletes(); err != nil { + return nil, err } return found, nil diff --git a/isaac/database/pool.go b/isaac/database/pool.go index 494df5d1..3760c86a 100644 --- a/isaac/database/pool.go +++ b/isaac/database/pool.go @@ -982,74 +982,90 @@ func (db *TempPool) cleanByHeight( deep int, keyf func(*leveldbstorage.PrefixStorageBatch, []byte, []byte), ) (int, error) { + pst, err := db.st() if err != nil { return 0, err } - top := base.NilHeight - - var keys [][3]interface{} - - _ = pst.Iter( + var top base.Height + err = pst.Iter( leveldbutil.BytesPrefix(prefix[:]), - func(key, b []byte) (bool, error) { - i, err := heightFromKey(key, prefix) - if err != nil { - keys = append(keys, [3]interface{}{key, b, nil}) - - return true, nil - } - - if i > top { - top = i + func(k, _ []byte) (bool, error) { + h, e := heightFromKey(k, prefix) + if e != nil { + return false, e } - - keys = append(keys, [3]interface{}{key, b, i}) - - return true, nil + top = h + return false, nil }, false, ) - - height := top - - switch { - case len(keys) < 1: - return 0, nil - case top-3 < base.GenesisHeight: + if err != nil { + return 0, err + } + if top == base.NilHeight { return 0, nil - default: - for range make([]int, deep) { - height = height.SafePrev() + } + + cutoff := top + for i := 0; i < deep; i++ { + cutoff = cutoff.SafePrev() + if cutoff < base.GenesisHeight { + return 0, nil } } + const recLimit = 10_000 // Record count threshold (tunable) + const byteLimit = 4 << 20 // 4MiB threshold (tunable) + batch := pst.NewBatch() defer batch.Reset() - var removed int + var removed, recCnt, byteCnt int - for i := range keys { - key, b, j := keys[i][0].([]byte), keys[i][1].([]byte), keys[i][2] //nolint:forcetypeassert //... - if j != nil && j.(base.Height) > height { //nolint:forcetypeassert //... - continue - } + err = pst.Iter( + leveldbutil.BytesPrefix(prefix[:]), + func(k, v []byte) (bool, error) { - batch.Delete(key) + h, e := heightFromKey(k, prefix) + if e != nil || h > cutoff { + return true, e + } - if keyf != nil { - keyf(batch, key, b) - } + batch.Delete(k) + + if keyf != nil { + keyf(batch, k, v) + } + + recCnt++ + byteCnt += len(k) + len(v) - removed++ + if recCnt >= recLimit || byteCnt >= byteLimit { + if err := pst.Batch(batch, nil); err != nil { + return false, err + } + removed += recCnt + batch.Reset() + recCnt, byteCnt = 0, 0 + } + return true, nil + }, + true, + ) + if err != nil { + return removed, err } - if batch.Len() < 1 { - return removed, nil + if recCnt > 0 { + if err := pst.Batch(batch, nil); err != nil { + return removed, err + } + removed += recCnt } - return removed, pst.Batch(batch, nil) + return removed, nil } func (db *TempPool) opFromCache(h util.Hash) (base.Operation, bool) { diff --git a/storage/leveldb/db.go b/storage/leveldb/db.go index 8d436d06..e78d82e8 100644 --- a/storage/leveldb/db.go +++ b/storage/leveldb/db.go @@ -1,7 +1,6 @@ package leveldbstorage import ( - "bytes" "context" "sync" @@ -106,47 +105,32 @@ func (st *Storage) Exists(key []byte) (bool, error) { func (st *Storage) Iter( r *leveldbutil.Range, - callback func(key []byte, raw []byte) (bool, error), - sort bool, // NOTE if true, ascend order + callback func(key, val []byte) (bool, error), + ascend bool, ) error { db, err := st.db() if err != nil { return err } - iter := db.NewIterator(r, nil) defer iter.Release() - var seek func() bool - var next func() bool - if sort { - seek = iter.First - next = iter.Next - } else { - seek = iter.Last - next = iter.Prev - } - - if !seek() { - return nil + seek, next := iter.Last, iter.Prev + if ascend { + seek, next = iter.First, iter.Next } - -end: - for { - switch keep, err := callback(bytes.Clone(iter.Key()), bytes.Clone(iter.Value())); { - case err != nil: + for ok := seek(); ok; ok = next() { + keep, err := callback(iter.Key(), iter.Value()) + if err != nil { return err - case !keep: - break end - case !next(): - break end + } + if !keep { + break } } - if err := iter.Error(); err != nil { return storage.ErrExec.Errorf("iter") } - return nil } @@ -315,31 +299,30 @@ func BatchRemove(st *Storage, r *leveldbutil.Range, limit int) (int, error) { return 0, err } - var removed int - - var batch leveldb.Batch + var ( + removed int + batch leveldb.Batch + ) defer batch.Reset() if r == nil { r = &leveldbutil.Range{} } - start := r.Start for { r.Start = start + var nextStart []byte if err := st.Iter( r, func(key, _ []byte) (bool, error) { if batch.Len() == limit { - start = key - + // Copy the key only for one moment when the limit is reached + nextStart = append(nextStart[:0], key...) return false, nil } - batch.Delete(key) - return true, nil }, true, @@ -347,19 +330,20 @@ func BatchRemove(st *Storage, r *leveldbutil.Range, limit int) (int, error) { return removed, err } - if batch.Len() < 1 { + if batch.Len() == 0 { break } - if err := st.Batch(&batch, nil); err != nil { return removed, err } - removed += batch.Len() - batch.Reset() - } + if nextStart == nil { + break + } + start = nextStart + } return removed, nil } diff --git a/storage/leveldb/prefix.go b/storage/leveldb/prefix.go index 0f898bb0..9f320295 100644 --- a/storage/leveldb/prefix.go +++ b/storage/leveldb/prefix.go @@ -226,19 +226,35 @@ func (b *PrefixStorageBatch) LBatch() *leveldb.Batch { } func RemoveByPrefix(st *Storage, prefix []byte) error { + const batchLimit = 10_000 + var batch leveldb.Batch + flush := func() error { + if batch.Len() == 0 { + return nil + } + if err := st.Batch(&batch, nil); err != nil { + return err + } + batch.Reset() + return nil + } if err := st.Iter( leveldbutil.BytesPrefix(prefix), - func(key, _ []byte) (bool, error) { - batch.Delete(key) - + func(k, _ []byte) (bool, error) { + batch.Delete(k) + if batch.Len() >= batchLimit { + if err := flush(); err != nil { + return false, err + } + } return true, nil }, true, ); err != nil { - return errors.Errorf("remove prefix storage") + return errors.Errorf("remove prefix storage: %w", err) } - return st.Batch(&batch, nil) + return flush() } From 1ce0c02b724190788bbc730b981bc6af95c7bc95 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Mon, 27 Oct 2025 08:30:43 +0900 Subject: [PATCH 18/19] add node metric to monitoring network outbound --- isaac/network/header.go | 28 ++ isaac/network/node_metric.go | 316 ++++++++++++++++++++++ isaac/network/quicstream_handlers.go | 68 ++++- launch/cmd/network.go | 1 + launch/cmd/network_client_node_metrics.go | 72 +++++ launch/hinters.go | 1 + launch/local_params.go | 1 + launch/p_memberlist.go | 14 +- launch/p_network.go | 14 +- launch/p_network_handlers.go | 23 ++ launch/p_node_metric.go | 20 ++ launch/ps_run.go | 3 +- network/quicmemberlist/memberlist.go | 27 ++ network/quicstream/connection.go | 49 ++-- network/quicstream/metrics.go | 49 ++++ network/quicstream/server.go | 11 + 16 files changed, 660 insertions(+), 37 deletions(-) create mode 100644 isaac/network/node_metric.go create mode 100644 launch/cmd/network_client_node_metrics.go create mode 100644 launch/p_node_metric.go create mode 100644 network/quicstream/metrics.go diff --git a/isaac/network/header.go b/isaac/network/header.go index b6c4d929..fcd11adb 100644 --- a/isaac/network/header.go +++ b/isaac/network/header.go @@ -28,6 +28,7 @@ var ( StateRequestHeaderHint = hint.MustNewHint("state-header-v0.0.1") ExistsInStateOperationRequestHeaderHint = hint.MustNewHint("exists-instate-operation-header-v0.0.1") NodeInfoRequestHeaderHint = hint.MustNewHint("node-info-header-v0.0.1") + NodeMetricsRequestHeaderHint = hint.MustNewHint("node-metrics-header-v0.0.1") SendBallotsHeaderHint = hint.MustNewHint("send-ballots-header-v0.0.1") SetAllowConsensusHeaderHint = hint.MustNewHint("set-allow-consensus-header-v0.0.1") StreamOperationsHeaderHint = hint.MustNewHint("stream-operations-header-v0.0.1") @@ -57,6 +58,7 @@ var ( HandlerNameSendOperation quicstream.HandlerName = "send_operation" HandlerNameState quicstream.HandlerName = "state" HandlerNameExistsInStateOperation quicstream.HandlerName = "exists_instate_operation" + HandlerNameNodeMetrics quicstream.HandlerName = "node_metrics" HandlerNameNodeInfo quicstream.HandlerName = "node_info" HandlerNameSendBallots quicstream.HandlerName = "send_ballots" HandlerNameSetAllowConsensus quicstream.HandlerName = "set_allow_consensus" @@ -82,6 +84,7 @@ var ( handlerPrefixOperation = quicstream.HashPrefix(HandlerNameOperation) handlerPrefixSendOperation = quicstream.HashPrefix(HandlerNameSendOperation) handlerPrefixState = quicstream.HashPrefix(HandlerNameState) + handlerPrefixNodeMetrics = quicstream.HashPrefix(HandlerNameNodeMetrics) handlerPrefixExistsInStateOperation = quicstream.HashPrefix(HandlerNameExistsInStateOperation) handlerPrefixNodeInfo = quicstream.HashPrefix(HandlerNameNodeInfo) handlerPrefixSendBallots = quicstream.HashPrefix(HandlerNameSendBallots) @@ -594,6 +597,29 @@ func (h ExistsInStateOperationRequestHeader) FactHash() util.Hash { return h.facthash } +type NodeMetricsRequestHeader struct { + interval string // "1s", "1m", "5m" 등 + BaseHeader +} + +func NewNodeMetricsRequestHeader(interval string) NodeMetricsRequestHeader { + return NodeMetricsRequestHeader{ + BaseHeader: NewBaseHeader(NodeMetricsRequestHeaderHint), + interval: interval, + } +} + +func (h NodeMetricsRequestHeader) IsValid([]byte) error { + if err := h.BaseHinter.IsValid(NodeMetricsRequestHeaderHint.Type().Bytes()); err != nil { + return errors.WithMessage(err, "invalid NodeMetricsHeader") + } + return nil +} + +func (h NodeMetricsRequestHeader) Interval() string { + return h.interval +} + type NodeInfoRequestHeader struct { BaseHeader } @@ -968,6 +994,8 @@ func headerPrefixByHint(ht hint.Hint) quicstream.HandlerPrefix { return handlerPrefixExistsInStateOperation case NodeInfoRequestHeaderHint.Type(): return handlerPrefixNodeInfo + case NodeMetricsRequestHeaderHint.Type(): + return handlerPrefixNodeMetrics case SendBallotsHeaderHint.Type(): return handlerPrefixSendBallots case SetAllowConsensusHeaderHint.Type(): diff --git a/isaac/network/node_metric.go b/isaac/network/node_metric.go new file mode 100644 index 00000000..8bb3e3c6 --- /dev/null +++ b/isaac/network/node_metric.go @@ -0,0 +1,316 @@ +package isaacnetwork + +import ( + "math" + "sync" + "sync/atomic" + "time" + + "github.com/ProtoconNet/mitum2/network/quicstream" + "github.com/ProtoconNet/mitum2/util" + "github.com/ProtoconNet/mitum2/util/hint" +) + +var NodeMetricsHint = hint.MustNewHint("node-metrics-v0.0.1") + +const ( + defaultRetention = 10 * time.Minute + defaultSampleLimit = 1 << 10 + defaultInterval = time.Minute + defaultIntervalLabel = "1m" +) + +type NodeMetrics struct { + hint.BaseHinter + Timestamp time.Time `json:"timestamp"` + Uptime time.Duration `json:"uptime"` + Cumulative CumulativeMetrics `json:"cumulative"` + Intervals map[string]IntervalMetrics `json:"intervals"` +} + +func (m NodeMetrics) MarshalJSON() ([]byte, error) { + type alias struct { + Hint hint.Type `json:"hint"` + Timestamp time.Time `json:"timestamp"` + Uptime string `json:"uptime"` + Cumulative CumulativeMetrics `json:"cumulative"` + Intervals map[string]IntervalMetrics `json:"intervals"` + } + + return util.MarshalJSON(alias{ + Hint: m.Hint().Type(), + Timestamp: m.Timestamp, + Uptime: m.Uptime.String(), + Cumulative: m.Cumulative, + Intervals: m.Intervals, + }) +} + +type CumulativeMetrics struct { + QuicBytesSent uint64 `json:"quic_bytes_sent"` + QuicBytesReceived uint64 `json:"quic_bytes_received"` + MemberlistBroadcasts uint64 `json:"memberlist_broadcasts"` + MemberlistMessagesRecv uint64 `json:"memberlist_messages_recv"` +} + +type IntervalMetrics struct { + QuicBytesSent uint64 `json:"quic_bytes_sent"` + QuicBytesReceived uint64 `json:"quic_bytes_received"` + QuicBytesPerSecSent float64 `json:"quic_bytes_per_sec_sent"` + QuicBytesPerSecRecv float64 `json:"quic_bytes_per_sec_recv"` + MemberlistBroadcasts uint64 `json:"memberlist_broadcasts"` + MemberlistMessagesRecv uint64 `json:"memberlist_messages_recv"` + MemberlistMsgsPerSec float64 `json:"memberlist_msgs_per_sec"` + ActiveConnections int64 `json:"active_connections"` + ActiveStreams int64 `json:"active_streams"` + MemberlistMembers int64 `json:"memberlist_members"` +} + +type metricsSample struct { + at time.Time + quicBytesSent uint64 + quicBytesRecv uint64 + memberlistBroadcasts uint64 + memberlistMessages uint64 +} + +type NetworkMetricsCollector struct { + quicBytesSent atomic.Uint64 + quicBytesRecv atomic.Uint64 + memberlistBcasts atomic.Uint64 + memberlistMsgsRecv atomic.Uint64 + activeConnections atomic.Int64 + activeStreams atomic.Int64 + memberlistMembers atomic.Int64 + + startTime time.Time + clock func() time.Time + + sampleLock sync.Mutex + samples []metricsSample +} + +var _ quicstream.MetricsCollector = (*NetworkMetricsCollector)(nil) + +func NewNetworkMetricsCollector() *NetworkMetricsCollector { + now := time.Now().UTC() + + return &NetworkMetricsCollector{ + startTime: now, + clock: func() time.Time { return time.Now().UTC() }, + samples: []metricsSample{{at: now}}, + } +} + +func (c *NetworkMetricsCollector) GetSnapshot(interval string) NodeMetrics { + if c == nil { + return NodeMetrics{} + } + + now := c.clock() + sample := metricsSample{ + at: now, + quicBytesSent: c.quicBytesSent.Load(), + quicBytesRecv: c.quicBytesRecv.Load(), + memberlistBroadcasts: c.memberlistBcasts.Load(), + memberlistMessages: c.memberlistMsgsRecv.Load(), + } + + intervalSpecs := c.intervalSpecs(interval) + + c.sampleLock.Lock() + c.samples = append(c.samples, sample) + c.pruneSamplesLocked(now) + + intervalResult := make(map[string]IntervalMetrics, len(intervalSpecs)) + for _, spec := range intervalSpecs { + baseline := c.baselineLocked(spec.duration, now) + intervalResult[spec.label] = c.buildIntervalMetrics(spec.duration, baseline, sample) + } + c.sampleLock.Unlock() + + return NodeMetrics{ + BaseHinter: hint.NewBaseHinter(NodeMetricsHint), + Timestamp: now, + Uptime: now.Sub(c.startTime), + Cumulative: CumulativeMetrics{ + QuicBytesSent: sample.quicBytesSent, + QuicBytesReceived: sample.quicBytesRecv, + MemberlistBroadcasts: sample.memberlistBroadcasts, + MemberlistMessagesRecv: sample.memberlistMessages, + }, + Intervals: intervalResult, + } +} + +func (c *NetworkMetricsCollector) RecordQuicBytesSent(n uint64) { + if c == nil || n == 0 { + return + } + + c.quicBytesSent.Add(n) +} + +func (c *NetworkMetricsCollector) RecordQuicBytesReceived(n uint64) { + if c == nil || n == 0 { + return + } + + c.quicBytesRecv.Add(n) +} + +func (c *NetworkMetricsCollector) RecordQuicStreamOpened() { + if c == nil { + return + } + + c.activeStreams.Add(1) +} + +func (c *NetworkMetricsCollector) RecordQuicStreamClosed() { + if c == nil { + return + } + + c.activeStreams.Add(-1) +} + +func (c *NetworkMetricsCollector) RecordQuicConnectionOpened() { + if c == nil { + return + } + + c.activeConnections.Add(1) +} + +func (c *NetworkMetricsCollector) RecordQuicConnectionClosed() { + if c == nil { + return + } + + c.activeConnections.Add(-1) +} + +func (c *NetworkMetricsCollector) RecordMemberlistBroadcast() { + if c == nil { + return + } + + c.memberlistBcasts.Add(1) +} + +func (c *NetworkMetricsCollector) RecordMemberlistMessageReceived() { + if c == nil { + return + } + + c.memberlistMsgsRecv.Add(1) +} + +func (c *NetworkMetricsCollector) SetMemberlistMembers(n int) { + if c == nil { + return + } + + c.memberlistMembers.Store(int64(n)) +} + +type intervalSpec struct { + label string + duration time.Duration +} + +func (c *NetworkMetricsCollector) intervalSpecs(requested string) []intervalSpec { + specs := []intervalSpec{ + {label: "1s", duration: time.Second}, + {label: defaultIntervalLabel, duration: time.Minute}, + {label: "5m", duration: 5 * time.Minute}, + } + + if requested == "" { + return specs + } + + alreadyIncluded := false + for i := range specs { + if specs[i].label == requested { + alreadyIncluded = true + break + } + } + + if alreadyIncluded { + return specs + } + + if d, err := time.ParseDuration(requested); err == nil && d > 0 { + specs = append(specs, intervalSpec{label: requested, duration: d}) + } + + return specs +} + +func (c *NetworkMetricsCollector) pruneSamplesLocked(now time.Time) { + cutoff := now.Add(-defaultRetention) + + var idx int + for idx = 0; idx < len(c.samples); idx++ { + if c.samples[idx].at.After(cutoff) { + break + } + } + + if idx > 0 && idx < len(c.samples) { + c.samples = append([]metricsSample{}, c.samples[idx:]...) + } + + if len(c.samples) > defaultSampleLimit { + c.samples = c.samples[len(c.samples)-defaultSampleLimit:] + } +} + +func (c *NetworkMetricsCollector) baselineLocked(duration time.Duration, now time.Time) metricsSample { + target := now.Add(-duration) + + for i := len(c.samples) - 1; i >= 0; i-- { + s := c.samples[i] + if !s.at.After(target) { + return s + } + } + + if len(c.samples) > 0 { + return c.samples[0] + } + + return metricsSample{at: now} +} + +func (c *NetworkMetricsCollector) buildIntervalMetrics( + duration time.Duration, + baseline, current metricsSample, +) IntervalMetrics { + elapsed := current.at.Sub(baseline.at) + if elapsed <= 0 { + elapsed = duration + } + + bytesSent := current.quicBytesSent - baseline.quicBytesSent + bytesRecv := current.quicBytesRecv - baseline.quicBytesRecv + bcasts := current.memberlistBroadcasts - baseline.memberlistBroadcasts + msgs := current.memberlistMessages - baseline.memberlistMessages + elapsedSec := math.Max(elapsed.Seconds(), 1) + + return IntervalMetrics{ + QuicBytesSent: bytesSent, + QuicBytesReceived: bytesRecv, + QuicBytesPerSecSent: float64(bytesSent) / elapsedSec, + QuicBytesPerSecRecv: float64(bytesRecv) / elapsedSec, + MemberlistBroadcasts: bcasts, + MemberlistMessagesRecv: msgs, + MemberlistMsgsPerSec: float64(msgs) / elapsedSec, + ActiveConnections: c.activeConnections.Load(), + ActiveStreams: c.activeStreams.Load(), + MemberlistMembers: c.memberlistMembers.Load(), + } +} diff --git a/isaac/network/quicstream_handlers.go b/isaac/network/quicstream_handlers.go index a1d1a812..e03cbffd 100644 --- a/isaac/network/quicstream_handlers.go +++ b/isaac/network/quicstream_handlers.go @@ -3,6 +3,7 @@ package isaacnetwork import ( "bytes" "context" + "fmt" "io" "net" "net/url" @@ -26,8 +27,8 @@ var ContextKeyNodeChallengedNode = util.ContextKey("node-challenge-node") func QuicstreamHandlerOperation( oppool isaac.NewOperationPool, getFromHandoverX func(context.Context, OperationRequestHeader) ( - enchint string, body []byte, found bool, _ error, -), + enchint string, body []byte, found bool, _ error, + ), ) quicstreamheader.Handler[OperationRequestHeader] { if getFromHandoverX == nil { getFromHandoverX = func(context.Context, OperationRequestHeader) ( //revive:disable-line:modifies-parameter @@ -291,10 +292,10 @@ func QuicstreamHandlerBlockMap( func QuicstreamHandlerBlockItem( blockItemf func( - base.Height, - base.BlockItemType, - func(_ io.Reader, found bool, uri url.URL, compressFormat string) error, -) error, + base.Height, + base.BlockItemType, + func(_ io.Reader, found bool, uri url.URL, compressFormat string) error, + ) error, ) quicstreamheader.Handler[BlockItemRequestHeader] { return func(ctx context.Context, _ net.Addr, broker *quicstreamheader.HandlerBroker, header BlockItemRequestHeader, @@ -335,9 +336,9 @@ func QuicstreamHandlerBlockItem( func QuicstreamHandlerBlockItemFiles( blockItemFilesf func( - base.Height, - func(_ io.Reader, found bool) error, -) error, + base.Height, + func(_ io.Reader, found bool) error, + ) error, ) quicstreamheader.Handler[BlockItemFilesRequestHeader] { return func(ctx context.Context, _ net.Addr, broker *quicstreamheader.HandlerBroker, header BlockItemFilesRequestHeader, @@ -541,7 +542,7 @@ func QuicstreamHandlerNodeInfo( broker *quicstreamheader.HandlerBroker, _ NodeInfoRequestHeader, ) (context.Context, error) { e := util.StringError("handle node info") - + fmt.Println("QuicstreamHandlerNodeInfo QuicstreamHandlerNodeInfo") b, err, _ := util.SingleflightDo[[]byte](&sg, HandlerNameNodeInfo.String(), func() ([]byte, error) { return getNodeInfo() }) @@ -564,6 +565,45 @@ func QuicstreamHandlerNodeInfo( } } +func QuicstreamHandlerNodeMetrics( + getMetrics func(interval string) ([]byte, error), +) quicstreamheader.Handler[NodeMetricsRequestHeader] { + var sg singleflight.Group + + return func(ctx context.Context, _ net.Addr, + broker *quicstreamheader.HandlerBroker, req NodeMetricsRequestHeader, + ) (context.Context, error) { + e := util.StringError("handle node metrics") + + interval := req.Interval() + if interval == "" { + interval = "1m" // default + } + + sgkey := HandlerNameNodeMetrics.String() + interval + + b, err, _ := util.SingleflightDo[[]byte](&sg, sgkey, func() ([]byte, error) { + return getMetrics(interval) + }) + + switch { + case err != nil: + return ctx, e.Wrap(err) + case len(b) < 1: + return ctx, e.Errorf("empty node metrics") + } + + body := bytes.NewBuffer(b) + defer body.Reset() + + if err := writeResponseStream(ctx, broker, true, nil, body); err != nil { + return ctx, e.Wrap(err) + } + + return ctx, nil + } +} + func QuicstreamHandlerSendBallots( networkID base.NetworkID, votef func(base.BallotSignFact) error, @@ -631,10 +671,10 @@ func QuicstreamHandlerStreamOperations( networkID base.NetworkID, limit uint64, traverse func( - _ context.Context, - offset []byte, - callback func(enchint string, meta isaacdatabase.FrameHeaderPoolOperation, body, offset []byte) (bool, error), -) error, + _ context.Context, + offset []byte, + callback func(enchint string, meta isaacdatabase.FrameHeaderPoolOperation, body, offset []byte) (bool, error), + ) error, ) quicstreamheader.Handler[StreamOperationsHeader] { return func(ctx context.Context, addr net.Addr, broker *quicstreamheader.HandlerBroker, header StreamOperationsHeader, diff --git a/launch/cmd/network.go b/launch/cmd/network.go index 30ba6afd..0db81d40 100644 --- a/launch/cmd/network.go +++ b/launch/cmd/network.go @@ -23,6 +23,7 @@ type NetworkClientCommand struct { //nolint:govet //... //revive:disable:line-length-limit //revive:disable:nested-structs NodeInfo NetworkClientNodeInfoCommand `cmd:"" name:"node-info" help:"remote node info"` + NodeMetrics NetworkClientNodeMetricsCommand `cmd:"" name:"node-metrics" help:"remote node metrics"` SendOperation NetworkClientSendOperationCommand `cmd:"" name:"send-operation" help:"send operation"` State NetworkClientStateCommand `cmd:"" name:"state" help:"get state"` LastBlockMap NetworkClientLastBlockMapCommand `cmd:"" name:"last-blockmap" help:"get last blockmap"` diff --git a/launch/cmd/network_client_node_metrics.go b/launch/cmd/network_client_node_metrics.go new file mode 100644 index 00000000..27e56634 --- /dev/null +++ b/launch/cmd/network_client_node_metrics.go @@ -0,0 +1,72 @@ +package launchcmd + +import ( + "context" + "os" + + isaacnetwork "github.com/ProtoconNet/mitum2/isaac/network" + quicstreamheader "github.com/ProtoconNet/mitum2/network/quicstream/header" + "github.com/ProtoconNet/mitum2/util/encoder" + "github.com/pkg/errors" +) + +type NetworkClientNodeMetricsCommand struct { //nolint:govet //... + BaseNetworkClientCommand + Interval string `arg:"" name:"interval" default:"1m" help:"metrics interval window (e.g. 1s, 1m, 5m)"` +} + +func (cmd *NetworkClientNodeMetricsCommand) Run(pctx context.Context) error { + if err := cmd.Prepare(pctx); err != nil { + return err + } + + ctx, cancel := context.WithTimeout(pctx, cmd.Timeout) + defer cancel() + + stream, _, err := cmd.Client.Dial(ctx, cmd.Remote.ConnInfo()) + if err != nil { + return err + } + + defer func() { + _ = cmd.Client.Close() + }() + + header := isaacnetwork.NewNodeMetricsRequestHeader(cmd.Interval) + header.SetClientID(cmd.ClientID) + + return stream(ctx, func(ctx context.Context, broker *quicstreamheader.ClientBroker) error { + if err := broker.WriteRequestHead(ctx, header); err != nil { + return err + } + + var enc encoder.Encoder + + switch renc, rh, err := broker.ReadResponseHead(ctx); { + case err != nil: + return err + case rh.Err() != nil: + return rh.Err() + case !rh.OK(): + return errors.Errorf("not ok") + default: + enc = renc + } + + switch bodyType, bodyLength, r, err := broker.ReadBodyErr(ctx); { + case err != nil: + return err + case bodyType == quicstreamheader.EmptyBodyType, + bodyType == quicstreamheader.FixedLengthBodyType && bodyLength < 1: + return errors.Errorf("empty body") + default: + var v interface{} + + if err := enc.StreamDecoder(r).Decode(&v); err != nil { + return err + } + + return cmd.Print(v, os.Stdout) + } + }) +} diff --git a/launch/hinters.go b/launch/hinters.go index 2a5a5c71..5ca86b87 100644 --- a/launch/hinters.go +++ b/launch/hinters.go @@ -73,6 +73,7 @@ var Hinters = []encoder.DecodeDetail{ {Hint: isaacnetwork.NodeChallengeRequestHeaderHint, Instance: isaacnetwork.NodeChallengeRequestHeader{}}, {Hint: isaacnetwork.NodeInfoHint, Instance: isaacnetwork.NodeInfo{}}, {Hint: isaacnetwork.NodeInfoRequestHeaderHint, Instance: isaacnetwork.NodeInfoRequestHeader{}}, + {Hint: isaacnetwork.NodeMetricsRequestHeaderHint, Instance: isaacnetwork.NodeMetricsRequestHeader{}}, {Hint: isaacnetwork.OperationRequestHeaderHint, Instance: isaacnetwork.OperationRequestHeader{}}, {Hint: isaacnetwork.ProposalRequestHeaderHint, Instance: isaacnetwork.ProposalRequestHeader{}}, {Hint: isaacnetwork.RequestProposalRequestHeaderHint, Instance: isaacnetwork.RequestProposalRequestHeader{}}, diff --git a/launch/local_params.go b/launch/local_params.go index 7c2012be..ab57598d 100644 --- a/launch/local_params.go +++ b/launch/local_params.go @@ -473,6 +473,7 @@ var networkHandlerNames = []quicstream.HandlerName{ isaacnetwork.HandlerNameLastSuffrageProof, isaacnetwork.HandlerNameNodeChallenge, isaacnetwork.HandlerNameNodeInfo, + isaacnetwork.HandlerNameNodeMetrics, isaacnetwork.HandlerNameOperation, isaacnetwork.HandlerNameProposal, isaacnetwork.HandlerNameRequestProposal, diff --git a/launch/p_memberlist.go b/launch/p_memberlist.go index 840894ab..64b1cd93 100644 --- a/launch/p_memberlist.go +++ b/launch/p_memberlist.go @@ -117,11 +117,21 @@ func PMemberlist(pctx context.Context) (context.Context, error) { func PStartMemberlist(pctx context.Context) (context.Context, error) { var m *quicmemberlist.Memberlist - if err := util.LoadFromContextOK(pctx, MemberlistContextKey, &m); err != nil { + var collector *isaacnetwork.NetworkMetricsCollector + + if err := util.LoadFromContextOK(pctx, + MemberlistContextKey, &m, + MetricsCollectorContextKey, &collector, + ); err != nil { return pctx, err } - return pctx, m.Start(context.Background()) + ctx := context.Background() + if collector != nil { + ctx = quicstream.WithMetricsCollector(ctx, collector) + } + + return pctx, m.Start(ctx) } func PCloseMemberlist(pctx context.Context) (context.Context, error) { diff --git a/launch/p_network.go b/launch/p_network.go index 25a46513..616a59e9 100644 --- a/launch/p_network.go +++ b/launch/p_network.go @@ -100,11 +100,21 @@ func PNetwork(pctx context.Context) (context.Context, error) { func PStartNetwork(pctx context.Context) (context.Context, error) { var server *quicstream.Server - if err := util.LoadFromContextOK(pctx, QuicstreamServerContextKey, &server); err != nil { + var collector *isaacnetwork.NetworkMetricsCollector + + if err := util.LoadFromContextOK(pctx, + QuicstreamServerContextKey, &server, + MetricsCollectorContextKey, &collector, + ); err != nil { return pctx, err } - return pctx, server.Start(context.Background()) + ctx := context.Background() + if collector != nil { + ctx = quicstream.WithMetricsCollector(ctx, collector) + } + + return pctx, server.Start(ctx) } func PCloseNetwork(pctx context.Context) (context.Context, error) { diff --git a/launch/p_network_handlers.go b/launch/p_network_handlers.go index 43c33060..fb31209c 100644 --- a/launch/p_network_handlers.go +++ b/launch/p_network_handlers.go @@ -61,6 +61,7 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { var ballotbox *isaacstates.Ballotbox var filternotifymsg quicmemberlist.FilterNotifyMsgFunc var lvps *isaac.LastVoteproofsHandler + var metricsCollector *isaacnetwork.NetworkMetricsCollector if err := util.LoadFromContextOK(pctx, LoggingContextKey, &log, @@ -77,6 +78,7 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { BallotboxContextKey, &ballotbox, FilterMemberlistNotifyMsgFuncContextKey, &filternotifymsg, LastVoteproofsHandlerContextKey, &lvps, + MetricsCollectorContextKey, &metricsCollector, ); err != nil { return pctx, e.Wrap(err) } @@ -166,6 +168,13 @@ func PNetworkHandlers(pctx context.Context) (context.Context, error) { isaacnetwork.HandlerNameNodeInfo, isaacnetwork.QuicstreamHandlerNodeInfo(QuicstreamHandlerGetNodeInfoFunc(encs.Default(), nodeinfo)), nil) + EnsureHandlerAdd(pctx, &gerror, + isaacnetwork.HandlerNameNodeMetrics, + isaacnetwork.QuicstreamHandlerNodeMetrics( + QuicstreamHandlerGetNodeMetricsFunc(encs.Default(), metricsCollector), + ), + nil) + EnsureHandlerAdd(pctx, &gerror, isaacnetwork.HandlerNameSendBallots, isaacnetwork.QuicstreamHandlerSendBallots(isaacparams.NetworkID(), @@ -447,6 +456,20 @@ func QuicstreamHandlerGetNodeInfoFunc( } } +func QuicstreamHandlerGetNodeMetricsFunc( + enc encoder.Encoder, + collector *isaacnetwork.NetworkMetricsCollector, +) func(string) ([]byte, error) { + return func(interval string) ([]byte, error) { + if collector == nil { + return nil, errors.Errorf("metrics collector not initialized") + } + + metrics := collector.GetSnapshot(interval) + return enc.Marshal(metrics) + } +} + func OperationPreProcess( db isaac.Database, oprs *hint.CompatibleSet[isaac.NewOperationProcessorInternalFunc], diff --git a/launch/p_node_metric.go b/launch/p_node_metric.go new file mode 100644 index 00000000..bea94a5d --- /dev/null +++ b/launch/p_node_metric.go @@ -0,0 +1,20 @@ +package launch + +import ( + "context" + + isaacnetwork "github.com/ProtoconNet/mitum2/isaac/network" + "github.com/ProtoconNet/mitum2/network/quicstream" + "github.com/ProtoconNet/mitum2/util/ps" +) + +var ( + PNameNodeMetric = ps.Name("node-metric") + MetricsCollectorContextKey = quicstream.MetricsCollectorContextKey +) + +func PNodeMetric(pctx context.Context) (context.Context, error) { + collector := isaacnetwork.NewNetworkMetricsCollector() + + return context.WithValue(pctx, MetricsCollectorContextKey, collector), nil +} diff --git a/launch/ps_run.go b/launch/ps_run.go index cdcb3f9d..f396d9a7 100644 --- a/launch/ps_run.go +++ b/launch/ps_run.go @@ -53,7 +53,8 @@ func DefaultRunPS() *ps.PS { PostAddOK(PNameLoadFromDatabase, PLoadFromDatabase). PostAddOK(PNameCheckBlocksOfStorage, PCheckBlocksOfStorage). PostAddOK(PNamePatchBlockItemReaders, PPatchBlockItemReaders). - PostAddOK(PNameNodeInfo, PNodeInfo) + PostAddOK(PNameNodeInfo, PNodeInfo). + PostAddOK(PNameNodeMetric, PNodeMetric) _ = pps.POK(PNameNetwork). PreAddOK(PNameQuicstreamClient, PQuicstreamClient). diff --git a/network/quicmemberlist/memberlist.go b/network/quicmemberlist/memberlist.go index 0faa3231..99fc828e 100644 --- a/network/quicmemberlist/memberlist.go +++ b/network/quicmemberlist/memberlist.go @@ -86,6 +86,7 @@ type Memberlist struct { args *MemberlistArgs *logging.Logging *util.ContextDaemon + ctx context.Context m *memberlist.Memberlist delegate *Delegate members *membersPool @@ -99,6 +100,7 @@ type Memberlist struct { joinedLock sync.RWMutex usermsgsLock sync.Mutex isJoined bool + metricsCollector quicstream.MetricsCollector } func NewMemberlist(local Member, args *MemberlistArgs) (*Memberlist, error) { @@ -136,6 +138,10 @@ func NewMemberlist(local Member, args *MemberlistArgs) (*Memberlist, error) { } func (srv *Memberlist) Start(ctx context.Context) error { + srv.ctx = ctx + srv.metricsCollector = quicstream.GetMetricsCollector(ctx) + srv.updateMemberMetrics() + m, err := srv.createMemberlist() if err != nil { return err @@ -226,6 +232,7 @@ func (srv *Memberlist) Leave(timeout time.Duration) error { notleft = true srv.members.Empty() + srv.updateMemberMetrics() if err := srv.m.Leave(timeout); err != nil { srv.Log().Error().Err(err).Msg("failed to leave previous memberlist; ignored") @@ -307,6 +314,10 @@ func (srv *Memberlist) Broadcast(b memberlist.Broadcast) { return } + if srv.metricsCollector != nil { + srv.metricsCollector.RecordMemberlistBroadcast() + } + srv.Log().Trace().Interface("broadcast", b).Msg("enqueue broadcast") srv.delegate.QueueBroadcast(b) @@ -699,6 +710,8 @@ func (srv *Memberlist) whenJoined(member Member) { if srv.members.Set(member) { srv.Log().Debug().Bool("is_joined", srv.isJoined).Interface("member", member).Msg("member joined") + + srv.updateMemberMetrics() } } @@ -723,6 +736,8 @@ func (srv *Memberlist) whenLeft(member Member) { if removed { srv.Log().Debug().Bool("is_joined", srv.isJoined).Interface("member", member).Msg("member left") + + srv.updateMemberMetrics() } return removed @@ -751,9 +766,21 @@ func (srv *Memberlist) notifyMsgFunc(b []byte) { srv.usermsgsLock.Lock() defer srv.usermsgsLock.Unlock() + if srv.metricsCollector != nil { + srv.metricsCollector.RecordMemberlistMessageReceived() + } + srv.usermsgs.PushBack(b) } +func (srv *Memberlist) updateMemberMetrics() { + if srv.metricsCollector == nil { + return + } + + srv.metricsCollector.SetMemberlistMembers(srv.members.Len()) +} + func (srv *Memberlist) notifyMsgCallbackBroadcastMessage(b []byte) ([]byte, encoder.Encoder, error) { var m ConnInfoBroadcastMessage diff --git a/network/quicstream/connection.go b/network/quicstream/connection.go index d1892ecf..8ceec239 100644 --- a/network/quicstream/connection.go +++ b/network/quicstream/connection.go @@ -47,32 +47,45 @@ func Dial( } func (c *Connection) Stream(ctx context.Context, f StreamFunc) error { - switch stream, err := c.openStream(ctx); { - case err != nil: + stream, err := c.openStream(ctx) + if err != nil { return ErrOpenStream.WithMessage(err, "stream") - default: - defer func() { - stream.CancelRead(0) - _ = stream.Close() - }() - - return util.AwareContext(ctx, func(ctx context.Context) error { - return f(ctx, stream, stream) - }) } + + if collector := GetMetricsCollector(ctx); collector != nil { + collector.RecordQuicStreamOpened() + defer collector.RecordQuicStreamClosed() + } + + defer func() { + stream.CancelRead(0) + _ = stream.Close() + }() + + return util.AwareContext(ctx, func(ctx context.Context) error { + return f(ctx, stream, stream) + }) } func (c *Connection) OpenStream(ctx context.Context) (io.Reader, io.WriteCloser, func() error, error) { - switch stream, err := c.openStream(ctx); { - case err != nil: + stream, err := c.openStream(ctx) + if err != nil { return nil, nil, nil, ErrOpenStream.WithMessage(err, "open stream") - default: - return stream, stream, func() error { - stream.CancelRead(0) + } - return errors.Wrap(stream.Close(), "close stream") - }, nil + if collector := GetMetricsCollector(ctx); collector != nil { + collector.RecordQuicStreamOpened() } + + return stream, stream, func() error { + if collector := GetMetricsCollector(ctx); collector != nil { + collector.RecordQuicStreamClosed() + } + + stream.CancelRead(0) + + return errors.Wrap(stream.Close(), "close stream") + }, nil } func (c *Connection) Close() error { diff --git a/network/quicstream/metrics.go b/network/quicstream/metrics.go new file mode 100644 index 00000000..8d6e1c3b --- /dev/null +++ b/network/quicstream/metrics.go @@ -0,0 +1,49 @@ +package quicstream + +import ( + "context" + + "github.com/ProtoconNet/mitum2/util" +) + +// MetricsCollectorContextKey is used to propagate the optional metrics collector +// through contexts that are shared by the networking stack. +var MetricsCollectorContextKey = util.ContextKey("network-metrics-collector") + +// MetricsCollector represents the subset of metrics hooks used by the network +// stack. Implementations are expected to be concurrency-safe. +type MetricsCollector interface { + RecordQuicBytesSent(uint64) + RecordQuicBytesReceived(uint64) + RecordQuicStreamOpened() + RecordQuicStreamClosed() + RecordQuicConnectionOpened() + RecordQuicConnectionClosed() + RecordMemberlistBroadcast() + RecordMemberlistMessageReceived() + SetMemberlistMembers(int) +} + +// WithMetricsCollector returns a derived context containing the provided +// collector. +func WithMetricsCollector(ctx context.Context, collector MetricsCollector) context.Context { + if ctx == nil || collector == nil { + return ctx + } + + return context.WithValue(ctx, MetricsCollectorContextKey, collector) +} + +// GetMetricsCollector extracts the MetricsCollector stored in the context, if +// any. +func GetMetricsCollector(ctx context.Context) MetricsCollector { + if ctx == nil { + return nil + } + + if collector, ok := ctx.Value(MetricsCollectorContextKey).(MetricsCollector); ok { + return collector + } + + return nil +} diff --git a/network/quicstream/server.go b/network/quicstream/server.go index ff1e965b..1f952f0d 100644 --- a/network/quicstream/server.go +++ b/network/quicstream/server.go @@ -102,6 +102,12 @@ func (srv *Server) accept(ctx context.Context, listener *quic.EarlyListener) { } func (srv *Server) handleConnection(ctx context.Context, conn *quic.Conn) { + collector := GetMetricsCollector(ctx) + if collector != nil { + collector.RecordQuicConnectionOpened() + defer collector.RecordQuicConnectionClosed() + } + for { stream, err := conn.AcceptStream(ctx) if err != nil { @@ -159,6 +165,11 @@ func (srv *Server) handleStream(ctx context.Context, remoteAddr net.Addr, stream sctx, cancel := srv.streamTimeoutContext(ctx) defer cancel() + if collector := GetMetricsCollector(ctx); collector != nil { + collector.RecordQuicStreamOpened() + defer collector.RecordQuicStreamClosed() + } + var errcode quic.StreamErrorCode if err := util.AwareContext(sctx, func(context.Context) error { From 48aef1088f5ffe28047d5a40ed44eb84c259e002 Mon Sep 17 00:00:00 2001 From: soonkuk Date: Wed, 29 Oct 2025 09:33:56 +0900 Subject: [PATCH 19/19] feat: update node metric to get BytesSend and BytesReceived --- isaac/network/node_metric.go | 56 +++++++++++++++++++++++++--- isaac/network/quicstream_handlers.go | 3 +- launch/hinters.go | 1 + network/quicstream/connection.go | 8 +++- network/quicstream/metrics.go | 45 ++++++++++++++++++++++ network/quicstream/server.go | 4 +- 6 files changed, 106 insertions(+), 11 deletions(-) diff --git a/isaac/network/node_metric.go b/isaac/network/node_metric.go index 8bb3e3c6..8fbaaf27 100644 --- a/isaac/network/node_metric.go +++ b/isaac/network/node_metric.go @@ -2,6 +2,7 @@ package isaacnetwork import ( "math" + "reflect" "sync" "sync/atomic" "time" @@ -9,6 +10,7 @@ import ( "github.com/ProtoconNet/mitum2/network/quicstream" "github.com/ProtoconNet/mitum2/util" "github.com/ProtoconNet/mitum2/util/hint" + "github.com/ProtoconNet/mitum2/util/localtime" ) var NodeMetricsHint = hint.MustNewHint("node-metrics-v0.0.1") @@ -30,22 +32,64 @@ type NodeMetrics struct { func (m NodeMetrics) MarshalJSON() ([]byte, error) { type alias struct { - Hint hint.Type `json:"hint"` - Timestamp time.Time `json:"timestamp"` - Uptime string `json:"uptime"` + hint.BaseHinter + Timestamp localtime.Time `json:"timestamp"` + Uptime util.ReadableDuration `json:"uptime"` Cumulative CumulativeMetrics `json:"cumulative"` Intervals map[string]IntervalMetrics `json:"intervals"` } return util.MarshalJSON(alias{ - Hint: m.Hint().Type(), - Timestamp: m.Timestamp, - Uptime: m.Uptime.String(), + BaseHinter: m.BaseHinter, + Timestamp: localtime.New(m.Timestamp), + Uptime: util.ReadableDuration(m.Uptime), Cumulative: m.Cumulative, Intervals: m.Intervals, }) } +func (m *NodeMetrics) UnmarshalJSON(b []byte) error { + e := util.StringError("unmarshal NodeMetrics") + + type alias struct { + hint.BaseHinter + Timestamp time.Time `json:"timestamp"` + Uptime *util.ReadableDuration `json:"uptime"` + Cumulative CumulativeMetrics `json:"cumulative"` + Intervals map[string]IntervalMetrics `json:"intervals"` + } + + var u alias + + if err := util.UnmarshalJSON(b, &u); err != nil { + return e.Wrap(err) + } + + m.BaseHinter = u.BaseHinter + m.Timestamp = u.Timestamp + m.Cumulative = u.Cumulative + m.Intervals = u.Intervals + + durArgs := [][2]interface{}{ + {u.Uptime, &m.Uptime}, + } + + for i := range durArgs { + v := durArgs[i][0].(*util.ReadableDuration) //nolint:forcetypeassert //... + t := durArgs[i][1].(*time.Duration) //nolint:forcetypeassert //... + + if reflect.ValueOf(v).IsZero() { + continue + } + + if err := util.SetInterfaceValue(time.Duration(*v), t); err != nil { + return err + } + } + + return nil +} + type CumulativeMetrics struct { QuicBytesSent uint64 `json:"quic_bytes_sent"` QuicBytesReceived uint64 `json:"quic_bytes_received"` diff --git a/isaac/network/quicstream_handlers.go b/isaac/network/quicstream_handlers.go index e03cbffd..e5f4c4e8 100644 --- a/isaac/network/quicstream_handlers.go +++ b/isaac/network/quicstream_handlers.go @@ -3,7 +3,6 @@ package isaacnetwork import ( "bytes" "context" - "fmt" "io" "net" "net/url" @@ -542,7 +541,7 @@ func QuicstreamHandlerNodeInfo( broker *quicstreamheader.HandlerBroker, _ NodeInfoRequestHeader, ) (context.Context, error) { e := util.StringError("handle node info") - fmt.Println("QuicstreamHandlerNodeInfo QuicstreamHandlerNodeInfo") + b, err, _ := util.SingleflightDo[[]byte](&sg, HandlerNameNodeInfo.String(), func() ([]byte, error) { return getNodeInfo() }) diff --git a/launch/hinters.go b/launch/hinters.go index 5ca86b87..a6ab6e1b 100644 --- a/launch/hinters.go +++ b/launch/hinters.go @@ -73,6 +73,7 @@ var Hinters = []encoder.DecodeDetail{ {Hint: isaacnetwork.NodeChallengeRequestHeaderHint, Instance: isaacnetwork.NodeChallengeRequestHeader{}}, {Hint: isaacnetwork.NodeInfoHint, Instance: isaacnetwork.NodeInfo{}}, {Hint: isaacnetwork.NodeInfoRequestHeaderHint, Instance: isaacnetwork.NodeInfoRequestHeader{}}, + {Hint: isaacnetwork.NodeMetricsHint, Instance: isaacnetwork.NodeMetrics{}}, {Hint: isaacnetwork.NodeMetricsRequestHeaderHint, Instance: isaacnetwork.NodeMetricsRequestHeader{}}, {Hint: isaacnetwork.OperationRequestHeaderHint, Instance: isaacnetwork.OperationRequestHeader{}}, {Hint: isaacnetwork.ProposalRequestHeaderHint, Instance: isaacnetwork.ProposalRequestHeader{}}, diff --git a/network/quicstream/connection.go b/network/quicstream/connection.go index 8ceec239..1753e4f9 100644 --- a/network/quicstream/connection.go +++ b/network/quicstream/connection.go @@ -57,13 +57,15 @@ func (c *Connection) Stream(ctx context.Context, f StreamFunc) error { defer collector.RecordQuicStreamClosed() } + r, w := wrapMetricsIO(ctx, stream, stream) + defer func() { stream.CancelRead(0) _ = stream.Close() }() return util.AwareContext(ctx, func(ctx context.Context) error { - return f(ctx, stream, stream) + return f(ctx, r, w) }) } @@ -77,7 +79,9 @@ func (c *Connection) OpenStream(ctx context.Context) (io.Reader, io.WriteCloser, collector.RecordQuicStreamOpened() } - return stream, stream, func() error { + r, w := wrapMetricsIO(ctx, stream, stream) + + return r, w, func() error { if collector := GetMetricsCollector(ctx); collector != nil { collector.RecordQuicStreamClosed() } diff --git a/network/quicstream/metrics.go b/network/quicstream/metrics.go index 8d6e1c3b..2e2ea453 100644 --- a/network/quicstream/metrics.go +++ b/network/quicstream/metrics.go @@ -2,6 +2,7 @@ package quicstream import ( "context" + "io" "github.com/ProtoconNet/mitum2/util" ) @@ -47,3 +48,47 @@ func GetMetricsCollector(ctx context.Context) MetricsCollector { return nil } + +func wrapMetricsIO(ctx context.Context, reader io.Reader, writer io.WriteCloser) (io.Reader, io.WriteCloser) { + collector := GetMetricsCollector(ctx) + + switch { + case collector == nil: + return reader, writer + case reader == nil: + return reader, &metricsWriteCloser{WriteCloser: writer, collector: collector} + case writer == nil: + return &metricsReader{reader: reader, collector: collector}, writer + default: + return &metricsReader{reader: reader, collector: collector}, + &metricsWriteCloser{WriteCloser: writer, collector: collector} + } +} + +type metricsReader struct { + reader io.Reader + collector MetricsCollector +} + +func (r *metricsReader) Read(p []byte) (int, error) { + n, err := r.reader.Read(p) + if n > 0 && r.collector != nil { + r.collector.RecordQuicBytesReceived(uint64(n)) + } + + return n, err +} + +type metricsWriteCloser struct { + io.WriteCloser + collector MetricsCollector +} + +func (w *metricsWriteCloser) Write(p []byte) (int, error) { + n, err := w.WriteCloser.Write(p) + if n > 0 && w.collector != nil { + w.collector.RecordQuicBytesSent(uint64(n)) + } + + return n, err +} diff --git a/network/quicstream/server.go b/network/quicstream/server.go index 1f952f0d..00a77ea6 100644 --- a/network/quicstream/server.go +++ b/network/quicstream/server.go @@ -165,6 +165,8 @@ func (srv *Server) handleStream(ctx context.Context, remoteAddr net.Addr, stream sctx, cancel := srv.streamTimeoutContext(ctx) defer cancel() + r, w := wrapMetricsIO(ctx, stream, stream) + if collector := GetMetricsCollector(ctx); collector != nil { collector.RecordQuicStreamOpened() defer collector.RecordQuicStreamClosed() @@ -173,7 +175,7 @@ func (srv *Server) handleStream(ctx context.Context, remoteAddr net.Addr, stream var errcode quic.StreamErrorCode if err := util.AwareContext(sctx, func(context.Context) error { - _, err := srv.handler(sctx, remoteAddr, stream, stream) + _, err := srv.handler(sctx, remoteAddr, r, w) return err }); err != nil {