Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(exec): dump intermediate cache blocks from FVM exec in StateReplay #12822

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
105 changes: 104 additions & 1 deletion api/api_full.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
Expand Down Expand Up @@ -385,7 +386,7 @@ type FullNode interface {
// StateCall applies the message to the tipset's parent state. The
// message is not applied on-top-of the messages in the passed-in
// tipset.
StateCall(context.Context, *types.Message, types.TipSetKey) (*InvocResult, error) //perm:read
StateCall(ctx context.Context, p jsonrpc.RawParams) (*InvocResult, error) //perm:read
// StateReplay replays a given message, assuming it was included in a block in the specified tipset.
//
// If a tipset key is provided, and a replacing message is not found on chain,
Expand Down Expand Up @@ -1270,6 +1271,12 @@ type InvocResult struct {
ExecutionTrace types.ExecutionTrace
Error string
Duration time.Duration
Blocks []Block `json:",omitempty"`
}

type Block struct {
Cid cid.Cid
Data []byte
}

type IpldObject struct {
Expand Down Expand Up @@ -1469,3 +1476,99 @@ type EthTxReceipt struct {
Logs []ethtypes.EthLog `json:"logs"`
Type ethtypes.EthUint64 `json:"type"`
}

// StateCallParams is the parameters for the StateCall method.
type StateCallParams struct {
Message *types.Message `json:"message"`
TipSetKey types.TipSetKey `json:"tipsetKey"`
IncludeBlocks bool `json:"includeBlocks,omitempty"`
}

// NewStateCallParams creates a new StateCallParams instance with the given message and tipset key.
func NewStateCallParams(msg *types.Message, tsk types.TipSetKey) StateCallParams {
return StateCallParams{
Message: msg,
TipSetKey: tsk,
IncludeBlocks: false,
}
}

// StateCallParamsFromRaw converts raw jsonrpc parameters to StateCallParams.
func StateCallParamsFromRaw(raw jsonrpc.RawParams) (StateCallParams, error) {
return jsonrpc.DecodeParams[StateCallParams](raw)
}

// ToRaw converts the StateCallParams to a jsonrpc.RawParams format for use with the StateCall
// method.
func (e StateCallParams) ToRaw() jsonrpc.RawParams {
if !e.IncludeBlocks {
// if we have 2 arguments, encode using the array format for backward compatibility with the
// old API, if a new client uses this to call an old server, it will be interpreted correctly
// as just 2 arguments
if b, err := json.Marshal([]any{e.Message, e.TipSetKey}); err == nil {
return jsonrpc.RawParams(b)
} // else fall through to the object format, maybe it'll work
}
// IncludeBlocks forces us to use the object format, so we can't be compatible with the old API
b, err := json.Marshal(e)
if err != nil {
return nil
}
return jsonrpc.RawParams(b)
}

// WithIncludeBlocks returns a new StateCallParams with IncludeBlocks set to true.
func (e StateCallParams) WithIncludeBlocks() StateCallParams {
return StateCallParams{
Message: e.Message,
TipSetKey: e.TipSetKey,
IncludeBlocks: true,
}
}

func (e *StateCallParams) UnmarshalJSON(b []byte) error {
if len(b) == 0 {
return xerrors.New("empty input")
}

// If input is an object, decode it directly into a temporary struct
if b[0] == '{' {
type stateCallParamsAlias StateCallParams
var alias stateCallParamsAlias
if err := json.Unmarshal(b, &alias); err != nil {
return err
}
*e = StateCallParams(alias)
// If input is an array, expect exactly two elements: Message and TipSetKey
} else if b[0] == '[' {
var params []json.RawMessage
if err := json.Unmarshal(b, &params); err != nil {
return err
}
if len(params) != 2 {
return xerrors.Errorf("expected two parameters, got %d", len(params))
}
if err := json.Unmarshal(params[0], &e.Message); err != nil {
return xerrors.Errorf("failed to unmarshal message: %w", err)
}
if err := json.Unmarshal(params[1], &e.TipSetKey); err != nil {
return xerrors.Errorf("failed to unmarshal tipset key: %w", err)
}
} else {
return xerrors.Errorf("unexpected JSON input: expected object or array, got %q", string(b[0]))
}

if e.Message == nil {
return xerrors.New("message required")
}

return nil
}

func (e *StateCallParams) MarshalJSON() ([]byte, error) {
return json.Marshal(map[string]interface{}{
"message": e.Message,
"tipsetKey": e.TipSetKey,
"includeBlocks": e.IncludeBlocks,
})
}
2 changes: 1 addition & 1 deletion api/api_gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type Gateway interface {
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
MsigGetVestingSchedule(ctx context.Context, addr address.Address, tsk types.TipSetKey) (MsigVesting, error)
StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
StateCall(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*InvocResult, error)
StateCall(ctx context.Context, p jsonrpc.RawParams) (*InvocResult, error)
StateDealProviderCollateralBounds(ctx context.Context, size abi.PaddedPieceSize, verified bool, tsk types.TipSetKey) (DealCollateralBounds, error)
StateDecodeParams(ctx context.Context, toAddr address.Address, method abi.MethodNum, params []byte, tsk types.TipSetKey) (interface{}, error)
StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error)
Expand Down
6 changes: 6 additions & 0 deletions api/docgen/docgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ func init() {
addExample(map[string]types.Actor{
"t01236": ExampleValue("init", reflect.TypeOf(types.Actor{}), nil).(types.Actor),
})
addExample(types.IpldOpGet)
addExample(&types.TraceIpld{
Op: types.IpldOpGet,
Cid: c,
Size: 123,
})
addExample(&types.ExecutionTrace{
Msg: ExampleValue("init", reflect.TypeOf(types.MessageTrace{}), nil).(types.MessageTrace),
MsgRct: ExampleValue("init", reflect.TypeOf(types.ReturnTrace{}), nil).(types.ReturnTrace),
Expand Down
8 changes: 4 additions & 4 deletions api/mocks/mock_full.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions api/proxy_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion api/v0api/full.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/filecoin-project/go-address"
"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/builtin/v8/paych"
verifregtypes "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
Expand Down Expand Up @@ -308,7 +309,7 @@ type FullNode interface {
// StateCall applies the message to the tipset's parent state. The
// message is not applied on-top-of the messages in the passed-in
// tipset.
StateCall(context.Context, *types.Message, types.TipSetKey) (*api.InvocResult, error) //perm:read
StateCall(context.Context, jsonrpc.RawParams) (*api.InvocResult, error) //perm:read
// StateReplay replays a given message, assuming it was included in a block in the specified tipset.
//
// If a tipset key is provided, and a replacing message is not found on chain,
Expand Down
3 changes: 2 additions & 1 deletion api/v0api/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/ipfs/go-cid"

"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi"
verifregtypes "github.com/filecoin-project/go-state-types/builtin/v9/verifreg"
"github.com/filecoin-project/go-state-types/dline"
Expand Down Expand Up @@ -58,7 +59,7 @@ type Gateway interface {
MsigGetVested(ctx context.Context, addr address.Address, start types.TipSetKey, end types.TipSetKey) (types.BigInt, error)
MsigGetPending(context.Context, address.Address, types.TipSetKey) ([]*api.MsigTransaction, error)
StateAccountKey(ctx context.Context, addr address.Address, tsk types.TipSetKey) (address.Address, error)
StateCall(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (*api.InvocResult, error)
StateCall(ctx context.Context, p jsonrpc.RawParams) (*api.InvocResult, error)
StateDealProviderCollateralBounds(ctx context.Context, size abi.PaddedPieceSize, verified bool, tsk types.TipSetKey) (api.DealCollateralBounds, error)
StateDecodeParams(ctx context.Context, toAddr address.Address, method abi.MethodNum, params []byte, tsk types.TipSetKey) (interface{}, error)
StateGetActor(ctx context.Context, actor address.Address, ts types.TipSetKey) (*types.Actor, error)
Expand Down
17 changes: 9 additions & 8 deletions api/v0api/proxy_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions api/v0api/v0mocks/mock_full.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading