mirror of
https://github.com/filecoin-project/lotus.git
synced 2025-08-14 17:50:33 +08:00
feat: implement ticket based F3 participation lease (#12531)
* Implement ticket based F3 participation lease 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 https://github.com/filecoin-project/go-f3/issues/599 * Use fresh timer every time for F3 backoffs To avoid potential of deadlock in case f3Participator is used from multiple goroutines use throw-away timers at the price of higher GC. Also use the cancel function in context explicitly in a unified stop hook that awaits the participation to end before exiting. * Strictly require start instance to never decrease Require the start instance of a participation to never decrease if there is an existing lease by the miner. * feat(f3): update go-f3 to 0.7.0 and adapt for changes to the API * feat(f3): Include the network name in the lease That way we don't re-use leases across networks. It's a bit racy (we ask for the manifest before we ask for the current progress) but it should be fine because at least we won't create a lease for the new network with a future instance. There's still an ABA problem if we rapidly switch back and forth between two networks but... let's just not do that? At least for the mainnet switchover, that won't be an issue because we enforce a 900 epoch silence period. I have to say, I'm not happy about this. But... we can probably just hard-code it in the future once we get rid of the dynamic manifest. * Handle not ready error gracefully in participator Back off and get a fresh token if F3 is not ready. --------- Co-authored-by: Steven Allen <steven@stebalien.com>
This commit is contained in:
@ -10,22 +10,57 @@ import (
|
||||
const (
|
||||
EOutOfGas = iota + jsonrpc.FirstUserCode
|
||||
EActorNotFound
|
||||
EF3Disabled
|
||||
EF3ParticipationTicketInvalid
|
||||
EF3ParticipationTicketExpired
|
||||
EF3ParticipationIssuerMismatch
|
||||
EF3ParticipationTooManyInstances
|
||||
EF3ParticipationTicketStartBeforeExisting
|
||||
EF3NotReady
|
||||
)
|
||||
|
||||
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{}
|
||||
// ErrF3ParticipationTooManyInstances signals that participation ticket cannot be
|
||||
// issued because it asks for too many instances.
|
||||
ErrF3ParticipationTooManyInstances = errF3ParticipationTooManyInstances{}
|
||||
// ErrF3ParticipationTicketStartBeforeExisting signals that participation ticket
|
||||
// is before the start instance of an existing lease held by the miner.
|
||||
ErrF3ParticipationTicketStartBeforeExisting = errF3ParticipationTicketStartBeforeExisting{}
|
||||
// ErrF3NotReady signals that the F3 instance isn't ready for participation yet. The caller
|
||||
// should back off and try again later.
|
||||
ErrF3NotReady = errF3NotReady{}
|
||||
|
||||
_ error = (*ErrOutOfGas)(nil)
|
||||
_ error = (*ErrActorNotFound)(nil)
|
||||
_ error = (*errF3Disabled)(nil)
|
||||
_ error = (*errF3ParticipationTicketInvalid)(nil)
|
||||
_ error = (*errF3ParticipationTicketExpired)(nil)
|
||||
_ error = (*errF3ParticipationIssuerMismatch)(nil)
|
||||
_ error = (*errF3NotReady)(nil)
|
||||
)
|
||||
|
||||
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))
|
||||
RPCErrors.Register(EF3ParticipationTooManyInstances, new(*errF3ParticipationTooManyInstances))
|
||||
RPCErrors.Register(EF3ParticipationTicketStartBeforeExisting, new(*errF3ParticipationTicketStartBeforeExisting))
|
||||
RPCErrors.Register(EF3NotReady, new(*errF3NotReady))
|
||||
}
|
||||
|
||||
type ErrActorNotFound struct{}
|
||||
|
||||
func (e *ErrActorNotFound) Error() string {
|
||||
return "actor not found"
|
||||
}
|
||||
|
||||
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()
|
||||
@ -36,7 +71,42 @@ 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" }
|
||||
|
||||
type errF3ParticipationTooManyInstances struct{}
|
||||
|
||||
func (errF3ParticipationTooManyInstances) Error() string { return "requested instance count too high" }
|
||||
|
||||
type errF3ParticipationTicketStartBeforeExisting struct{}
|
||||
|
||||
func (errF3ParticipationTicketStartBeforeExisting) Error() string {
|
||||
return "ticket starts before existing lease"
|
||||
}
|
||||
|
||||
type errF3NotReady struct{}
|
||||
|
||||
func (errF3NotReady) Error() string { return "f3 isn't yet ready to participate" }
|
||||
|
Reference in New Issue
Block a user