Skip to content

Commit

Permalink
Implement ticket based F3 participation lease
Browse files Browse the repository at this point in the history
Implemented enhanced ticket-based participation system for F3 consensus
in `F3Participate`. This update introduces a new design where
participation tickets grant a temporary lease, allowing storage
providers to sign as part of the F3 consensus mechanism. This design ensures that
tickets are checked for validity and issuer alignment, handling errors
robustly. If there's an issuer mismatch, the system advises miners to
retry with the existing ticket. If the ticket is invalid or expired,
miners are directed to obtain a new ticket via
`F3GetOrRenewParticipationTicket`.

Fixes filecoin-project/go-f3#599
  • Loading branch information
masih committed Oct 2, 2024
1 parent 35b03d6 commit bc978d4
Show file tree
Hide file tree
Showing 22 changed files with 1,639 additions and 855 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Add `EthGetBlockReceipts` RPC method to retrieve transaction receipts for a spec
* [Checkpoint TipSets finalized by F3](https://github.com/filecoin-project/lotus/pull/12460): Once a decision is made by F3, the TipSet is check-pointed in `ChainStore`. As part of this change, any missing TipSets are asynchronously synced as required by the `ChainStore` checkpointing mechanism.
* Add an environment variable, `F3_INITIAL_POWERTABLE_CID` to allow specifying the initial power table used by F3 ([filecoin-project/lotus#12502](https://github.com/filecoin-project/lotus/pull/12502)). This may be used to help a lotus node re-sync the F3 chain when syncing from a snapshot after the F3 upgrade epoch. The precise CID to use here won't be known until the F3 is officially "live".
* Added `StateMinerInitialPledgeForSector` RPC method and deprecated existing `StateMinerInitialPledgeCollateral` method. Since ProveCommitSectors3 and ProveReplicaUpdates3, sector onboarding no longer includes an explicit notion of "deals", and precommit messages no longer contain deal information. This makes the existing `StateMinerInitialPledgeCollateral` unable to properly calculate pledge requirements with only the precommit. `StateMinerInitialPledgeForSector` is a new simplified calculator that simply takes duration, sector size, and verified size and estimates pledge based on current network conditions. Please note that the `StateMinerInitialPledgeCollateral` method will be removed entirely in the next non-patch release. ([filecoin-project/lotus#12384](https://github.com/filecoin-project/lotus/pull/12384)
* [Token-based F3 participation API](https://github.com/filecoin-project/lotus/pull/12531): This update introduces a new design where participation tokens grant a temporary lease, allowing storage providers to sign as part of a single GPBFT instance at any given point in time. This design ensures that tokens are checked for validity and issuer alignment, handling errors robustly in order to avoid self-equivocation during GPBFT instances.

## Improvements

Expand Down
66 changes: 53 additions & 13 deletions api/api_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,41 @@ import (
const (
EOutOfGas = iota + jsonrpc.FirstUserCode
EActorNotFound
EF3Disabled
EF3ParticipationTicketInvalid
EF3ParticipationTicketExpired
EF3ParticipationIssuerMismatch
)

type ErrOutOfGas struct{}
var (
RPCErrors = jsonrpc.NewErrors()

func (e *ErrOutOfGas) Error() string {
return "call ran out of gas"
}
// ErrF3Disabled signals that F3 consensus process is disabled.
ErrF3Disabled = errF3Disabled{}
// ErrF3ParticipationTicketInvalid signals that F3ParticipationTicket cannot be decoded.
ErrF3ParticipationTicketInvalid = errF3ParticipationTicketInvalid{}
// ErrF3ParticipationTicketExpired signals that the current GPBFT instance as surpassed the expiry of the ticket.
ErrF3ParticipationTicketExpired = errF3ParticipationTicketExpired{}
// ErrF3ParticipationIssuerMismatch signals that the ticket is not issued by the current node.
ErrF3ParticipationIssuerMismatch = errF3ParticipationIssuerMismatch{}

type ErrActorNotFound struct{}
_ error = (*ErrOutOfGas)(nil)
_ error = (*ErrActorNotFound)(nil)
_ error = (*errF3Disabled)(nil)
_ error = (*errF3ParticipationTicketInvalid)(nil)
_ error = (*errF3ParticipationTicketExpired)(nil)
_ error = (*errF3ParticipationIssuerMismatch)(nil)
)

func (e *ErrActorNotFound) Error() string {
return "actor not found"
func init() {
RPCErrors.Register(EOutOfGas, new(*ErrOutOfGas))
RPCErrors.Register(EActorNotFound, new(*ErrActorNotFound))
RPCErrors.Register(EF3Disabled, new(*errF3Disabled))
RPCErrors.Register(EF3ParticipationTicketInvalid, new(*errF3ParticipationTicketInvalid))
RPCErrors.Register(EF3ParticipationTicketExpired, new(*errF3ParticipationTicketExpired))
RPCErrors.Register(EF3ParticipationIssuerMismatch, new(*errF3ParticipationIssuerMismatch))
}

var RPCErrors = jsonrpc.NewErrors()

func ErrorIsIn(err error, errorTypes []error) bool {
for _, etype := range errorTypes {
tmp := reflect.New(reflect.PointerTo(reflect.ValueOf(etype).Elem().Type())).Interface()
Expand All @@ -36,7 +55,28 @@ func ErrorIsIn(err error, errorTypes []error) bool {
return false
}

func init() {
RPCErrors.Register(EOutOfGas, new(*ErrOutOfGas))
RPCErrors.Register(EActorNotFound, new(*ErrActorNotFound))
}
// ErrOutOfGas signals that a call failed due to insufficient gas.
type ErrOutOfGas struct{}

func (ErrOutOfGas) Error() string { return "call ran out of gas" }

// ErrActorNotFound signals that the actor is not found.
type ErrActorNotFound struct{}

func (ErrActorNotFound) Error() string { return "actor not found" }

type errF3Disabled struct{}

func (errF3Disabled) Error() string { return "f3 is disabled" }

type errF3ParticipationTicketInvalid struct{}

func (errF3ParticipationTicketInvalid) Error() string { return "ticket is not valid" }

type errF3ParticipationTicketExpired struct{}

func (errF3ParticipationTicketExpired) Error() string { return "ticket has expired" }

type errF3ParticipationIssuerMismatch struct{}

func (errF3ParticipationIssuerMismatch) Error() string { return "issuer does not match current node" }
86 changes: 70 additions & 16 deletions api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import (
"fmt"
"time"

"github.com/google/uuid"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/peer"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
Expand Down Expand Up @@ -910,24 +912,43 @@ type FullNode interface {

//*********************************** ALL F3 APIs below are not stable & subject to change ***********************************

// F3Participate should be called by a storage provider to participate in signing F3 consensus.
// Calling this API gives the lotus node a lease to sign in F3 on behalf of given SP.
// The lease should be active only on one node. The lease will expire at the newLeaseExpiration.
// To continue participating in F3 with the given node, call F3Participate again before
// the newLeaseExpiration time.
// newLeaseExpiration cannot be further than 5 minutes in the future.
// It is recommended to call F3Participate every 60 seconds
// with newLeaseExpiration set 2min into the future.
// The oldLeaseExpiration has to be set to newLeaseExpiration of the last successful call.
// For the first call to F3Participate, set the oldLeaseExpiration to zero value/time in the past.
// F3Participate will return true if the lease was accepted.
// The minerID has to be the ID address of the miner.
F3Participate(ctx context.Context, minerID address.Address, newLeaseExpiration time.Time, oldLeaseExpiration time.Time) (bool, error) //perm:sign
// F3GetCertificate returns a finality certificate at given instance number
// F3GetOrRenewParticipationTicket retrieves or renews a participation ticket
// necessary for a miner to engage in the F3 consensus process.
//
// This function accepts an optional previous ticket. If provided, a new ticket
// will be issued only under one the following conditions:
// 1. The previous ticket has expired.
// 2. The issuer of the previous ticket matches the node processing this
// request.
//
// If there is an issuer mismatch (ErrF3ParticipationIssuerMismatch), the miner
// must retry obtaining a new ticket to ensure it is only participating in one F3
// instance at any time.
//
// Note: Successfully acquiring a ticket alone does not constitute participation.
// The retrieved ticket must be used to invoke F3Participate to actively engage
// in the F3 consensus process.
F3GetOrRenewParticipationTicket(ctx context.Context, minerID address.Address, previous F3ParticipationTicket) (F3ParticipationTicket, error) //perm:sign
// F3Participate enrolls a storage provider in the F3 consensus process using a
// provided participation ticket. This ticket grants a temporary lease that enables
// the provider to sign transactions as part of the F3 consensus.
//
// The function verifies the ticket's validity and checks if the ticket's issuer
// aligns with the current node. If there is an issuer mismatch
// (ErrF3ParticipationIssuerMismatch), the provider should retry with the same
// ticket, assuming the issue is due to transient network problems or operational
// deployment conditions. If the ticket is invalid
// (ErrF3ParticipationTicketInvalid) or has expired
// (ErrF3ParticipationTicketExpired), the provider must obtain a new ticket by
// calling F3GetOrRenewParticipationTicket.
//
// For details on obtaining or renewing a ticket, see F3GetOrRenewParticipationTicket.
F3Participate(ctx context.Context, ticket F3ParticipationTicket) (F3ParticipationLease, error) //perm:sign
// F3GetCertificate returns a finality certificate at given instance.
F3GetCertificate(ctx context.Context, instance uint64) (*certs.FinalityCertificate, error) //perm:read
// F3GetLatestCertificate returns the latest finality certificate
// F3GetLatestCertificate returns the latest finality certificate.
F3GetLatestCertificate(ctx context.Context) (*certs.FinalityCertificate, error) //perm:read
// F3GetGetManifest returns the current manifest being used for F3
// F3GetManifest returns the current manifest being used for F3 operations.
F3GetManifest(ctx context.Context) (*manifest.Manifest, error) //perm:read
// F3GetECPowerTable returns a F3 specific power table for use in standalone F3 nodes.
F3GetECPowerTable(ctx context.Context, tsk types.TipSetKey) (gpbft.PowerEntries, error) //perm:read
Expand All @@ -936,6 +957,39 @@ type FullNode interface {
// F3IsRunning returns true if the F3 instance is running, false if it's not running but
// it's enabled, and an error when disabled entirely.
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) (F3Progress, error) //perm:read
}

// F3ParticipationTicket represents a ticket that authorizes a miner to
// participate in the F3 consensus.
type F3ParticipationTicket []byte

// F3ParticipationLease defines the lease granted to a storage provider for
// participating in F3 consensus, detailing the session identifier, issuer,
// subject, and the expiration instance.
type F3ParticipationLease struct {
// Session uniquely identifies the session in which this lease is issued. This
// information can then be used to infer if the issuer of the ticket has restarted
// since the lease was issued.
Session uuid.UUID
// Issuer is the identity of the node that issued the lease.
Issuer peer.ID
// MinerID is the actor ID of the miner that holds the lease.
MinerID uint64
// ExpireAfterInstance is the GPBFT instance ID, beyond which this lease expires.
// See F3Progress.
ExpireAfterInstance uint64
}

// F3Progress encapsulates the current progress of the F3 instance, specifying
// the instance ID, round, and the current phase of the consensus process.
//
// See FullNode.F3GetProgress.
type F3Progress struct {
Instance uint64
Round uint64
Phase gpbft.Phase
}

// EthSubscriber is the reverse interface to the client, called after EthSubscribe
Expand Down
Loading

0 comments on commit bc978d4

Please sign in to comment.