From 1cf79dab805ce2efe0a92c7ca4923910109afbe2 Mon Sep 17 00:00:00 2001 From: simlecode <69969590+simlecode@users.noreply.github.com> Date: Thu, 31 Oct 2024 13:55:51 +0800 Subject: [PATCH] feat: implement F3 list participants API --- pkg/vf3/f3.go | 13 +++++++ pkg/vf3/participation_lease.go | 37 +++++++++++++------ venus-shared/api/chain/v1/f3.go | 3 ++ venus-shared/api/chain/v1/method.md | 20 ++++++++++ .../api/chain/v1/mock/mock_fullnode.go | 15 ++++++++ venus-shared/api/chain/v1/proxy_gen.go | 4 ++ venus-shared/types/api_types.go | 13 +++++++ 7 files changed, 94 insertions(+), 11 deletions(-) diff --git a/pkg/vf3/f3.go b/pkg/vf3/f3.go index baafa85b31..9645c3c3ca 100644 --- a/pkg/vf3/f3.go +++ b/pkg/vf3/f3.go @@ -225,3 +225,16 @@ func (fff *F3) IsRunning() bool { func (fff *F3) Progress() gpbft.Instant { return fff.inner.Progress() } + +func (fff *F3) ListParticipants() []types.F3Participant { + leases := fff.leaser.getValidLeases() + participants := make([]types.F3Participant, len(leases)) + for i, lease := range leases { + participants[i] = types.F3Participant{ + MinerID: lease.MinerID, + FromInstance: lease.FromInstance, + ValidityTerm: lease.ValidityTerm, + } + } + return participants +} diff --git a/pkg/vf3/participation_lease.go b/pkg/vf3/participation_lease.go index 230c5ea026..c5f5f47ba5 100644 --- a/pkg/vf3/participation_lease.go +++ b/pkg/vf3/participation_lease.go @@ -55,7 +55,7 @@ func (l *leaser) getOrRenewParticipationTicket(participant uint64, previous type // - it is valid and was issued by this node. // // Otherwise, return ErrF3ParticipationIssuerMismatch to signal to the caller the need for retry. - switch _, err := l.validate(manifest.NetworkName, currentInstance, previous); { + switch _, err := l.validateTicket(manifest.NetworkName, currentInstance, previous); { case errors.Is(err, types.ErrF3ParticipationTicketInvalid): // Invalid ticket means the miner must have got the ticket from a node with a potentially different version. // Refuse to issue a new ticket in case there is some other node with active lease for the miner. @@ -89,7 +89,7 @@ func (l *leaser) participate(ticket types.F3ParticipationTicket) (types.F3Partic if manifest == nil { return types.F3ParticipationLease{}, types.ErrF3NotReady } - newLease, err := l.validate(manifest.NetworkName, instant.ID, ticket) + newLease, err := l.validateTicket(manifest.NetworkName, instant.ID, ticket) if err != nil { return types.F3ParticipationLease{}, err } @@ -118,15 +118,10 @@ func (l *leaser) getParticipantsByInstance(network gpbft.NetworkName, instance u } var participants []uint64 for id, lease := range l.leases { - if currentNetwork != lease.Network { - // Lazily delete any lease that does not belong to network, likely acquired from - // prior manifests. + if _, err := l.validateLease(currentNetwork, instance, lease); err != nil { + // Lazily clear old leases. + log.Warnf("lost F3 participation lease for miner %d at instance %d since it is no loger valid: %v ", id, instance, err) delete(l.leases, id) - log.Warnf("lost F3 participation lease for miner %d at instance %d due to network mismatch: %s != %s", id, instance, currentNetwork, lease.Network) - } else if instance > lease.ToInstance() { - // Lazily delete the expired leases. - delete(l.leases, id) - log.Warnf("lost F3 participation lease for miner %d due to instance (%d) > lease to instance (%d)", id, instance, lease.ToInstance()) } else { participants = append(participants, id) } @@ -134,6 +129,23 @@ func (l *leaser) getParticipantsByInstance(network gpbft.NetworkName, instance u return participants } +func (l *leaser) getValidLeases() []types.F3ParticipationLease { + l.mutex.Lock() + defer l.mutex.Unlock() + currentManifest, progress := l.status() + var leases []types.F3ParticipationLease + for id, lease := range l.leases { + // Lazily clear old leases. + if validatedLease, err := l.validateLease(currentManifest.NetworkName, progress.ID, lease); err != nil { + log.Warnf("lost F3 participation lease for miner %d at instance %d while getting valid leases since it is no loger valid: %v ", id, progress.ID, err) + delete(l.leases, id) + } else { + leases = append(leases, validatedLease) + } + } + return leases +} + func (l *leaser) newParticipationTicket(nn gpbft.NetworkName, participant uint64, from uint64, instances uint64) (types.F3ParticipationTicket, error) { // Lotus node API and miners run in a trusted environment. For now we make the // ticket to simply be the CBOR encoding of the lease. In the future, where the @@ -152,13 +164,16 @@ func (l *leaser) newParticipationTicket(nn gpbft.NetworkName, participant uint64 return buf.Bytes(), nil } -func (l *leaser) validate(currentNetwork gpbft.NetworkName, currentInstance uint64, t types.F3ParticipationTicket) (types.F3ParticipationLease, error) { +func (l *leaser) validateTicket(currentNetwork gpbft.NetworkName, currentInstance uint64, t types.F3ParticipationTicket) (types.F3ParticipationLease, error) { var lease types.F3ParticipationLease reader := bytes.NewReader(t) if err := lease.UnmarshalCBOR(reader); err != nil { return types.F3ParticipationLease{}, types.ErrF3ParticipationTicketInvalid } + return l.validateLease(currentNetwork, currentInstance, lease) +} +func (l *leaser) validateLease(currentNetwork gpbft.NetworkName, currentInstance uint64, lease types.F3ParticipationLease) (types.F3ParticipationLease, error) { // Combine the errors to remove significance of the order by which they are // checked outside if this function. var err error diff --git a/venus-shared/api/chain/v1/f3.go b/venus-shared/api/chain/v1/f3.go index a3d7b3664a..e088590fbc 100644 --- a/venus-shared/api/chain/v1/f3.go +++ b/venus-shared/api/chain/v1/f3.go @@ -68,4 +68,7 @@ type IF3 interface { F3IsRunning(ctx context.Context) (bool, error) //perm:read // F3GetProgress returns the progress of the current F3 instance in terms of instance ID, round and phase. F3GetProgress(ctx context.Context) (gpbft.Instant, error) //perm:read + + // F3ListParticipants returns the list of miners that are currently participating in F3 via this node. + F3ListParticipants(ctx context.Context) ([]types.F3Participant, error) //perm:read } diff --git a/venus-shared/api/chain/v1/method.md b/venus-shared/api/chain/v1/method.md index a07d0a200d..d06dca4485 100644 --- a/venus-shared/api/chain/v1/method.md +++ b/venus-shared/api/chain/v1/method.md @@ -126,6 +126,7 @@ curl http://:/rpc/v1 -X POST -H "Content-Type: application/json" -H " * [F3GetOrRenewParticipationTicket](#f3getorrenewparticipationticket) * [F3GetProgress](#f3getprogress) * [F3IsRunning](#f3isrunning) + * [F3ListParticipants](#f3listparticipants) * [F3Participate](#f3participate) * [Market](#market) * [StateMarketParticipants](#statemarketparticipants) @@ -3803,6 +3804,25 @@ Inputs: `[]` Response: `true` +### F3ListParticipants +F3ListParticipants returns the list of miners that are currently participating in F3 via this node. + + +Perms: read + +Inputs: `[]` + +Response: +```json +[ + { + "MinerID": 42, + "FromInstance": 42, + "ValidityTerm": 42 + } +] +``` + ### F3Participate F3Participate enrolls a storage provider in the F3 consensus process using a provided participation ticket. This ticket grants a temporary lease that enables diff --git a/venus-shared/api/chain/v1/mock/mock_fullnode.go b/venus-shared/api/chain/v1/mock/mock_fullnode.go index 4c4e4c9eea..ba26845c47 100644 --- a/venus-shared/api/chain/v1/mock/mock_fullnode.go +++ b/venus-shared/api/chain/v1/mock/mock_fullnode.go @@ -1224,6 +1224,21 @@ func (mr *MockFullNodeMockRecorder) F3IsRunning(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "F3IsRunning", reflect.TypeOf((*MockFullNode)(nil).F3IsRunning), arg0) } +// F3ListParticipants mocks base method. +func (m *MockFullNode) F3ListParticipants(arg0 context.Context) ([]types0.F3Participant, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "F3ListParticipants", arg0) + ret0, _ := ret[0].([]types0.F3Participant) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// F3ListParticipants indicates an expected call of F3ListParticipants. +func (mr *MockFullNodeMockRecorder) F3ListParticipants(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "F3ListParticipants", reflect.TypeOf((*MockFullNode)(nil).F3ListParticipants), arg0) +} + // F3Participate mocks base method. func (m *MockFullNode) F3Participate(arg0 context.Context, arg1 types0.F3ParticipationTicket) (types0.F3ParticipationLease, error) { m.ctrl.T.Helper() diff --git a/venus-shared/api/chain/v1/proxy_gen.go b/venus-shared/api/chain/v1/proxy_gen.go index 0690ff561e..59e7288dba 100644 --- a/venus-shared/api/chain/v1/proxy_gen.go +++ b/venus-shared/api/chain/v1/proxy_gen.go @@ -1099,6 +1099,7 @@ type IF3Struct struct { F3GetOrRenewParticipationTicket func(ctx context.Context, minerID address.Address, previous types.F3ParticipationTicket, instances uint64) (types.F3ParticipationTicket, error) `perm:"sign"` F3GetProgress func(ctx context.Context) (gpbft.Instant, error) `perm:"read"` F3IsRunning func(ctx context.Context) (bool, error) `perm:"read"` + F3ListParticipants func(ctx context.Context) ([]types.F3Participant, error) `perm:"read"` F3Participate func(ctx context.Context, ticket types.F3ParticipationTicket) (types.F3ParticipationLease, error) `perm:"sign"` } } @@ -1125,6 +1126,9 @@ func (s *IF3Struct) F3GetProgress(p0 context.Context) (gpbft.Instant, error) { return s.Internal.F3GetProgress(p0) } func (s *IF3Struct) F3IsRunning(p0 context.Context) (bool, error) { return s.Internal.F3IsRunning(p0) } +func (s *IF3Struct) F3ListParticipants(p0 context.Context) ([]types.F3Participant, error) { + return s.Internal.F3ListParticipants(p0) +} func (s *IF3Struct) F3Participate(p0 context.Context, p1 types.F3ParticipationTicket) (types.F3ParticipationLease, error) { return s.Internal.F3Participate(p0, p1) } diff --git a/venus-shared/types/api_types.go b/venus-shared/types/api_types.go index 4aee040bcb..b4dbe9d0db 100644 --- a/venus-shared/types/api_types.go +++ b/venus-shared/types/api_types.go @@ -528,6 +528,19 @@ func (l *F3ParticipationLease) ToInstance() uint64 { return l.FromInstance + l.ValidityTerm } +// F3Participant captures information about the miners that are currently +// participating in F3, along with the number of instances for which their lease +// is valid. +type F3Participant struct { + // MinerID is the actor ID of the miner that is + MinerID uint64 + // FromInstance specifies the instance ID from which this lease is valid. + FromInstance uint64 + // ValidityTerm specifies the number of instances for which the lease remains + // valid from the FromInstance. + ValidityTerm uint64 +} + var ( // ErrF3Disabled signals that F3 consensus process is disabled. ErrF3Disabled = errF3Disabled{}