forest icon indicating copy to clipboard operation
forest copied to clipboard

Fix Ipld serialization in `State*Msg*` RPC API

Open hanabi1224 opened this issue 2 years ago • 2 comments

Issue summary

Lotus uses runtime reflection for IPLD serialization in RPC API json responses, it's ignored in forest for now. More investigation is needed to implement it in forest.

Related lotus code

type MsgLookup struct {
	Message   cid.Cid // Can be different than requested, in case it was replaced, but only gas values changed
	Receipt   types.MessageReceipt
	ReturnDec interface{}
	TipSet    types.TipSetKey
	Height    abi.ChainEpoch
}

var returndec interface{}
	if recpt.ExitCode == 0 && len(recpt.Return) > 0 {
		cmsg, err := m.Chain.GetCMessage(ctx, msg)
		if err != nil {
			return nil, xerrors.Errorf("failed to load message after successful receipt search: %w", err)
		}

		vmsg := cmsg.VMMessage()

		switch t, err := stmgr.GetReturnType(ctx, m.StateManager, vmsg.To, vmsg.Method, ts); {
		case errors.Is(err, stmgr.ErrMetadataNotFound):
			// This is not necessarily an error -- EVM methods (and in the future native actors) may
			// return just bytes, and in the not so distant future we'll have native wasm actors
			// that are by definition not in the registry.
			// So in this case, log a debug message and retun the raw bytes.
			log.Debugf("failed to get return type: %s", err)
			returndec = recpt.Return
		case err != nil:
			return nil, xerrors.Errorf("failed to get return type: %w", err)
		default:
			if err := t.UnmarshalCBOR(bytes.NewReader(recpt.Return)); err != nil {
				return nil, err
			}
			returndec = t
		}
	}

	return &api.MsgLookup{
		Message:   found,
		Receipt:   *recpt,
		ReturnDec: returndec,
		TipSet:    ts.Key(),
		Height:    ts.Height(),
	}, nil

func GetReturnType(ctx context.Context, sm *StateManager, to address.Address, method abi.MethodNum, ts *types.TipSet) (cbg.CBORUnmarshaler, error) {
	act, err := sm.LoadActor(ctx, to, ts)
	if err != nil {
		return nil, xerrors.Errorf("(get sset) failed to load actor: %w", err)
	}

	m, found := sm.tsExec.NewActorRegistry().Methods[act.Code][method]
	if !found {
		return nil, fmt.Errorf("unknown method %d for actor %s: %w", method, act.Code, ErrMetadataNotFound)
	}

	return reflect.New(m.Ret.Elem()).Interface().(cbg.CBORUnmarshaler), nil
}

example lotus API call:

request:

{
    "id": 0,
    "jsonrpc": "2.0",
    "method": "Filecoin.StateWaitMsg",
    "params": [
        {
            "/": "bafy2bzacear2xvf6lnxvtyooupaosxgh2bosn7bp35bzz6syofzk7b45gp2es"
        },
        0
    ]
}

response:

{
    "jsonrpc": "2.0",
    "result": {
        "Message": {
            "/": "bafy2bzacear2xvf6lnxvtyooupaosxgh2bosn7bp35bzz6syofzk7b45gp2es"
        },
        "Receipt": {
            "ExitCode": 0,
            "Return": "gxnmiFUCU1RCVs/3Evl82pH2EU27iJjACWtUdZ6JBkmIZWDdZSQ9ieRv0l4paI4=",
            "GasUsed": 82943114,
            "EventsRoot": null
        },
        "ReturnDec": {
            "ActorID": 59016,
            "RobustAddress": "t2knkeevwp64jps7g2sh3bctn3rcmmaclltnpwtdi",
            "EthAddress": [
                117,
                158,
                137,
                6,
                73,
                136,
                101,
                96,
                221,
                101,
                36,
                61,
                137,
                228,
                111,
                210,
                94,
                41,
                104,
                142
            ]
        },
        "TipSet": [
            {
                "/": "bafy2bzaceanaa2pyrqcn43ybv4ig5urvtumte4ftlkoaqudojyjptvtiadq7w"
            }
        ],
        "Height": 1144812
    },
    "id": 0
}

Other information and links

https://github.com/ChainSafe/forest/pull/3783 https://github.com/ChainSafe/forest/issues/3769

hanabi1224 avatar Dec 04 '23 09:12 hanabi1224

I'd love to see a more detailed explanation of the behaviour here :)

aatifsyed avatar Dec 05 '23 01:12 aatifsyed

@aatifsyed I've added related lotus code to the description. Basically lotus uses any type (interface{}) for this field and use runtime reflection to serialize it with different concrete types

hanabi1224 avatar Dec 05 '23 11:12 hanabi1224