From cc7c311a458521d507ac9b84fe65ba2e7b3af567 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 12 Mar 2024 15:48:51 +1100 Subject: [PATCH] feat(events): introduce compact JSON form of EventEntry Optional compact form, can round-trip as either full standard Go style JSON or compact tuple struct with decoded "value" field represented as dag-json. Currently turned on as strict default for GetActorEvents and SubscribeActorEvents --- build/openrpc/full.json.gz | Bin 35937 -> 35941 bytes build/openrpc/gateway.json.gz | Bin 12141 -> 12145 bytes chain/types/actor_event.go | 299 ++++++++++++++++++++ chain/types/actor_event_test.go | 45 ++- chain/types/event.go | 3 + chain/types/tipset_key.go | 64 +++++ chain/types/tipset_key_test.go | 65 +++++ documentation/en/api-v1-unstable-methods.md | 28 +- itests/direct_data_onboard_verified_test.go | 5 +- node/impl/full/actor_events.go | 10 +- node/impl/full/actor_events_test.go | 5 +- 11 files changed, 495 insertions(+), 29 deletions(-) diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index e011458afee18b4bbbecdbf9a8bdd3432afaa6eb..cd2278ebb13e81bd3cc70c47751e499c2b73bbf5 100644 GIT binary patch delta 21799 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 delta 21771 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 diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index c06286e38b9f6d97c672a8cf9a3fb5a7282678e6..bf6fd3e793ea342858b8c73ff77b2b4783dd8912 100644 GIT binary patch delta 4613 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 delta 4610 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 0 { + first, _ := entries.LookupByIndex(0) + if first.Kind() == datamodel.Kind_List { + ae.encodeCompact = new(bool) + *ae.encodeCompact = true + } + } + + return nil +} + +func (ae ActorEvent) MarshalJSON() ([]byte, error) { + var entryOpt bindnode.Option = eventEntryBindnodeOption + if ae.encodeCompact != nil { + if *ae.encodeCompact { + entryOpt = eventEntryCompactBindnodeOption + } + ae.encodeCompact = nil // hide it from this encode + } + nd := bindnode.Wrap( + &ae, + actorEventProto.Type(), + TipSetKeyAsLinksListBindnodeOption, + addressAsStringBindnodeOption, + entryOpt, + ) + return ipld.Encode(nd, dagjson.Encode) +} + +// TODO: move this in to go-state-types/ipld with the address "as bytes" form +var addressAsStringBindnodeOption = bindnode.TypedStringConverter(&address.Address{}, addressFromString, addressToString) + +func addressFromString(s string) (interface{}, error) { + a, err := address.NewFromString(s) + if err != nil { + return nil, err + } + return &a, nil +} + +func addressToString(iface interface{}) (string, error) { + addr, ok := iface.(*address.Address) + if !ok { + return "", errors.New("expected *Address value") + } + return addr.String(), nil +} + +var eventEntryBindnodeOption = bindnode.TypedAnyConverter(&EventEntry{}, eventEntryFromAny, eventEntryToAny) +var eventEntryCompactBindnodeOption = bindnode.TypedAnyConverter(&EventEntry{}, eventEntryCompactFromAny, eventEntryCompactToAny) + +// eventEntryFromAny will instantiate an EventEntry assuming standard Go JSON form, i.e.: +// {"Codec":82,"Flags":0,"Key":"key2","Value":"dmFsdWUy"} +// Where the value is intact as raw bytes but represented as a base64 string, and the object is +// represented as a map. +func eventEntryFromAny(n datamodel.Node) (interface{}, error) { + if n.Kind() == datamodel.Kind_List { + return eventEntryCompactFromAny(n) + } + if n.Kind() != datamodel.Kind_Map { + return nil, errors.New("expected map representation for EventEntry") + } + if n.Length() != 4 { + return nil, errors.New("expected 4 fields for EventEntry") + } + fn, err := n.LookupByString("Flags") + if err != nil { + return nil, fmt.Errorf("missing Flags field for EventEntry: %w", err) + } + flags, err := fn.AsInt() + if err != nil { + return nil, fmt.Errorf("expected int in Flags field for EventEntry: %w", err) + } + cn, err := n.LookupByString("Codec") + if err != nil { + return nil, fmt.Errorf("missing Codec field for EventEntry: %w", err) + } + codec, err := cn.AsInt() + if err != nil { + return nil, fmt.Errorf("expected int in Codec field for EventEntry: %w", err) + } + // it has to fit into a uint8 + if flags < 0 || flags > 255 { + return nil, fmt.Errorf("expected uint8 in Flags field for EventEntry, got %d", flags) + } + kn, err := n.LookupByString("Key") + if err != nil { + return nil, fmt.Errorf("missing Key field for EventEntry: %w", err) + } + key, err := kn.AsString() + if err != nil { + return nil, fmt.Errorf("expected string in Key field for EventEntry: %w", err) + } + vn, err := n.LookupByString("Value") + if err != nil { + return nil, fmt.Errorf("missing Value field for EventEntry: %w", err) + } + value64, err := vn.AsString() // base64 + if err != nil { + return nil, fmt.Errorf("expected string in Value field for EventEntry: %w", err) + } + value, err := base64.StdEncoding.DecodeString(value64) + if err != nil { + return nil, fmt.Errorf("failed to decode base64 value: %w", err) + } + return &EventEntry{ + Flags: uint8(flags), + Key: key, + Codec: uint64(codec), + Value: value, + }, nil +} + +// eventEntryCompactFromAny will instantiate an EventEntry assuming compact form, i.e.: +// [0,82,"key2",{"/":{"bytes":"dmFsdWUy"}}] +// Where the value is represented in its decoded IPLD data model form, and the object is represented +// as a tuple. +func eventEntryCompactFromAny(n datamodel.Node) (interface{}, error) { + if n.Kind() != datamodel.Kind_List { + return nil, errors.New("expected list representation for compact EventEntry") + } + if n.Length() != 4 { + return nil, errors.New("expected 4 fields for EventEntry") + } + // Flags before Codec in this form, sorted Codec before Flags in the non-compact form when dag-json + fn, err := n.LookupByIndex(0) + if err != nil { + return nil, fmt.Errorf("missing Flags field for EventEntry: %w", err) + } + flags, err := fn.AsInt() + if err != nil { + return nil, fmt.Errorf("expected int in Flags field for EventEntry: %w", err) + } + // it has to fit into a uint8 + if flags < 0 || flags > 255 { + return nil, fmt.Errorf("expected uint8 in Flags field for EventEntry, got %d", flags) + } + cn, err := n.LookupByIndex(1) + if err != nil { + return nil, fmt.Errorf("missing Codec field for EventEntry: %w", err) + } + codecCode, err := cn.AsInt() + if err != nil { + return nil, fmt.Errorf("expected int in Codec field for EventEntry: %w", err) + } + kn, err := n.LookupByIndex(2) + if err != nil { + return nil, fmt.Errorf("missing Key field for EventEntry: %w", err) + } + key, err := kn.AsString() + if err != nil { + return nil, fmt.Errorf("expected string in Key field for EventEntry: %w", err) + } + vn, err := n.LookupByIndex(3) + if err != nil { + return nil, fmt.Errorf("missing Value field for EventEntry: %w", err) + } + // as of writing only 0x55 and 0x51 are supported here, but we'll treat raw as the default, + // regardless, which means that for any unexpected codecs encountered we'll assume that the + // encoder also didn't know what to do with it and just treat it as raw bytes. + var value []byte + switch codecCode { + case 0x51: // plain cbor + if value, err = ipld.Encode(vn, dagcbor.Encode); err != nil { + return nil, fmt.Errorf("failed to encode cbor value: %w", err) + } + default: // raw (0x55) and all unknowns + if vn.Kind() != datamodel.Kind_Bytes { + return nil, fmt.Errorf("expected bytes in Value field for EventEntry, got %s", vn.Kind()) + } + if value, err = vn.AsBytes(); err != nil { + return nil, err + } + } + + return &EventEntry{ + Flags: uint8(flags), + Key: key, + Codec: uint64(codecCode), + Value: value, + }, nil +} + +// eventEntryToAny does the reverse of eventEntryFromAny, converting an EventEntry back to the +// standard Go JSON form, i.e.: +// {"Codec":82,"Flags":0,"Key":"key2","Value":"dmFsdWUy"} +func eventEntryToAny(iface interface{}) (datamodel.Node, error) { + ee, ok := iface.(*EventEntry) + if !ok { + return nil, errors.New("expected *Address value") + } + return qp.BuildMap(basicnode.Prototype.Map, 4, func(ma datamodel.MapAssembler) { + qp.MapEntry(ma, "Flags", qp.Int(int64(ee.Flags))) + qp.MapEntry(ma, "Codec", qp.Int(int64(ee.Codec))) + qp.MapEntry(ma, "Key", qp.String(ee.Key)) + qp.MapEntry(ma, "Value", qp.String(base64.StdEncoding.EncodeToString(ee.Value))) + }) +} + +// eventEntryCompactToAny does the reverse of eventEntryCompactFromAny, converting an EventEntry +// back to the compact form, i.e.: +// [0,82,"key2",{"/":{"bytes":"dmFsdWUy"}}] +func eventEntryCompactToAny(iface interface{}) (datamodel.Node, error) { + ee, ok := iface.(*EventEntry) + if !ok { + return nil, errors.New("expected *Address value") + } + var decoder codec.Decoder = raw.Decode + if ee.Codec == 0x51 { + decoder = dagcbor.Decode + } + valueNode, err := ipld.Decode(ee.Value, decoder) + if err != nil { + log.Warn("failed to decode event entry value with expected codec", "err", err) + valueNode = basicnode.NewBytes(ee.Value) + } + return qp.BuildList(basicnode.Prototype.List, 4, func(la datamodel.ListAssembler) { + qp.ListEntry(la, qp.Int(int64(ee.Flags))) + qp.ListEntry(la, qp.Int(int64(ee.Codec))) + qp.ListEntry(la, qp.String(ee.Key)) + qp.ListEntry(la, qp.Node(valueNode)) + }) +} + +var ( + actorEventProto schema.TypedPrototype + fullFormIpldSchema = ` +type ActorEvent struct { + encodeCompact optional Bool + Entries [Any] (rename "entries") # EventEntry + Emitter String (rename "emitter") # addr.Address + Reverted Bool (rename "reverted") + Height Int (rename "height") + TipSetKey Any (rename "tipsetKey") # types.TipSetKey + MsgCid &Any (rename "msgCid") +} +` +) + +func init() { + typeSystem, err := ipld.LoadSchemaBytes([]byte(fullFormIpldSchema)) + if err != nil { + panic(err) + } + schemaType := typeSystem.TypeByName("ActorEvent") + if schemaType == nil { + panic(fmt.Errorf("schema for [%T] does not contain that named type [%s]", (*ActorEvent)(nil), "ActorEvent")) + } + actorEventProto = bindnode.Prototype( + (*ActorEvent)(nil), + schemaType, + TipSetKeyAsLinksListBindnodeOption, + addressAsStringBindnodeOption, + eventEntryBindnodeOption, + ) +} diff --git a/chain/types/actor_event_test.go b/chain/types/actor_event_test.go index 8c50b171754..30f1e150b0c 100644 --- a/chain/types/actor_event_test.go +++ b/chain/types/actor_event_test.go @@ -16,6 +16,10 @@ import ( func TestJSONMarshalling(t *testing.T) { rng := pseudo.New(pseudo.NewSource(0)) + emitter := randomF4Addr(t, rng) + tskCid := randomCid(t, rng) + msgCid := randomCid(t, rng) + t.Run("actor event with entries", testJsonMarshalling( ActorEvent{ @@ -23,7 +27,7 @@ func TestJSONMarshalling(t *testing.T) { { Key: "key1", Codec: 0x51, - Value: []byte("value1"), + Value: []byte("fvalue1"), }, { Key: "key2", @@ -31,13 +35,40 @@ func TestJSONMarshalling(t *testing.T) { Value: []byte("value2"), }, }, - Emitter: randomF4Addr(t, rng), + Emitter: emitter, Reverted: false, Height: 1001, - TipSetKey: NewTipSetKey(randomCid(t, rng)), - MsgCid: randomCid(t, rng), + TipSetKey: NewTipSetKey(tskCid), + MsgCid: msgCid, }, - `{"entries":[{"Flags":0,"Key":"key1","Codec":81,"Value":"dmFsdWUx"},{"Flags":0,"Key":"key2","Codec":82,"Value":"dmFsdWUy"}],"emitter":"f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","reverted":false,"height":1001,"tipsetKey":[{"/":"bafkqacx3dag26sfht3qlcdi"}],"msgCid":{"/":"bafkqacrziziykd6uuf4islq"}}`, + `{"entries":[{"Flags":0,"Key":"key1","Codec":81,"Value":"ZnZhbHVlMQ=="},{"Flags":0,"Key":"key2","Codec":82,"Value":"dmFsdWUy"}],"emitter":"f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","reverted":false,"height":1001,"tipsetKey":[{"/":"bafkqacx3dag26sfht3qlcdi"}],"msgCid":{"/":"bafkqacrziziykd6uuf4islq"}}`, + ), + ) + + t.Run("actor event with entries as compact form ", + testJsonMarshalling( + ActorEvent{ + Entries: []EventEntry{ + { + // this should get decoded as a string: "value1" in our compact form and + // round-tripped back to this encoded form + Key: "key1", + Codec: 0x51, + Value: []byte("fvalue1"), + }, + { + Key: "key2", + Codec: 0x52, + Value: []byte("value2"), + }, + }, + Emitter: emitter, + Reverted: false, + Height: 1001, + TipSetKey: NewTipSetKey(tskCid), + MsgCid: msgCid, + }.AsCompactEncoded(), + `{"entries":[[0,81,"key1","value1"],[0,82,"key2",{"/":{"bytes":"dmFsdWUy"}}]],"emitter":"f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","reverted":false,"height":1001,"tipsetKey":[{"/":"bafkqacx3dag26sfht3qlcdi"}],"msgCid":{"/":"bafkqacrziziykd6uuf4islq"}}`, ), ) @@ -52,7 +83,7 @@ func TestJSONMarshalling(t *testing.T) { "key1": { { Codec: 0x51, - Value: []byte("value1"), + Value: []byte("fvalue1"), }, }, "key2": { @@ -66,7 +97,7 @@ func TestJSONMarshalling(t *testing.T) { ToHeight: heightOf(100), TipSetKey: randomTipSetKey(t, rng), }, - `{"addresses":["f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua"],"fields":{"key1":[{"codec":81,"value":"dmFsdWUx"}],"key2":[{"codec":82,"value":"dmFsdWUy"}]},"fromHeight":0,"toHeight":100,"tipsetKey":[{"/":"bafkqacxcqxwocuiukv4aq5i"}]}`, + `{"addresses":["f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua","f410fagkp3qx2f76maqot74jaiw3tzbxe76k76zrkl3xifk67isrnbn2sll3yua"],"fields":{"key1":[{"codec":81,"value":"ZnZhbHVlMQ=="}],"key2":[{"codec":82,"value":"dmFsdWUy"}]},"fromHeight":0,"toHeight":100,"tipsetKey":[{"/":"bafkqacxcqxwocuiukv4aq5i"}]}`, ), ) t.Run("actor event block", diff --git a/chain/types/event.go b/chain/types/event.go index 5f6415d49e1..7f192459f72 100644 --- a/chain/types/event.go +++ b/chain/types/event.go @@ -32,4 +32,7 @@ type EventEntry struct { Value []byte } +// TODO: implement EventEntry#UnmarshalJSON and EventEntry#MarshalJSON to allow for both compact and +// non-compact forms as per ActorEvent#UnmarshalJSON and ActorEvent#MarshalJSON + type FilterID [32]byte // compatible with EthHash diff --git a/chain/types/tipset_key.go b/chain/types/tipset_key.go index 15e655da7d6..0e0872bbd7e 100644 --- a/chain/types/tipset_key.go +++ b/chain/types/tipset_key.go @@ -3,12 +3,17 @@ package types import ( "bytes" "encoding/json" + "errors" "fmt" "io" "strings" block "github.com/ipfs/go-block-format" "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime/datamodel" + cidlink "github.com/ipld/go-ipld-prime/linking/cid" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/node/bindnode" typegen "github.com/whyrusleeping/cbor-gen" "github.com/filecoin-project/go-state-types/abi" @@ -192,3 +197,62 @@ func decodeKey(encoded []byte) ([]cid.Cid, error) { var _ typegen.CBORMarshaler = &TipSetKey{} var _ typegen.CBORUnmarshaler = &TipSetKey{} + +// TipSetKeyAsLinksListBindnodeOption Used in conjunction +// github.com/ipld/go-ipld-prime/node/bindnode when you want to refer to a TipSetKey and have it +// encoded as a list of links, as in the JSON representation above rather than as a byte string as +// in the CBOR representation above. +// +// This option lets you represent a TipSetKey field as an Any in a schema, and it will map directly +// on to a TipSetKey in the corresponding concrete Go types. +var TipSetKeyAsLinksListBindnodeOption = bindnode.TypedAnyConverter(&TipSetKey{}, tipsetKeyAsLinksListFromAny, tipsetKeyAsLinksListToAny) + +func tipsetKeyAsLinksListFromAny(n datamodel.Node) (interface{}, error) { + if n.Kind() != datamodel.Kind_List { + return nil, errors.New("expected list representation") + } + cids := make([]cid.Cid, 0) + itr := n.ListIterator() + for !itr.Done() { + _, v, err := itr.Next() + if err != nil { + return nil, err + } + if v.Kind() != datamodel.Kind_Link { + return nil, errors.New("expected link") + } + l, err := v.AsLink() + if err != nil { + return nil, err + } + if cl, ok := l.(cidlink.Link); ok { + cids = append(cids, cl.Cid) + } else { + return nil, errors.New("expected CID link") + } + } + return &TipSetKey{value: string(encodeKey(cids))}, nil + +} + +func tipsetKeyAsLinksListToAny(iface interface{}) (datamodel.Node, error) { + tsk, ok := iface.(*TipSetKey) + if !ok { + return nil, fmt.Errorf("expected *Address value") + } + cids := tsk.Cids() + nb := basicnode.Prototype.List.NewBuilder() + la, err := nb.BeginList(int64(len(cids))) + if err != nil { + return nil, err + } + for _, c := range cids { + if err := la.AssembleValue().AssignLink(cidlink.Link{Cid: c}); err != nil { + return nil, err + } + } + if err := la.Finish(); err != nil { + return nil, err + } + return nb.Build(), nil +} diff --git a/chain/types/tipset_key_test.go b/chain/types/tipset_key_test.go index 5fbecb3ea7d..554b90ade17 100644 --- a/chain/types/tipset_key_test.go +++ b/chain/types/tipset_key_test.go @@ -8,6 +8,9 @@ import ( "testing" "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec/dagjson" + "github.com/ipld/go-ipld-prime/node/bindnode" "github.com/multiformats/go-multihash" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -93,3 +96,65 @@ func verifyJSON(t *testing.T, expected string, k TipSetKey) { require.NoError(t, err) assert.Equal(t, k, rehydrated) } + +// Test that our go-ipld-prime bindnode option works with TipSetKey as an Any in the schema but +// properly typed in the Go type. In this form we expect it to match the JSON style encoding, but +// we could use an alternate encoder (dag-cbor) and get the same form: a list of links. This is +// distinct from the natural CBOR form of TipSetKey which is just a byte string, which we don't +// (yet) have a bindnode option for (but could). + +var ipldSchema string = ` +type TSKHolder struct { + K1 String + TSK Any + K2 String +} +` + +type testTSKHolder struct { + K1 string + TSK TipSetKey + K2 string +} + +func TestBindnodeTipSetKey(t *testing.T) { + req := require.New(t) + + cb := cid.V1Builder{Codec: cid.DagCBOR, MhType: multihash.BLAKE2B_MIN + 31} + c1, _ := cb.Sum([]byte("a")) + c2, _ := cb.Sum([]byte("b")) + c3, _ := cb.Sum([]byte("c")) + tsk := NewTipSetKey(c1, c2, c3) + + typeSystem, err := ipld.LoadSchemaBytes([]byte(ipldSchema)) + req.NoError(err, "should load a properly encoded schema") + schemaType := typeSystem.TypeByName("TSKHolder") + req.NotNil(schemaType, "should have the expected type") + proto := bindnode.Prototype((*testTSKHolder)(nil), schemaType, TipSetKeyAsLinksListBindnodeOption) + nd := bindnode.Wrap(&testTSKHolder{ + K1: "before", + TSK: tsk, + K2: "after", + }, schemaType, TipSetKeyAsLinksListBindnodeOption) + jsonBytes, err := ipld.Encode(nd, dagjson.Encode) + req.NoError(err, "should encode to JSON with ipld-prime") + + // plain json unmarshal, make sure we're compatible + var holder testTSKHolder + err = json.Unmarshal(jsonBytes, &holder) + req.NoError(err, "should decode with encoding/json") + req.Equal("before", holder.K1) + req.Equal("after", holder.K2) + req.Equal(tsk, holder.TSK) + + // decode with ipld-prime + decoded, err := ipld.DecodeUsingPrototype(jsonBytes, dagjson.Decode, proto) + req.NoError(err, "should decode with ipld-prime") + tskPtr := bindnode.Unwrap(decoded) + req.NotNil(tskPtr, "should have a non-nil value") + holder2, ok := tskPtr.(*testTSKHolder) + req.True(ok, "should unwrap to the correct go type") + req.Equal("before", holder2.K1) + req.Equal("after", holder2.K2) + req.Equal(tsk, holder2.TSK) +} diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 4c1dbdee4fe..f6c8e456e37 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -3426,17 +3426,20 @@ Response: ```json [ { + "emitter": "f01234", "entries": [ { + "Codec": 42, "Flags": 7, "Key": "string value", - "Codec": 42, "Value": "Ynl0ZSBhcnJheQ==" } ], - "emitter": "f01234", - "reverted": true, "height": 10101, + "msgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "reverted": true, "tipsetKey": [ { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" @@ -3444,10 +3447,7 @@ Response: { "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" } - ], - "msgCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + ] } ] ``` @@ -8874,17 +8874,20 @@ Inputs: Response: ```json { + "emitter": "f01234", "entries": [ { + "Codec": 42, "Flags": 7, "Key": "string value", - "Codec": 42, "Value": "Ynl0ZSBhcnJheQ==" } ], - "emitter": "f01234", - "reverted": true, "height": 10101, + "msgCid": { + "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" + }, + "reverted": true, "tipsetKey": [ { "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" @@ -8892,10 +8895,7 @@ Response: { "/": "bafy2bzacebp3shtrn43k7g3unredz7fxn4gj533d3o43tqn2p2ipxxhrvchve" } - ], - "msgCid": { - "/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4" - } + ] } ``` diff --git a/itests/direct_data_onboard_verified_test.go b/itests/direct_data_onboard_verified_test.go index 0c3de2448d2..3676c5aa8ba 100644 --- a/itests/direct_data_onboard_verified_test.go +++ b/itests/direct_data_onboard_verified_test.go @@ -588,14 +588,15 @@ func ddoVerifiedBuildActorEventsFromMessages(ctx context.Context, t *testing.T, addr, err := address.NewIDAddress(uint64(evt.Emitter)) require.NoError(t, err) - actorEvents = append(actorEvents, &types.ActorEvent{ + ae := types.ActorEvent{ Entries: evt.Entries, Emitter: addr, Reverted: false, Height: ts.Height(), TipSetKey: ts.Key(), MsgCid: m.Cid, - }) + }.AsCompactEncoded() + actorEvents = append(actorEvents, &ae) } } } diff --git a/node/impl/full/actor_events.go b/node/impl/full/actor_events.go index fecd1d2b6ad..784cf41eb97 100644 --- a/node/impl/full/actor_events.go +++ b/node/impl/full/actor_events.go @@ -296,14 +296,15 @@ func (a *ActorEventHandler) SubscribeActorEvents(ctx context.Context, evtFilter return false } - buffer = append(buffer, &types.ActorEvent{ + ae := types.ActorEvent{ Entries: ce.Entries, Emitter: ce.EmitterAddr, Reverted: ce.Reverted, Height: ce.Height, TipSetKey: ce.TipSetKey, MsgCid: ce.MsgCid, - }) + }.AsCompactEncoded() + buffer = append(buffer, &ae) return true } @@ -362,14 +363,15 @@ func getCollected(ctx context.Context, f filter.EventFilter) []*types.ActorEvent var out []*types.ActorEvent for _, e := range ces { - out = append(out, &types.ActorEvent{ + ae := types.ActorEvent{ Entries: e.Entries, Emitter: e.EmitterAddr, Reverted: e.Reverted, Height: e.Height, TipSetKey: e.TipSetKey, MsgCid: e.MsgCid, - }) + }.AsCompactEncoded() + out = append(out, &ae) } return out diff --git a/node/impl/full/actor_events_test.go b/node/impl/full/actor_events_test.go index ab446e57b4a..975a9cd9843 100644 --- a/node/impl/full/actor_events_test.go +++ b/node/impl/full/actor_events_test.go @@ -732,14 +732,15 @@ func epochPtr(i int) *abi.ChainEpoch { func collectedToActorEvents(collected []*filter.CollectedEvent) []*types.ActorEvent { var out []*types.ActorEvent for _, c := range collected { - out = append(out, &types.ActorEvent{ + ae := types.ActorEvent{ Entries: c.Entries, Emitter: c.EmitterAddr, Reverted: c.Reverted, Height: c.Height, TipSetKey: c.TipSetKey, MsgCid: c.MsgCid, - }) + }.AsCompactEncoded() + out = append(out, &ae) } return out }