Merge pull request #9384 from filecoin-project/backport/asr/rpc-errors

Backport: #9061 rpc errors
This commit is contained in:
Shrenuj Bansal 2022-09-27 11:59:10 -04:00 committed by GitHub
commit d11248b998
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 168 additions and 69 deletions

42
api/api_errors.go Normal file
View File

@ -0,0 +1,42 @@
package api
import (
"errors"
"reflect"
"github.com/filecoin-project/go-jsonrpc"
)
const (
EOutOfGas = iota + jsonrpc.FirstUserCode
EActorNotFound
)
type ErrOutOfGas struct{}
func (e *ErrOutOfGas) Error() string {
return "call ran out of gas"
}
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()
if errors.As(err, tmp) {
return true
}
}
return false
}
func init() {
RPCErrors.Register(EOutOfGas, new(*ErrOutOfGas))
RPCErrors.Register(EActorNotFound, new(*ErrActorNotFound))
}

View File

@ -12,6 +12,9 @@ import (
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc"
) )
func goCmd() string { func goCmd() string {
@ -124,3 +127,18 @@ func TestPermTags(t *testing.T) {
_ = PermissionedStorMinerAPI(&StorageMinerStruct{}) _ = PermissionedStorMinerAPI(&StorageMinerStruct{})
_ = PermissionedWorkerAPI(&WorkerStruct{}) _ = PermissionedWorkerAPI(&WorkerStruct{})
} }
func TestRetryErrorIsInTrue(t *testing.T) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}}
require.True(t, ErrorIsIn(&jsonrpc.RPCConnectionError{}, errorsToRetry))
}
func TestRetryErrorIsInFalse(t *testing.T) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}}
require.False(t, ErrorIsIn(xerrors.Errorf("random error"), errorsToRetry))
}
func TestRetryWrappedErrorIsInTrue(t *testing.T) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}}
require.True(t, ErrorIsIn(xerrors.Errorf("wrapped: %w", &jsonrpc.RPCConnectionError{}), errorsToRetry))
}

View File

@ -19,7 +19,7 @@ import (
func NewCommonRPCV0(ctx context.Context, addr string, requestHeader http.Header) (api.CommonNet, jsonrpc.ClientCloser, error) { func NewCommonRPCV0(ctx context.Context, addr string, requestHeader http.Header) (api.CommonNet, jsonrpc.ClientCloser, error) {
var res v0api.CommonNetStruct var res v0api.CommonNetStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader) api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))
return &res, closer, err return &res, closer, err
} }
@ -29,7 +29,7 @@ func NewFullNodeRPCV0(ctx context.Context, addr string, requestHeader http.Heade
var res v0api.FullNodeStruct var res v0api.FullNodeStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader) api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))
return &res, closer, err return &res, closer, err
} }
@ -38,7 +38,7 @@ func NewFullNodeRPCV0(ctx context.Context, addr string, requestHeader http.Heade
func NewFullNodeRPCV1(ctx context.Context, addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) { func NewFullNodeRPCV1(ctx context.Context, addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) {
var res v1api.FullNodeStruct var res v1api.FullNodeStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader) api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))
return &res, closer, err return &res, closer, err
} }
@ -72,6 +72,7 @@ func NewStorageMinerRPCV0(ctx context.Context, addr string, requestHeader http.H
api.GetInternalStructs(&res), requestHeader, api.GetInternalStructs(&res), requestHeader,
append([]jsonrpc.Option{ append([]jsonrpc.Option{
rpcenc.ReaderParamEncoder(pushUrl), rpcenc.ReaderParamEncoder(pushUrl),
jsonrpc.WithErrors(api.RPCErrors),
}, opts...)...) }, opts...)...)
return &res, closer, err return &res, closer, err
@ -90,6 +91,7 @@ func NewWorkerRPCV0(ctx context.Context, addr string, requestHeader http.Header)
rpcenc.ReaderParamEncoder(pushUrl), rpcenc.ReaderParamEncoder(pushUrl),
jsonrpc.WithNoReconnect(), jsonrpc.WithNoReconnect(),
jsonrpc.WithTimeout(30*time.Second), jsonrpc.WithTimeout(30*time.Second),
jsonrpc.WithErrors(api.RPCErrors),
) )
return &res, closer, err return &res, closer, err
@ -101,7 +103,7 @@ func NewGatewayRPCV1(ctx context.Context, addr string, requestHeader http.Header
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), api.GetInternalStructs(&res),
requestHeader, requestHeader,
opts..., append(opts, jsonrpc.WithErrors(api.RPCErrors))...,
) )
return &res, closer, err return &res, closer, err
@ -113,7 +115,7 @@ func NewGatewayRPCV0(ctx context.Context, addr string, requestHeader http.Header
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), api.GetInternalStructs(&res),
requestHeader, requestHeader,
opts..., append(opts, jsonrpc.WithErrors(api.RPCErrors))...,
) )
return &res, closer, err return &res, closer, err
@ -124,6 +126,7 @@ func NewWalletRPCV0(ctx context.Context, addr string, requestHeader http.Header)
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), api.GetInternalStructs(&res),
requestHeader, requestHeader,
jsonrpc.WithErrors(api.RPCErrors),
) )
return &res, closer, err return &res, closer, err

View File

@ -118,8 +118,9 @@ const VerifSigCacheSize = 32000
// TODO: If this is gonna stay, it should move to specs-actors // TODO: If this is gonna stay, it should move to specs-actors
const BlockMessageLimit = 10000 const BlockMessageLimit = 10000
const BlockGasLimit = 10_000_000_000 var BlockGasLimit = int64(10_000_000_000)
const BlockGasTarget = BlockGasLimit / 2 var BlockGasTarget = BlockGasLimit / 2
const BaseFeeMaxChangeDenom = 8 // 12.5% const BaseFeeMaxChangeDenom = 8 // 12.5%
const InitialBaseFee = 100e6 const InitialBaseFee = 100e6
const MinimumBaseFee = 100 const MinimumBaseFee = 100

View File

@ -79,7 +79,7 @@ func (mp *MessagePool) republishPendingMessages(ctx context.Context) error {
return chains[i].Before(chains[j]) return chains[i].Before(chains[j])
}) })
gasLimit := int64(build.BlockGasLimit) gasLimit := build.BlockGasLimit
minGas := int64(gasguess.MinGas) minGas := int64(gasguess.MinGas)
var msgs []*types.SignedMessage var msgs []*types.SignedMessage
loop: loop:

View File

@ -258,7 +258,7 @@ func (mp *MessagePool) selectMessagesOptimal(ctx context.Context, curTs, ts *typ
nextChain := 0 nextChain := 0
partitions := make([][]*msgChain, MaxBlocks) partitions := make([][]*msgChain, MaxBlocks)
for i := 0; i < MaxBlocks && nextChain < len(chains); i++ { for i := 0; i < MaxBlocks && nextChain < len(chains); i++ {
gasLimit := int64(build.BlockGasLimit) gasLimit := build.BlockGasLimit
msgLimit := build.BlockMessageLimit msgLimit := build.BlockMessageLimit
for nextChain < len(chains) { for nextChain < len(chains) {
chain := chains[nextChain] chain := chains[nextChain]
@ -590,7 +590,7 @@ func (mp *MessagePool) selectPriorityMessages(ctx context.Context, pending map[a
mpCfg := mp.getConfig() mpCfg := mp.getConfig()
result := &selectedMessages{ result := &selectedMessages{
msgs: make([]*types.SignedMessage, 0, mpCfg.SizeLimitLow), msgs: make([]*types.SignedMessage, 0, mpCfg.SizeLimitLow),
gasLimit: int64(build.BlockGasLimit), gasLimit: build.BlockGasLimit,
blsLimit: cbg.MaxLength, blsLimit: cbg.MaxLength,
secpLimit: cbg.MaxLength, secpLimit: cbg.MaxLength,
} }

View File

@ -25,7 +25,7 @@ func TestBaseFee(t *testing.T) {
{100e6, build.BlockGasTarget, 1, 103.125e6, 100e6}, {100e6, build.BlockGasTarget, 1, 103.125e6, 100e6},
{100e6, build.BlockGasTarget * 2, 2, 103.125e6, 100e6}, {100e6, build.BlockGasTarget * 2, 2, 103.125e6, 100e6},
{100e6, build.BlockGasLimit * 2, 2, 112.5e6, 112.5e6}, {100e6, build.BlockGasLimit * 2, 2, 112.5e6, 112.5e6},
{100e6, build.BlockGasLimit * 1.5, 2, 110937500, 106.250e6}, {100e6, (build.BlockGasLimit * 15) / 10, 2, 110937500, 106.250e6},
} }
for _, test := range tests { for _, test := range tests {

View File

@ -31,12 +31,13 @@ const (
// has. // has.
// 5 per tipset, but we effectively get 4 blocks worth of messages. // 5 per tipset, but we effectively get 4 blocks worth of messages.
expectedBlocks = 4 expectedBlocks = 4
// TODO: This will produce invalid blocks but it will accurately model the amount of gas
// we're willing to use per-tipset.
// A more correct approach would be to produce 5 blocks. We can do that later.
targetGas = build.BlockGasTarget * expectedBlocks
) )
// TODO: This will produce invalid blocks but it will accurately model the amount of gas
// we're willing to use per-tipset.
// A more correct approach would be to produce 5 blocks. We can do that later.
var targetGas = build.BlockGasTarget * expectedBlocks
type BlockBuilder struct { type BlockBuilder struct {
ctx context.Context ctx context.Context
logger *zap.SugaredLogger logger *zap.SugaredLogger

View File

@ -209,7 +209,7 @@ var runCmd = &cli.Command{
rpcApi = api.PermissionedWalletAPI(rpcApi) rpcApi = api.PermissionedWalletAPI(rpcApi)
} }
rpcServer := jsonrpc.NewServer() rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors))
rpcServer.Register("Filecoin", rpcApi) rpcServer.Register("Filecoin", rpcApi)
mux.Handle("/rpc/v0", rpcServer) mux.Handle("/rpc/v0", rpcServer)

View File

@ -29,7 +29,7 @@ var log = logging.Logger("sealworker")
func WorkerHandler(authv func(ctx context.Context, token string) ([]auth.Permission, error), remote http.HandlerFunc, a api.Worker, permissioned bool) http.Handler { func WorkerHandler(authv func(ctx context.Context, token string) ([]auth.Permission, error), remote http.HandlerFunc, a api.Worker, permissioned bool) http.Handler {
mux := mux.NewRouter() mux := mux.NewRouter()
readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder() readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder()
rpcServer := jsonrpc.NewServer(readerServerOpt) rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors), readerServerOpt)
wapi := proxy.MetricedWorkerAPI(a) wapi := proxy.MetricedWorkerAPI(a)
if permissioned { if permissioned {

View File

@ -30,7 +30,7 @@ import (
"github.com/filecoin-project/go-jsonrpc" "github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-paramfetch" "github.com/filecoin-project/go-paramfetch"
"github.com/filecoin-project/lotus/api" lapi "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/consensus/filcns" "github.com/filecoin-project/lotus/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/stmgr"
@ -303,7 +303,7 @@ var DaemonCmd = &cli.Command{
} }
defer closer() defer closer()
liteModeDeps = node.Override(new(api.Gateway), gapi) liteModeDeps = node.Override(new(lapi.Gateway), gapi)
} }
// some libraries like ipfs/go-ds-measure and ipfs/go-ipfs-blockstore // some libraries like ipfs/go-ds-measure and ipfs/go-ipfs-blockstore
@ -313,7 +313,7 @@ var DaemonCmd = &cli.Command{
log.Warnf("unable to inject prometheus ipfs/go-metrics exporter; some metrics will be unavailable; err: %s", err) log.Warnf("unable to inject prometheus ipfs/go-metrics exporter; some metrics will be unavailable; err: %s", err)
} }
var api api.FullNode var api lapi.FullNode
stop, err := node.New(ctx, stop, err := node.New(ctx,
node.FullAPI(&api, node.Lite(isLite)), node.FullAPI(&api, node.Lite(isLite)),
@ -360,7 +360,7 @@ var DaemonCmd = &cli.Command{
// ---- // ----
// Populate JSON-RPC options. // Populate JSON-RPC options.
serverOptions := make([]jsonrpc.ServerOption, 0) serverOptions := []jsonrpc.ServerOption{jsonrpc.WithServerErrors(lapi.RPCErrors)}
if maxRequestSize := cctx.Int("api-max-req-size"); maxRequestSize != 0 { if maxRequestSize := cctx.Int("api-max-req-size"); maxRequestSize != 0 {
serverOptions = append(serverOptions, jsonrpc.WithMaxRequestSize(int64(maxRequestSize))) serverOptions = append(serverOptions, jsonrpc.WithMaxRequestSize(int64(maxRequestSize)))
} }
@ -392,7 +392,7 @@ var DaemonCmd = &cli.Command{
}, },
} }
func importKey(ctx context.Context, api api.FullNode, f string) error { func importKey(ctx context.Context, api lapi.FullNode, f string) error {
f, err := homedir.Expand(f) f, err := homedir.Expand(f)
if err != nil { if err != nil {
return err return err

View File

@ -30,7 +30,7 @@ func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinu
m := mux.NewRouter() m := mux.NewRouter()
serveRpc := func(path string, hnd interface{}) { serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(opts...) rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(lapi.RPCErrors))...)
rpcServer.Register("Filecoin", hnd) rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")

2
go.mod
View File

@ -38,7 +38,7 @@ require (
github.com/filecoin-project/go-fil-commcid v0.1.0 github.com/filecoin-project/go-fil-commcid v0.1.0
github.com/filecoin-project/go-fil-commp-hashhash v0.1.0 github.com/filecoin-project/go-fil-commp-hashhash v0.1.0
github.com/filecoin-project/go-fil-markets v1.24.0 github.com/filecoin-project/go-fil-markets v1.24.0
github.com/filecoin-project/go-jsonrpc v0.1.7 github.com/filecoin-project/go-jsonrpc v0.1.8
github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-legs v0.4.4
github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-padreader v0.0.1
github.com/filecoin-project/go-paramfetch v0.0.4 github.com/filecoin-project/go-paramfetch v0.0.4

4
go.sum
View File

@ -327,8 +327,8 @@ github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+
github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI= github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI=
github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI=
github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g=
github.com/filecoin-project/go-jsonrpc v0.1.7 h1:Ti/QkQLI31v+6hvidA+i9Wv/NrS4CfHk0yXLntHX3Uk= github.com/filecoin-project/go-jsonrpc v0.1.8 h1:uXX/ikAk3Q4f/k8DRd9Zw+fWnfiYb5I+UI1tzlQgHog=
github.com/filecoin-project/go-jsonrpc v0.1.7/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= github.com/filecoin-project/go-jsonrpc v0.1.8/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
github.com/filecoin-project/go-legs v0.4.4 h1:mpMmAOOnamaz0CV9rgeKhEWA8j9kMC+f+UGCGrxKaZo= github.com/filecoin-project/go-legs v0.4.4 h1:mpMmAOOnamaz0CV9rgeKhEWA8j9kMC+f+UGCGrxKaZo=
github.com/filecoin-project/go-legs v0.4.4/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s= github.com/filecoin-project/go-legs v0.4.4/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s=
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak=

View File

@ -10,6 +10,7 @@ import (
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
@ -51,6 +52,8 @@ func runAPITest(t *testing.T, opts ...interface{}) {
t.Run("testMiningReal", ts.testMiningReal) t.Run("testMiningReal", ts.testMiningReal)
t.Run("testSlowNotify", ts.testSlowNotify) t.Run("testSlowNotify", ts.testSlowNotify)
t.Run("testSearchMsg", ts.testSearchMsg) t.Run("testSearchMsg", ts.testSearchMsg)
t.Run("testOutOfGasError", ts.testOutOfGasError)
t.Run("testLookupNotFoundError", ts.testLookupNotFoundError)
t.Run("testNonGenesisMiner", ts.testNonGenesisMiner) t.Run("testNonGenesisMiner", ts.testNonGenesisMiner)
} }
@ -149,6 +152,46 @@ func (ts *apiSuite) testSearchMsg(t *testing.T) {
require.Equalf(t, res.TipSet, searchRes.TipSet, "search ts: %s, different from wait ts: %s", searchRes.TipSet, res.TipSet) require.Equalf(t, res.TipSet, searchRes.TipSet, "search ts: %s, different from wait ts: %s", searchRes.TipSet, res.TipSet)
} }
func (ts *apiSuite) testOutOfGasError(t *testing.T) {
ctx := context.Background()
full, _, _ := kit.EnsembleMinimal(t, ts.opts...)
senderAddr, err := full.WalletDefaultAddress(ctx)
require.NoError(t, err)
// the gas estimator API executes the message with gasLimit = BlockGasLimit
// Lowering it to 2 will cause it to run out of gas, testing the failure case we want
originalLimit := build.BlockGasLimit
build.BlockGasLimit = 2
defer func() {
build.BlockGasLimit = originalLimit
}()
msg := &types.Message{
From: senderAddr,
To: senderAddr,
Value: big.Zero(),
}
_, err = full.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK)
require.Error(t, err, "should have failed")
require.True(t, xerrors.Is(err, &lapi.ErrOutOfGas{}))
}
func (ts *apiSuite) testLookupNotFoundError(t *testing.T) {
ctx := context.Background()
full, _, _ := kit.EnsembleMinimal(t, ts.opts...)
addr, err := full.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err)
_, err = full.StateLookupID(ctx, addr, types.EmptyTSK)
require.Error(t, err)
require.True(t, xerrors.Is(err, &lapi.ErrActorNotFound{}))
}
func (ts *apiSuite) testMining(t *testing.T) { func (ts *apiSuite) testMining(t *testing.T) {
ctx := context.Background() ctx := context.Background()

View File

@ -13,6 +13,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-bitfield"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/builtin"
minertypes "github.com/filecoin-project/go-state-types/builtin/v8/miner" minertypes "github.com/filecoin-project/go-state-types/builtin/v8/miner"
@ -191,7 +192,7 @@ func (bm *BlockMiner) MineBlocksMustPost(ctx context.Context, blocktime time.Dur
reportSuccessFn := func(success bool, epoch abi.ChainEpoch, err error) { reportSuccessFn := func(success bool, epoch abi.ChainEpoch, err error) {
// if api shuts down before mining, we may get an error which we should probably just ignore // if api shuts down before mining, we may get an error which we should probably just ignore
// (fixing it will require rewriting most of the mining loop) // (fixing it will require rewriting most of the mining loop)
if err != nil && !strings.Contains(err.Error(), "websocket connection closed") { if err != nil && !strings.Contains(err.Error(), "websocket connection closed") && !api.ErrorIsIn(err, []error{new(jsonrpc.RPCConnectionError)}) {
require.NoError(bm.t, err) require.NoError(bm.t, err)
} }

View File

@ -13,6 +13,7 @@ import (
cbor "github.com/ipfs/go-ipld-cbor" cbor "github.com/ipfs/go-ipld-cbor"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
@ -66,6 +67,21 @@ func TestPaymentChannelsBasic(t *testing.T) {
vamt := strconv.Itoa(voucherAmt) vamt := strconv.Itoa(voucherAmt)
voucher := creatorCLI.RunCmd("paych", "voucher", "create", chAddr.String(), vamt) voucher := creatorCLI.RunCmd("paych", "voucher", "create", chAddr.String(), vamt)
// DEFLAKE: We have observed this test flakily failing when the receiver node hasn't seen the paych create message
// This makes us wait as much as 10 epochs before giving up and failing
retry := 0
_, err = paymentReceiver.StateLookupID(ctx, chAddr, types.EmptyTSK)
for err != nil && xerrors.Is(err, &api.ErrActorNotFound{}) {
time.Sleep(blocktime)
_, err = paymentReceiver.StateLookupID(ctx, chAddr, types.EmptyTSK)
retry++
if retry > 10 {
break
}
}
require.NoError(t, err)
// receiver: paych voucher add <channel> <voucher> // receiver: paych voucher add <channel> <voucher>
receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher) receiverCLI.RunCmd("paych", "voucher", "add", chAddr.String(), voucher)

View File

@ -1,25 +1,15 @@
package retry package retry
import ( import (
"errors"
"reflect"
"time" "time"
logging "github.com/ipfs/go-log/v2" logging "github.com/ipfs/go-log/v2"
"github.com/filecoin-project/lotus/api"
) )
var log = logging.Logger("retry") var log = logging.Logger("retry")
func ErrorIsIn(err error, errorTypes []error) bool {
for _, etype := range errorTypes {
tmp := reflect.New(reflect.PointerTo(reflect.ValueOf(etype).Elem().Type())).Interface()
if errors.As(err, tmp) {
return true
}
}
return false
}
func Retry[T any](attempts int, initialBackoff time.Duration, errorTypes []error, f func() (T, error)) (result T, err error) { func Retry[T any](attempts int, initialBackoff time.Duration, errorTypes []error, f func() (T, error)) (result T, err error) {
for i := 0; i < attempts; i++ { for i := 0; i < attempts; i++ {
if i > 0 { if i > 0 {
@ -28,7 +18,7 @@ func Retry[T any](attempts int, initialBackoff time.Duration, errorTypes []error
initialBackoff *= 2 initialBackoff *= 2
} }
result, err = f() result, err = f()
if err == nil || !ErrorIsIn(err, errorTypes) { if err == nil || !api.ErrorIsIn(err, errorTypes) {
return result, err return result, err
} }
} }

View File

@ -1,25 +0,0 @@
package retry
import (
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc"
)
func TestRetryErrorIsInTrue(t *testing.T) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}}
require.True(t, ErrorIsIn(&jsonrpc.RPCConnectionError{}, errorsToRetry))
}
func TestRetryErrorIsInFalse(t *testing.T) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}}
require.False(t, ErrorIsIn(xerrors.Errorf("random error"), errorsToRetry))
}
func TestRetryWrappedErrorIsInTrue(t *testing.T) {
errorsToRetry := []error{&jsonrpc.RPCConnectionError{}}
require.True(t, ErrorIsIn(xerrors.Errorf("wrapped: %w", &jsonrpc.RPCConnectionError{}), errorsToRetry))
}

View File

@ -290,6 +290,10 @@ func gasEstimateGasLimit(
if err != nil { if err != nil {
return -1, xerrors.Errorf("CallWithGas failed: %w", err) return -1, xerrors.Errorf("CallWithGas failed: %w", err)
} }
if res.MsgRct.ExitCode == exitcode.SysErrOutOfGas {
return -1, &api.ErrOutOfGas{}
}
if res.MsgRct.ExitCode != exitcode.Ok { if res.MsgRct.ExitCode != exitcode.Ok {
return -1, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error) return -1, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error)
} }
@ -356,7 +360,7 @@ func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Messag
if msg.GasLimit == 0 { if msg.GasLimit == 0 {
gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.EmptyTSK) gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.EmptyTSK)
if err != nil { if err != nil {
return nil, xerrors.Errorf("estimating gas used: %w", err) return nil, err
} }
msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation) msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation)
} }

View File

@ -482,7 +482,12 @@ func (m *StateModule) StateLookupID(ctx context.Context, addr address.Address, t
return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err) return address.Undef, xerrors.Errorf("loading tipset %s: %w", tsk, err)
} }
return m.StateManager.LookupID(ctx, addr, ts) ret, err := m.StateManager.LookupID(ctx, addr, ts)
if err != nil && xerrors.Is(err, types.ErrActorNotFound) {
return address.Undef, &api.ErrActorNotFound{}
}
return ret, err
} }
func (a *StateAPI) StateLookupRobustAddress(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) { func (a *StateAPI) StateLookupRobustAddress(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error) {

View File

@ -69,7 +69,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
m := mux.NewRouter() m := mux.NewRouter()
serveRpc := func(path string, hnd interface{}) { serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(opts...) rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(api.RPCErrors))...)
rpcServer.Register("Filecoin", hnd) rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
@ -130,7 +130,7 @@ func MinerHandler(a api.StorageMiner, permissioned bool) (http.Handler, error) {
} }
readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder() readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder()
rpcServer := jsonrpc.NewServer(readerServerOpt) rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors), readerServerOpt)
rpcServer.Register("Filecoin", mapi) rpcServer.Register("Filecoin", mapi)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")