330 lines
7.9 KiB
Go
330 lines
7.9 KiB
Go
package types
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"reflect"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/gogo/protobuf/jsonpb"
|
|
proto "github.com/gogo/protobuf/proto"
|
|
abci "github.com/tendermint/tendermint/abci/types"
|
|
|
|
"github.com/cosmos/cosmos-sdk/codec"
|
|
)
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Event Manager
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// EventManager implements a simple wrapper around a slice of Event objects that
|
|
// can be emitted from.
|
|
type EventManager struct {
|
|
events Events
|
|
}
|
|
|
|
func NewEventManager() *EventManager {
|
|
return &EventManager{EmptyEvents()}
|
|
}
|
|
|
|
func (em *EventManager) Events() Events { return em.events }
|
|
|
|
// EmitEvent stores a single Event object.
|
|
// Deprecated: Use EmitTypedEvent
|
|
func (em *EventManager) EmitEvent(event Event) {
|
|
em.events = em.events.AppendEvent(event)
|
|
}
|
|
|
|
// EmitEvents stores a series of Event objects.
|
|
// Deprecated: Use EmitTypedEvents
|
|
func (em *EventManager) EmitEvents(events Events) {
|
|
em.events = em.events.AppendEvents(events)
|
|
}
|
|
|
|
// ABCIEvents returns all stored Event objects as abci.Event objects.
|
|
func (em EventManager) ABCIEvents() []abci.Event {
|
|
return em.events.ToABCIEvents()
|
|
}
|
|
|
|
// EmitTypedEvent takes typed event and emits converting it into Event
|
|
func (em *EventManager) EmitTypedEvent(tev proto.Message) error {
|
|
event, err := TypedEventToEvent(tev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
em.EmitEvent(event)
|
|
return nil
|
|
}
|
|
|
|
// EmitTypedEvents takes series of typed events and emit
|
|
func (em *EventManager) EmitTypedEvents(tevs ...proto.Message) error {
|
|
events := make(Events, len(tevs))
|
|
for i, tev := range tevs {
|
|
res, err := TypedEventToEvent(tev)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
events[i] = res
|
|
}
|
|
|
|
em.EmitEvents(events)
|
|
return nil
|
|
}
|
|
|
|
// TypedEventToEvent takes typed event and converts to Event object
|
|
func TypedEventToEvent(tev proto.Message) (Event, error) {
|
|
evtType := proto.MessageName(tev)
|
|
evtJSON, err := codec.ProtoMarshalJSON(tev, nil)
|
|
if err != nil {
|
|
return Event{}, err
|
|
}
|
|
|
|
var attrMap map[string]json.RawMessage
|
|
err = json.Unmarshal(evtJSON, &attrMap)
|
|
if err != nil {
|
|
return Event{}, err
|
|
}
|
|
|
|
attrs := make([]abci.EventAttribute, 0, len(attrMap))
|
|
for k, v := range attrMap {
|
|
attrs = append(attrs, abci.EventAttribute{
|
|
Key: []byte(k),
|
|
Value: v,
|
|
})
|
|
}
|
|
|
|
return Event{
|
|
Type: evtType,
|
|
Attributes: attrs,
|
|
}, nil
|
|
}
|
|
|
|
// ParseTypedEvent converts abci.Event back to typed event
|
|
func ParseTypedEvent(event abci.Event) (proto.Message, error) {
|
|
concreteGoType := proto.MessageType(event.Type)
|
|
if concreteGoType == nil {
|
|
return nil, fmt.Errorf("failed to retrieve the message of type %q", event.Type)
|
|
}
|
|
|
|
var value reflect.Value
|
|
if concreteGoType.Kind() == reflect.Ptr {
|
|
value = reflect.New(concreteGoType.Elem())
|
|
} else {
|
|
value = reflect.Zero(concreteGoType)
|
|
}
|
|
|
|
protoMsg, ok := value.Interface().(proto.Message)
|
|
if !ok {
|
|
return nil, fmt.Errorf("%q does not implement proto.Message", event.Type)
|
|
}
|
|
|
|
attrMap := make(map[string]json.RawMessage)
|
|
for _, attr := range event.Attributes {
|
|
attrMap[string(attr.Key)] = attr.Value
|
|
}
|
|
|
|
attrBytes, err := json.Marshal(attrMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
err = jsonpb.Unmarshal(strings.NewReader(string(attrBytes)), protoMsg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return protoMsg, nil
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Events
|
|
// ----------------------------------------------------------------------------
|
|
|
|
type (
|
|
// Event is a type alias for an ABCI Event
|
|
Event abci.Event
|
|
|
|
// Events defines a slice of Event objects
|
|
Events []Event
|
|
)
|
|
|
|
// NewEvent creates a new Event object with a given type and slice of one or more
|
|
// attributes.
|
|
func NewEvent(ty string, attrs ...Attribute) Event {
|
|
e := Event{Type: ty}
|
|
|
|
for _, attr := range attrs {
|
|
e.Attributes = append(e.Attributes, attr.ToKVPair())
|
|
}
|
|
|
|
return e
|
|
}
|
|
|
|
// NewAttribute returns a new key/value Attribute object.
|
|
func NewAttribute(k, v string) Attribute {
|
|
return Attribute{k, v}
|
|
}
|
|
|
|
// EmptyEvents returns an empty slice of events.
|
|
func EmptyEvents() Events {
|
|
return make(Events, 0)
|
|
}
|
|
|
|
func (a Attribute) String() string {
|
|
return fmt.Sprintf("%s: %s", a.Key, a.Value)
|
|
}
|
|
|
|
// ToKVPair converts an Attribute object into a Tendermint key/value pair.
|
|
func (a Attribute) ToKVPair() abci.EventAttribute {
|
|
return abci.EventAttribute{Key: toBytes(a.Key), Value: toBytes(a.Value)}
|
|
}
|
|
|
|
// AppendAttributes adds one or more attributes to an Event.
|
|
func (e Event) AppendAttributes(attrs ...Attribute) Event {
|
|
for _, attr := range attrs {
|
|
e.Attributes = append(e.Attributes, attr.ToKVPair())
|
|
}
|
|
return e
|
|
}
|
|
|
|
// AppendEvent adds an Event to a slice of events.
|
|
func (e Events) AppendEvent(event Event) Events {
|
|
return append(e, event)
|
|
}
|
|
|
|
// AppendEvents adds a slice of Event objects to an exist slice of Event objects.
|
|
func (e Events) AppendEvents(events Events) Events {
|
|
return append(e, events...)
|
|
}
|
|
|
|
// ToABCIEvents converts a slice of Event objects to a slice of abci.Event
|
|
// objects.
|
|
func (e Events) ToABCIEvents() []abci.Event {
|
|
res := make([]abci.Event, len(e))
|
|
for i, ev := range e {
|
|
res[i] = abci.Event{Type: ev.Type, Attributes: ev.Attributes}
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
func toBytes(i interface{}) []byte {
|
|
switch x := i.(type) {
|
|
case []uint8:
|
|
return x
|
|
case string:
|
|
return []byte(x)
|
|
default:
|
|
panic(i)
|
|
}
|
|
}
|
|
|
|
// Common event types and attribute keys
|
|
var (
|
|
EventTypeMessage = "message"
|
|
|
|
AttributeKeyAction = "action"
|
|
AttributeKeyModule = "module"
|
|
AttributeKeySender = "sender"
|
|
AttributeKeyAmount = "amount"
|
|
)
|
|
|
|
type (
|
|
// StringAttributes defines a slice of StringEvents objects.
|
|
StringEvents []StringEvent
|
|
)
|
|
|
|
func (se StringEvents) String() string {
|
|
var sb strings.Builder
|
|
|
|
for _, e := range se {
|
|
sb.WriteString(fmt.Sprintf("\t\t- %s\n", e.Type))
|
|
|
|
for _, attr := range e.Attributes {
|
|
sb.WriteString(fmt.Sprintf("\t\t\t- %s\n", attr.String()))
|
|
}
|
|
}
|
|
|
|
return strings.TrimRight(sb.String(), "\n")
|
|
}
|
|
|
|
// Flatten returns a flattened version of StringEvents by grouping all attributes
|
|
// per unique event type.
|
|
func (se StringEvents) Flatten() StringEvents {
|
|
flatEvents := make(map[string][]Attribute)
|
|
|
|
for _, e := range se {
|
|
flatEvents[e.Type] = append(flatEvents[e.Type], e.Attributes...)
|
|
}
|
|
keys := make([]string, 0, len(flatEvents))
|
|
res := make(StringEvents, 0, len(flatEvents)) // appeneded to keys, same length of what is allocated to keys
|
|
|
|
for ty := range flatEvents {
|
|
keys = append(keys, ty)
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
for _, ty := range keys {
|
|
res = append(res, StringEvent{Type: ty, Attributes: flatEvents[ty]})
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// StringifyEvent converts an Event object to a StringEvent object.
|
|
func StringifyEvent(e abci.Event) StringEvent {
|
|
res := StringEvent{Type: e.Type}
|
|
|
|
for _, attr := range e.Attributes {
|
|
res.Attributes = append(
|
|
res.Attributes,
|
|
Attribute{string(attr.Key), string(attr.Value)},
|
|
)
|
|
}
|
|
|
|
return res
|
|
}
|
|
|
|
// StringifyEvents converts a slice of Event objects into a slice of StringEvent
|
|
// objects.
|
|
func StringifyEvents(events []abci.Event) StringEvents {
|
|
res := make(StringEvents, 0, len(events))
|
|
|
|
for _, e := range events {
|
|
res = append(res, StringifyEvent(e))
|
|
}
|
|
|
|
return res.Flatten()
|
|
}
|
|
|
|
// MarkEventsToIndex returns the set of ABCI events, where each event's attribute
|
|
// has it's index value marked based on the provided set of events to index.
|
|
func MarkEventsToIndex(events []abci.Event, indexSet map[string]struct{}) []abci.Event {
|
|
indexAll := len(indexSet) == 0
|
|
updatedEvents := make([]abci.Event, len(events))
|
|
|
|
for i, e := range events {
|
|
updatedEvent := abci.Event{
|
|
Type: e.Type,
|
|
Attributes: make([]abci.EventAttribute, len(e.Attributes)),
|
|
}
|
|
|
|
for j, attr := range e.Attributes {
|
|
_, index := indexSet[fmt.Sprintf("%s.%s", e.Type, attr.Key)]
|
|
updatedAttr := abci.EventAttribute{
|
|
Key: attr.Key,
|
|
Value: attr.Value,
|
|
Index: index || indexAll,
|
|
}
|
|
|
|
updatedEvent.Attributes[j] = updatedAttr
|
|
}
|
|
|
|
updatedEvents[i] = updatedEvent
|
|
}
|
|
|
|
return updatedEvents
|
|
}
|