From d7d37d087ba90e204ea12bac629730a0ca2d939b Mon Sep 17 00:00:00 2001 From: "Masih H. Derkani" Date: Thu, 24 Oct 2024 15:31:38 +0100 Subject: [PATCH] Fix F3 JSON RPC error pass through across API boundary Fix the issue by instantiating pointers to sentinel F3 error values and assert that errors indeed pass through via an integration test. Fixes #12630 --- api/api_errors.go | 14 +++++++------- itests/f3_test.go | 44 ++++++++++++++++++++++++++++++++------------ 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/api/api_errors.go b/api/api_errors.go index c5cce58ad56..a46cfe6c26d 100644 --- a/api/api_errors.go +++ b/api/api_errors.go @@ -23,22 +23,22 @@ var ( RPCErrors = jsonrpc.NewErrors() // ErrF3Disabled signals that F3 consensus process is disabled. - ErrF3Disabled = errF3Disabled{} + ErrF3Disabled = &errF3Disabled{} // ErrF3ParticipationTicketInvalid signals that F3ParticipationTicket cannot be decoded. - ErrF3ParticipationTicketInvalid = errF3ParticipationTicketInvalid{} + ErrF3ParticipationTicketInvalid = &errF3ParticipationTicketInvalid{} // ErrF3ParticipationTicketExpired signals that the current GPBFT instance as surpassed the expiry of the ticket. - ErrF3ParticipationTicketExpired = errF3ParticipationTicketExpired{} + ErrF3ParticipationTicketExpired = &errF3ParticipationTicketExpired{} // ErrF3ParticipationIssuerMismatch signals that the ticket is not issued by the current node. - ErrF3ParticipationIssuerMismatch = errF3ParticipationIssuerMismatch{} + ErrF3ParticipationIssuerMismatch = &errF3ParticipationIssuerMismatch{} // ErrF3ParticipationTooManyInstances signals that participation ticket cannot be // issued because it asks for too many instances. - ErrF3ParticipationTooManyInstances = errF3ParticipationTooManyInstances{} + ErrF3ParticipationTooManyInstances = &errF3ParticipationTooManyInstances{} // ErrF3ParticipationTicketStartBeforeExisting signals that participation ticket // is before the start instance of an existing lease held by the miner. - ErrF3ParticipationTicketStartBeforeExisting = errF3ParticipationTicketStartBeforeExisting{} + 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{} + ErrF3NotReady = &errF3NotReady{} _ error = (*ErrOutOfGas)(nil) _ error = (*ErrActorNotFound)(nil) diff --git a/itests/f3_test.go b/itests/f3_test.go index 6318406e604..31b2239dabf 100644 --- a/itests/f3_test.go +++ b/itests/f3_test.go @@ -18,6 +18,7 @@ import ( "github.com/filecoin-project/go-f3/manifest" "github.com/filecoin-project/go-state-types/abi" + lotus_api "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/lf3" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/itests/kit" @@ -157,6 +158,23 @@ func TestF3_Bootstrap(t *testing.T) { e.requireAllMinersParticipate() } +func TestF3_JsonRPCErrorsPassThrough(t *testing.T) { + const blocktime = 100 * time.Millisecond + e := setup(t, blocktime, kit.ThroughRPC()) + n := e.nodes[0].FullNode + + lease, err := n.F3Participate(e.testCtx, []byte("fish")) + require.ErrorIs(t, err, lotus_api.ErrF3ParticipationTicketInvalid) + require.Zero(t, lease) + + addr, err := address.NewIDAddress(1413) + require.NoError(t, err) + + ticket, err := n.F3GetOrRenewParticipationTicket(e.testCtx, addr, nil, 100) + require.ErrorIs(t, err, lotus_api.ErrF3ParticipationTooManyInstances) + require.Zero(t, ticket) +} + func (e *testEnv) waitTillF3Rebootstrap(timeout time.Duration) { e.waitFor(func(n *kit.TestFullNode) bool { // the prev epoch yet, check if we already bootstrapped and from @@ -289,8 +307,8 @@ func (e *testEnv) waitFor(f func(n *kit.TestFullNode) bool, timeout time.Duratio // The first node returned by the function is directly connected to a miner, // and the second full-node is an observer that is not directly connected to // a miner. The last return value is the manifest sender for the network. -func setup(t *testing.T, blocktime time.Duration) *testEnv { - return setupWithStaticManifest(t, newTestManifest(BaseNetworkName+"/1", DefaultBootstrapEpoch, blocktime), false) +func setup(t *testing.T, blocktime time.Duration, opts ...kit.NodeOpt) *testEnv { + return setupWithStaticManifest(t, newTestManifest(BaseNetworkName+"/1", DefaultBootstrapEpoch, blocktime), false, opts...) } func newTestManifest(networkName gpbft.NetworkName, bootstrapEpoch int64, blocktime time.Duration) *manifest.Manifest { @@ -328,7 +346,7 @@ func newTestManifest(networkName gpbft.NetworkName, bootstrapEpoch int64, blockt } } -func setupWithStaticManifest(t *testing.T, manif *manifest.Manifest, testBootstrap bool) *testEnv { +func setupWithStaticManifest(t *testing.T, manif *manifest.Manifest, testBootstrap bool, extraOpts ...kit.NodeOpt) *testEnv { ctx, stopServices := context.WithCancel(context.Background()) errgrp, ctx := errgroup.WithContext(ctx) @@ -351,8 +369,10 @@ func setupWithStaticManifest(t *testing.T, manif *manifest.Manifest, testBootstr AllowDynamicFinalize: !testBootstrap, } - f3NOpt := kit.F3Enabled(cfg) - f3MOpt := kit.ConstructorOpts(node.Override(node.F3Participation, modules.F3Participation)) + nodeOpts := []kit.NodeOpt{kit.WithAllSubsystems(), kit.F3Enabled(cfg)} + nodeOpts = append(nodeOpts, extraOpts...) + minerOpts := []kit.NodeOpt{kit.WithAllSubsystems(), kit.ConstructorOpts(node.Override(node.F3Participation, modules.F3Participation))} + minerOpts = append(minerOpts, extraOpts...) var ( n1, n2, n3 kit.TestFullNode @@ -360,13 +380,13 @@ func setupWithStaticManifest(t *testing.T, manif *manifest.Manifest, testBootstr ) ens := kit.NewEnsemble(t, kit.MockProofs()). - FullNode(&n1, kit.WithAllSubsystems(), f3NOpt). - FullNode(&n2, kit.WithAllSubsystems(), f3NOpt). - FullNode(&n3, kit.WithAllSubsystems(), f3NOpt). - Miner(&m1, &n1, kit.WithAllSubsystems(), f3MOpt). - Miner(&m2, &n2, kit.WithAllSubsystems(), f3MOpt). - Miner(&m3, &n3, kit.WithAllSubsystems(), f3MOpt). - Miner(&m4, &n3, kit.WithAllSubsystems(), f3MOpt). + FullNode(&n1, nodeOpts...). + FullNode(&n2, nodeOpts...). + FullNode(&n3, nodeOpts...). + Miner(&m1, &n1, minerOpts...). + Miner(&m2, &n2, minerOpts...). + Miner(&m3, &n3, minerOpts...). + Miner(&m4, &n3, minerOpts...). Start() ens.InterconnectAll().BeginMining(blocktime)