diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index e011458afee..cd2278ebb13 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index c06286e38b9..bf6fd3e793e 100644 Binary files a/build/openrpc/gateway.json.gz and b/build/openrpc/gateway.json.gz differ diff --git a/chain/types/actor_event.go b/chain/types/actor_event.go index bf95189e19c..db0c40ca457 100644 --- a/chain/types/actor_event.go +++ b/chain/types/actor_event.go @@ -1,7 +1,21 @@ package types import ( + "encoding/base64" + "errors" + "fmt" + "github.com/ipfs/go-cid" + "github.com/ipld/go-ipld-prime" + "github.com/ipld/go-ipld-prime/codec" + "github.com/ipld/go-ipld-prime/codec/dagcbor" + "github.com/ipld/go-ipld-prime/codec/dagjson" + "github.com/ipld/go-ipld-prime/codec/raw" + "github.com/ipld/go-ipld-prime/datamodel" + "github.com/ipld/go-ipld-prime/fluent/qp" + basicnode "github.com/ipld/go-ipld-prime/node/basic" + "github.com/ipld/go-ipld-prime/node/bindnode" + "github.com/ipld/go-ipld-prime/schema" "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" @@ -44,6 +58,8 @@ type ActorEventFilter struct { } type ActorEvent struct { + encodeCompact *bool `json:"-"` // shouldn't be exposed publicly for any reason + // Event entries in log form. Entries []EventEntry `json:"entries"` @@ -65,3 +81,286 @@ type ActorEvent struct { // CID of message that produced this event. MsgCid cid.Cid `json:"msgCid"` } + +// AsCompactEncoded will trigger alternate JSON encoding for ActorEvents, where the event entries +// are encoded as a list of tuple representation structs, rather than a list of maps, values are +// decoded using the specified codec where possible, and they are encoded using dag-json form so +// bytes are represented using the `{"/":{"bytes":"base64"}}` form rather than Go standard base64 +// encoding. +func (ae ActorEvent) AsCompactEncoded() ActorEvent { + ae.encodeCompact = new(bool) + *ae.encodeCompact = true + return ae +} + +func (ae *ActorEvent) UnmarshalJSON(b []byte) error { + nd, err := ipld.Decode(b, dagjson.Decode) + if err != nil { + return err + } + builder := actorEventProto.Representation().NewBuilder() + if err := builder.AssignNode(nd); err != nil { + return err + } + aePtr := bindnode.Unwrap(builder.Build()) + aec, _ := aePtr.(*ActorEvent) // safe to assume type + *ae = *aec + + // check if we were encoded in compact form and set the flag accordingly + entries, _ := nd.LookupByString("entries") + if entries.Length() > 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 }