Skip to content

Commit

Permalink
feat: support typed errors over RPC
Browse files Browse the repository at this point in the history
  • Loading branch information
arajasek committed Jul 19, 2022
1 parent d3e5654 commit 24ed596
Show file tree
Hide file tree
Showing 16 changed files with 92 additions and 34 deletions.
19 changes: 19 additions & 0 deletions api/api_errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package api

import "github.com/filecoin-project/go-jsonrpc"

const (
EOutOfGas = iota + jsonrpc.FirstUserCode
)

type ErrOutOfGas struct{}

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

var RPCErrors = jsonrpc.NewErrors()

func init() {
RPCErrors.Register(EOutOfGas, new(ErrOutOfGas))
}
13 changes: 8 additions & 5 deletions api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
func NewCommonRPCV0(ctx context.Context, addr string, requestHeader http.Header) (api.CommonNet, jsonrpc.ClientCloser, error) {
var res v0api.CommonNetStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader)
api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))

return &res, closer, err
}
Expand All @@ -29,7 +29,7 @@ func NewFullNodeRPCV0(ctx context.Context, addr string, requestHeader http.Heade
var res v0api.FullNodeStruct

closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader)
api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))

return &res, closer, err
}
Expand All @@ -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) {
var res v1api.FullNodeStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader)
api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors))

return &res, closer, err
}
Expand Down Expand Up @@ -72,6 +72,7 @@ func NewStorageMinerRPCV0(ctx context.Context, addr string, requestHeader http.H
api.GetInternalStructs(&res), requestHeader,
append([]jsonrpc.Option{
rpcenc.ReaderParamEncoder(pushUrl),
jsonrpc.WithErrors(api.RPCErrors),
}, opts...)...)

return &res, closer, err
Expand All @@ -90,6 +91,7 @@ func NewWorkerRPCV0(ctx context.Context, addr string, requestHeader http.Header)
rpcenc.ReaderParamEncoder(pushUrl),
jsonrpc.WithNoReconnect(),
jsonrpc.WithTimeout(30*time.Second),
jsonrpc.WithErrors(api.RPCErrors),
)

return &res, closer, err
Expand All @@ -101,7 +103,7 @@ func NewGatewayRPCV1(ctx context.Context, addr string, requestHeader http.Header
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res),
requestHeader,
opts...,
append(opts, jsonrpc.WithErrors(api.RPCErrors))...,
)

return &res, closer, err
Expand All @@ -113,7 +115,7 @@ func NewGatewayRPCV0(ctx context.Context, addr string, requestHeader http.Header
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res),
requestHeader,
opts...,
append(opts, jsonrpc.WithErrors(api.RPCErrors))...,
)

return &res, closer, err
Expand All @@ -124,6 +126,7 @@ func NewWalletRPCV0(ctx context.Context, addr string, requestHeader http.Header)
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res),
requestHeader,
jsonrpc.WithErrors(api.RPCErrors),
)

return &res, closer, err
Expand Down
5 changes: 3 additions & 2 deletions build/params_shared_vals.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ const VerifSigCacheSize = 32000
// TODO: If this is gonna stay, it should move to specs-actors
const BlockMessageLimit = 10000

const BlockGasLimit = 10_000_000_000
const BlockGasTarget = BlockGasLimit / 2
var BlockGasLimit = int64(10_000_000_000)
var BlockGasTarget = BlockGasLimit / 2

const BaseFeeMaxChangeDenom = 8 // 12.5%
const InitialBaseFee = 100e6
const MinimumBaseFee = 100
Expand Down
2 changes: 1 addition & 1 deletion chain/messagepool/repub.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func (mp *MessagePool) republishPendingMessages(ctx context.Context) error {
return chains[i].Before(chains[j])
})

gasLimit := int64(build.BlockGasLimit)
gasLimit := build.BlockGasLimit
minGas := int64(gasguess.MinGas)
var msgs []*types.SignedMessage
loop:
Expand Down
4 changes: 2 additions & 2 deletions chain/messagepool/selection.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (mp *MessagePool) selectMessagesOptimal(ctx context.Context, curTs, ts *typ
nextChain := 0
partitions := make([][]*msgChain, MaxBlocks)
for i := 0; i < MaxBlocks && nextChain < len(chains); i++ {
gasLimit := int64(build.BlockGasLimit)
gasLimit := build.BlockGasLimit
msgLimit := build.BlockMessageLimit
for nextChain < len(chains) {
chain := chains[nextChain]
Expand Down Expand Up @@ -590,7 +590,7 @@ func (mp *MessagePool) selectPriorityMessages(ctx context.Context, pending map[a
mpCfg := mp.getConfig()
result := &selectedMessages{
msgs: make([]*types.SignedMessage, 0, mpCfg.SizeLimitLow),
gasLimit: int64(build.BlockGasLimit),
gasLimit: build.BlockGasLimit,
blsLimit: cbg.MaxLength,
secpLimit: cbg.MaxLength,
}
Expand Down
2 changes: 1 addition & 1 deletion chain/store/basefee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestBaseFee(t *testing.T) {
{100e6, build.BlockGasTarget, 1, 103.125e6, 100e6},
{100e6, build.BlockGasTarget * 2, 2, 103.125e6, 100e6},
{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 {
Expand Down
9 changes: 5 additions & 4 deletions cmd/lotus-sim/simulation/blockbuilder/blockbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ const (
// has.
// 5 per tipset, but we effectively get 4 blocks worth of messages.
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 {
ctx context.Context
logger *zap.SugaredLogger
Expand Down
2 changes: 1 addition & 1 deletion cmd/lotus-wallet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ var runCmd = &cli.Command{
rpcApi = api.PermissionedWalletAPI(rpcApi)
}

rpcServer := jsonrpc.NewServer()
rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors))
rpcServer.Register("Filecoin", rpcApi)

mux.Handle("/rpc/v0", rpcServer)
Expand Down
2 changes: 1 addition & 1 deletion cmd/lotus-worker/sealworker/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
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()
readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder()
rpcServer := jsonrpc.NewServer(readerServerOpt)
rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors), readerServerOpt)

wapi := proxy.MetricedWorkerAPI(a)
if permissioned {
Expand Down
5 changes: 3 additions & 2 deletions cmd/lotus/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"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/chain/consensus/filcns"
"github.com/filecoin-project/lotus/chain/stmgr"
Expand Down Expand Up @@ -303,7 +304,7 @@ var DaemonCmd = &cli.Command{
}

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
Expand Down Expand Up @@ -360,7 +361,7 @@ var DaemonCmd = &cli.Command{
// ----

// 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 {
serverOptions = append(serverOptions, jsonrpc.WithMaxRequestSize(int64(maxRequestSize)))
}
Expand Down
2 changes: 1 addition & 1 deletion gateway/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinu
m := mux.NewRouter()

serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(opts...)
rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(lapi.RPCErrors))...)
rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ require (
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-markets v1.23.1
github.com/filecoin-project/go-jsonrpc v0.1.5
github.com/filecoin-project/go-jsonrpc v0.1.6-0.20220718153054-f4f3f0e8d7c4
github.com/filecoin-project/go-legs v0.4.4
github.com/filecoin-project/go-padreader v0.0.1
github.com/filecoin-project/go-paramfetch v0.0.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,8 @@ github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGy
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-indexer-core v0.2.16/go.mod h1:5kCKyhtT9k1vephr9l9SFGX8B/HowXIvOhGCkmbxwbY=
github.com/filecoin-project/go-jsonrpc v0.1.5 h1:ckxqZ09ivBAVf5CSmxxrqqNHC7PJm3GYGtYKiNQ+vGk=
github.com/filecoin-project/go-jsonrpc v0.1.5/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
github.com/filecoin-project/go-jsonrpc v0.1.6-0.20220718153054-f4f3f0e8d7c4 h1:Gc9r5KJtnBsLPOdd1DklhHtYv7g1owEtn8Bbej0kjvw=
github.com/filecoin-project/go-jsonrpc v0.1.6-0.20220718153054-f4f3f0e8d7c4/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/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s=
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak=
Expand Down
45 changes: 37 additions & 8 deletions itests/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
logging "github.com/ipfs/go-log/v2"
"github.com/libp2p/go-libp2p-core/peer"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
Expand Down Expand Up @@ -44,14 +45,15 @@ type apiSuite struct {
func runAPITest(t *testing.T, opts ...interface{}) {
ts := apiSuite{opts: opts}

t.Run("version", ts.testVersion)
t.Run("id", ts.testID)
t.Run("testConnectTwo", ts.testConnectTwo)
t.Run("testMining", ts.testMining)
t.Run("testMiningReal", ts.testMiningReal)
t.Run("testSlowNotify", ts.testSlowNotify)
t.Run("testSearchMsg", ts.testSearchMsg)
t.Run("testNonGenesisMiner", ts.testNonGenesisMiner)
//t.Run("version", ts.testVersion)
//t.Run("id", ts.testID)
//t.Run("testConnectTwo", ts.testConnectTwo)
//t.Run("testMining", ts.testMining)
//t.Run("testMiningReal", ts.testMiningReal)
//t.Run("testSlowNotify", ts.testSlowNotify)
//t.Run("testSearchMsg", ts.testSearchMsg)
t.Run("testOutOfGasError", ts.testOutOfGasError)
//t.Run("testNonGenesisMiner", ts.testNonGenesisMiner)
}

func (ts *apiSuite) testVersion(t *testing.T) {
Expand Down Expand Up @@ -149,6 +151,33 @@ 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)
}

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) testMining(t *testing.T) {
ctx := context.Background()

Expand Down
6 changes: 5 additions & 1 deletion node/impl/full/gas.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,10 @@ func gasEstimateGasLimit(
if err != nil {
return -1, xerrors.Errorf("CallWithGas failed: %w", err)
}
if res.MsgRct.ExitCode == exitcode.SysErrOutOfGas {
return -1, api.ErrOutOfGas{}
}

if res.MsgRct.ExitCode != exitcode.Ok {
return -1, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error)
}
Expand Down Expand Up @@ -356,7 +360,7 @@ func (m *GasModule) GasEstimateMessageGas(ctx context.Context, msg *types.Messag
if msg.GasLimit == 0 {
gasLimit, err := m.GasEstimateGasLimit(ctx, msg, types.EmptyTSK)
if err != nil {
return nil, xerrors.Errorf("estimating gas used: %w", err)
return nil, err
}
msg.GasLimit = int64(float64(gasLimit) * m.Mpool.GetConfig().GasLimitOverestimation)
}
Expand Down
4 changes: 2 additions & 2 deletions node/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
m := mux.NewRouter()

serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(opts...)
rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(api.RPCErrors))...)
rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")

Expand Down Expand Up @@ -130,7 +130,7 @@ func MinerHandler(a api.StorageMiner, permissioned bool) (http.Handler, error) {
}

readerHandler, readerServerOpt := rpcenc.ReaderParamDecoder()
rpcServer := jsonrpc.NewServer(readerServerOpt)
rpcServer := jsonrpc.NewServer(jsonrpc.WithServerErrors(api.RPCErrors), readerServerOpt)
rpcServer.Register("Filecoin", mapi)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")

Expand Down

0 comments on commit 24ed596

Please sign in to comment.