Skip to content

Commit

Permalink
Implement token based F3 participation lease
Browse files Browse the repository at this point in the history
Implemented enhanced token-based participation system for F3 consensus
in `F3Participate`. This update introduces a new design where
participation tokens grant a temporary lease, allowing storage providers
to sign as part of the F3 consensus mechanism. This design ensures that
tokens 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 token. If the token is invalid or expired,
miners are directed to obtain a new token via `F3GetParticipationToken`.

Fixes filecoin-project/go-f3#599
  • Loading branch information
masih committed Oct 1, 2024
1 parent 941f049 commit e107151
Show file tree
Hide file tree
Showing 22 changed files with 1,634 additions and 854 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
63 changes: 49 additions & 14 deletions api/api_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,32 @@ import (
const (
EOutOfGas = iota + jsonrpc.FirstUserCode
EActorNotFound
EF3Disabled
EF3ParticipationTokenInvalid
EF3ParticipationTokenExpired
EF3ParticipationIssuerMismatch
)

type ErrOutOfGas struct{}

func (e *ErrOutOfGas) Error() string {
return "call ran out of gas"
}
var (
RPCErrors = jsonrpc.NewErrors()

type ErrActorNotFound struct{}
_ error = (*ErrOutOfGas)(nil)
_ error = (*ErrActorNotFound)(nil)
_ error = (*ErrF3Disabled)(nil)
_ error = (*ErrF3ParticipationTokenInvalid)(nil)
_ error = (*ErrF3ParticipationTokenExpired)(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(EF3ParticipationTokenInvalid, new(*ErrF3ParticipationTokenInvalid))
RPCErrors.Register(EF3ParticipationTokenExpired, new(*ErrF3ParticipationTokenExpired))
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 +46,32 @@ 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 (e *ErrOutOfGas) Error() string { return "call ran out of gas" }

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

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

// ErrF3Disabled signals that F3 consensus process is disabled.
type ErrF3Disabled struct{}

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

// ErrF3ParticipationTokenInvalid signals that F3ParticipationToken cannot be decoded.
type ErrF3ParticipationTokenInvalid struct{}

func (e ErrF3ParticipationTokenInvalid) Error() string { return "token is not valid" }

// ErrF3ParticipationTokenExpired signals that the current GPBFT instance as surpassed the expiry of the token.
type ErrF3ParticipationTokenExpired struct{}

func (e ErrF3ParticipationTokenExpired) Error() string { return "token has expired" }

// ErrF3ParticipationIssuerMismatch signals that the token is not issued by the current node.
type ErrF3ParticipationIssuerMismatch struct{}

func (e ErrF3ParticipationIssuerMismatch) Error() string { return "issuer does not match current node" }
85 changes: 69 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,42 @@ 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
// F3GetOrRenewParticipationToken retrieves or renews a participation token
// necessary for a miner to engage in the F3 consensus process.
//
// This function accepts an optional previous token. If provided, a new token
// will be issued only under one the following conditions:
// 1. The previous token has expired.
// 2. The issuer of the previous token matches the node processing this request.
//
// If there is an issuer mismatch (ErrF3ParticipationIssuerMismatch), the miner
// must retry obtaining a new token to ensure it is only participating in one F3
// instance at any time.
//
// Note: Successfully acquiring a token alone does not constitute participation.
// The retrieved token must be used to invoke F3Participate to actively engage in
// the F3 consensus process.
F3GetOrRenewParticipationToken(ctx context.Context, minerID address.Address, previous F3ParticipationToken) (F3ParticipationToken, error) //perm:sign
// F3Participate enrolls a storage provider in the F3 consensus process using a
// provided participation token. This token grants a temporary lease that enables
// the provider to sign transactions as part of the F3 consensus.
//
// The function verifies the token's validity and checks if the token's issuer
// aligns with the current node. If there is an issuer mismatch
// (ErrF3ParticipationIssuerMismatch), the provider should retry with the same
// token, assuming the issue is due to transient network problems or operational
// deployment conditions. If the token is invalid
// (ErrF3ParticipationTokenInvalid) or has expired
// (ErrF3ParticipationTokenExpired), the provider must obtain a new token by
// calling F3GetOrRenewParticipationToken.
//
// For details on obtaining or renewing a token, see F3GetOrRenewParticipationToken.
F3Participate(ctx context.Context, token F3ParticipationToken) (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 +956,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
}

// F3ParticipationToken represents a binary token that authorizes a miner to
// participate in the F3 consensus.
type F3ParticipationToken []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 token has restarted
// since the lease was issued.
Session uuid.UUID
// Issuer is the identity of the node that issued the lease.
Issuer peer.ID
// Subject is the actor ID of the lease holder.
Subject 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 e107151

Please sign in to comment.