forest
forest copied to clipboard
Fix Ipld serialization in `State*Msg*` RPC API
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
I'd love to see a more detailed explanation of the behaviour here :)
@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