Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ingest: Extract diagnostic events in GetOperationEvents() #4820

Merged
merged 2 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions ingest/ledger_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func operationChanges(ops []xdr.OperationMeta, index uint32) []Change {
}

// GetOperationEvents returns all contract events emitted by a given operation.
func (t *LedgerTransaction) GetOperationEvents(operationIndex uint32) ([]xdr.ContractEvent, error) {
func (t *LedgerTransaction) GetOperationEvents(operationIndex uint32) ([]xdr.DiagnosticEvent, error) {
// Ignore operations meta if txInternalError https://github.com/stellar/go/issues/2111
if t.txInternalError() {
return nil, nil
Expand All @@ -165,11 +165,23 @@ func (t *LedgerTransaction) GetOperationEvents(operationIndex uint32) ([]xdr.Con
case 2:
return nil, nil
case 3:
diagnosticEventsByOperation := t.UnsafeMeta.MustV3().DiagnosticEvents
if int(operationIndex) < len(diagnosticEventsByOperation) {
return diagnosticEventsByOperation[operationIndex].Events, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does int(operationIndex) < len(diagnosticEventsByOperation) mean functionally? just wanted to understand reason this triggers returning immediately.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it means that there are diagnostic events for that particular operation which are present in t.UnsafeMeta.MustV3().DiagnosticEvents

}
eventsByOperation := t.UnsafeMeta.MustV3().Events
if int(operationIndex) >= len(eventsByOperation) {
return nil, nil
}
return eventsByOperation[operationIndex].Events, nil
events := eventsByOperation[operationIndex].Events
diagnosticEvents := make([]xdr.DiagnosticEvent, len(events))
for i, event := range events {
diagnosticEvents[i] = xdr.DiagnosticEvent{
InSuccessfulContractCall: true,
Event: event,
}
}
return diagnosticEvents, nil
default:
return nil, fmt.Errorf("unsupported TransactionMeta version: %v", t.UnsafeMeta.V)
}
Expand Down
104 changes: 101 additions & 3 deletions ingest/ledger_transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ func TestGetOperationEvents(t *testing.T) {
events, err := tx.GetOperationEvents(0)
assert.NoError(t, err)
assert.Len(t, events, 1)
assert.Equal(t, *events[0].Body.V0.Data.U32, values[0])
assert.True(t, events[0].InSuccessfulContractCall)
assert.Equal(t, *events[0].Event.Body.V0.Data.U32, values[0])

events, err = tx.GetOperationEvents(1)
assert.NoError(t, err)
Expand All @@ -79,8 +80,105 @@ func TestGetOperationEvents(t *testing.T) {
events, err = tx.GetOperationEvents(2)
assert.NoError(t, err)
assert.Len(t, events, 2)
assert.Equal(t, *events[0].Body.V0.Data.U32, values[1])
assert.Equal(t, *events[1].Body.V0.Data.U32, values[2])
assert.True(t, events[0].InSuccessfulContractCall)
assert.Equal(t, *events[0].Event.Body.V0.Data.U32, values[1])
assert.True(t, events[1].InSuccessfulContractCall)
assert.Equal(t, *events[1].Event.Body.V0.Data.U32, values[2])

events, err = tx.GetOperationEvents(3)
assert.NoError(t, err)
assert.Empty(t, events)

tx.UnsafeMeta.V = 0
_, err = tx.GetOperationEvents(0)
assert.EqualError(t, err, "unsupported TransactionMeta version: 0")

tx.UnsafeMeta.V = 4
_, err = tx.GetOperationEvents(0)
assert.EqualError(t, err, "unsupported TransactionMeta version: 4")

tx.UnsafeMeta.V = 1
events, err = tx.GetOperationEvents(0)
assert.NoError(t, err)
assert.Empty(t, events)

tx.UnsafeMeta.V = 2
events, err = tx.GetOperationEvents(0)
assert.NoError(t, err)
assert.Empty(t, events)
}

func TestGetDiagnosticEvents(t *testing.T) {
values := make([]xdr.Uint32, 3)
for i := range values {
values[i] = xdr.Uint32(i)
}
tx := LedgerTransaction{
FeeChanges: xdr.LedgerEntryChanges{},
UnsafeMeta: xdr.TransactionMeta{
V: 3,
V3: &xdr.TransactionMetaV3{
DiagnosticEvents: []xdr.OperationDiagnosticEvents{
{Events: []xdr.DiagnosticEvent{
{
InSuccessfulContractCall: false,
Event: xdr.ContractEvent{
Type: xdr.ContractEventTypeSystem,
Body: xdr.ContractEventBody{
V: 0,
V0: &xdr.ContractEventV0{
Data: xdr.ScVal{Type: xdr.ScValTypeScvU32, U32: &values[0]},
},
},
},
},
}},
{Events: []xdr.DiagnosticEvent{}},
{Events: []xdr.DiagnosticEvent{
{
InSuccessfulContractCall: true,

Event: xdr.ContractEvent{
Type: xdr.ContractEventTypeSystem,
Body: xdr.ContractEventBody{
V: 0,
V0: &xdr.ContractEventV0{
Data: xdr.ScVal{Type: xdr.ScValTypeScvU32, U32: &values[1]},
},
},
}},
{
InSuccessfulContractCall: true,
Event: xdr.ContractEvent{
Type: xdr.ContractEventTypeSystem,
Body: xdr.ContractEventBody{
V: 0,
V0: &xdr.ContractEventV0{
Data: xdr.ScVal{Type: xdr.ScValTypeScvU32, U32: &values[2]},
},
},
}},
}},
},
},
}}
events, err := tx.GetOperationEvents(0)
assert.NoError(t, err)
assert.Len(t, events, 1)
assert.False(t, events[0].InSuccessfulContractCall)
assert.Equal(t, *events[0].Event.Body.V0.Data.U32, values[0])

events, err = tx.GetOperationEvents(1)
assert.NoError(t, err)
assert.Empty(t, events)

events, err = tx.GetOperationEvents(2)
assert.NoError(t, err)
assert.Len(t, events, 2)
assert.True(t, events[0].InSuccessfulContractCall)
assert.Equal(t, *events[0].Event.Body.V0.Data.U32, values[1])
assert.True(t, events[1].InSuccessfulContractCall)
assert.Equal(t, *events[1].Event.Body.V0.Data.U32, values[2])

events, err = tx.GetOperationEvents(3)
assert.NoError(t, err)
Expand Down
15 changes: 13 additions & 2 deletions services/horizon/internal/ingest/processors/effects_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,14 +236,14 @@ func (operation *transactionOperationWrapper) effects() ([]effect, error) {
case xdr.OperationTypeInvokeHostFunction:
// If there's an invokeHostFunction operation, there's definitely V3
// meta in the transaction, which means this error is real.
events, innerErr := operation.transaction.GetOperationEvents(operation.index)
diagnosticEvents, innerErr := operation.transaction.GetOperationEvents(operation.index)
if innerErr != nil {
return nil, innerErr
}

// For now, the only effects are related to the events themselves.
// Possible add'l work: https://github.com/stellar/go/issues/4585
err = wrapper.addInvokeHostFunctionEffects(events)
err = wrapper.addInvokeHostFunctionEffects(filterEvents(diagnosticEvents))

default:
return nil, fmt.Errorf("unknown operation type: %s", op.Body.Type)
Expand Down Expand Up @@ -274,6 +274,17 @@ func (operation *transactionOperationWrapper) effects() ([]effect, error) {
return wrapper.effects, nil
}

func filterEvents(diagnosticEvents []xdr.DiagnosticEvent) []xdr.ContractEvent {
var filtered []xdr.ContractEvent
for _, diagnosticEvent := range diagnosticEvents {
if !diagnosticEvent.InSuccessfulContractCall || diagnosticEvent.Event.Type != xdr.ContractEventTypeContract {
continue
}
filtered = append(filtered, diagnosticEvent.Event)
}
return filtered
}

type effectsWrapper struct {
effects []effect
operation *transactionOperationWrapper
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,13 +268,13 @@ func (operation *transactionOperationWrapper) IsPayment() bool {
case xdr.OperationTypeAccountMerge:
return true
case xdr.OperationTypeInvokeHostFunction:
events, err := operation.transaction.GetOperationEvents(0)
diagnosticEvents, err := operation.transaction.GetOperationEvents(operation.index)
if err != nil {
return false
}
// scan all the contract events for at least one SAC event, qualified to be a payment
// in horizon
for _, contractEvent := range events {
for _, contractEvent := range filterEvents(diagnosticEvents) {
if sacEvent, err := contractevents.NewStellarAssetContractEvent(&contractEvent, operation.network); err == nil {
switch sacEvent.GetType() {
case contractevents.EventTypeTransfer:
Expand Down Expand Up @@ -719,14 +719,14 @@ func (operation *transactionOperationWrapper) Details() (map[string]interface{},
func (operation *transactionOperationWrapper) parseAssetBalanceChangesFromContractEvents() ([]map[string]interface{}, error) {
balanceChanges := []map[string]interface{}{}

events, err := operation.transaction.GetOperationEvents(0)
diagnosticEvents, err := operation.transaction.GetOperationEvents(operation.index)
if err != nil {
// this operation in this context must be an InvokeHostFunctionOp, therefore V3Meta should be present
// as it's in same soroban model, so if any err, it's real,
return nil, err
}

for _, contractEvent := range events {
for _, contractEvent := range filterEvents(diagnosticEvents) {
// Parse the xdr contract event to contractevents.StellarAssetContractEvent model

// has some convenience like to/from attributes are expressed in strkey format for accounts(G...) and contracts(C...)
Expand Down