From 40f1a57f2d8615556c711dede1d4b72fae73040a Mon Sep 17 00:00:00 2001 From: Urvi Date: Fri, 17 Jan 2025 15:27:30 -0800 Subject: [PATCH] Add unit tests --- .../ingest/processors/operations_processor.go | 57 ++++----- .../transaction_operation_wrapper_test.go | 111 ++++++++++++++++++ 2 files changed, 141 insertions(+), 27 deletions(-) diff --git a/services/horizon/internal/ingest/processors/operations_processor.go b/services/horizon/internal/ingest/processors/operations_processor.go index 04731dcc60..563bcd1a92 100644 --- a/services/horizon/internal/ingest/processors/operations_processor.go +++ b/services/horizon/internal/ingest/processors/operations_processor.go @@ -1047,33 +1047,6 @@ func (operation *transactionOperationWrapper) Participants() ([]xdr.AccountId, e case xdr.OperationTypeLiquidityPoolWithdraw: // the only direct participant is the source_account case xdr.OperationTypeInvokeHostFunction: - changes, err := operation.transaction.GetOperationChanges(operation.index) - if err != nil { - return participants, err - } - - for _, change := range changes { - if change.Type == xdr.LedgerEntryTypeAccount || change.Type == xdr.LedgerEntryTypeTrustline { - var data xdr.LedgerEntryData - switch { - case change.Post != nil: - data = change.Post.Data - case change.Pre != nil: - data = change.Pre.Data - default: - log.Errorf("Change Type %s with no pre or post", change.Type.String()) - continue - } - - switch change.Type { - case xdr.LedgerEntryTypeAccount: - participants = append(participants, data.MustAccount().AccountId) - case xdr.LedgerEntryTypeTrustline: - participants = append(participants, data.MustTrustLine().AccountId) - } - } - } - diagnosticEvents, err := operation.transaction.GetDiagnosticEvents() if err != nil { return participants, err @@ -1081,6 +1054,8 @@ func (operation *transactionOperationWrapper) Participants() ([]xdr.AccountId, e for _, contractEvent := range filterEvents(diagnosticEvents) { if sacEvent, err := contractevents.NewStellarAssetContractEvent(&contractEvent, operation.network); err == nil { + // 'to' and 'from' fields in these events can be either a Contract address or an Account address. We're + // only interested in account addresses and will skip Contract addresses. switch sacEvent.GetType() { case contractevents.EventTypeTransfer: transferEvt := sacEvent.(*contractevents.TransferEvent) @@ -1109,6 +1084,34 @@ func (operation *transactionOperationWrapper) Participants() ([]xdr.AccountId, e } } + // The SAC events above should be sufficient to identify the participating accounts. However, + // to be thorough, we will also iterate through the operation Changes to ensure no participants are missed. + changes, err := operation.transaction.GetOperationChanges(operation.index) + if err != nil { + return participants, err + } + + for _, change := range changes { + if change.Type == xdr.LedgerEntryTypeAccount || change.Type == xdr.LedgerEntryTypeTrustline { + var data xdr.LedgerEntryData + switch { + case change.Post != nil: + data = change.Post.Data + case change.Pre != nil: + data = change.Pre.Data + default: + log.Errorf("Change Type %s with no pre or post", change.Type.String()) + continue + } + + switch change.Type { + case xdr.LedgerEntryTypeAccount: + participants = append(participants, data.MustAccount().AccountId) + case xdr.LedgerEntryTypeTrustline: + participants = append(participants, data.MustTrustLine().AccountId) + } + } + } case xdr.OperationTypeExtendFootprintTtl: // the only direct participant is the source_account case xdr.OperationTypeRestoreFootprint: diff --git a/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go b/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go index 9fe5e226bd..bfd0e65e02 100644 --- a/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go +++ b/services/horizon/internal/ingest/processors/transaction_operation_wrapper_test.go @@ -3,12 +3,16 @@ package processors import ( + "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" + "github.com/stellar/go/keypair" "github.com/stellar/go/protocols/horizon/base" + "github.com/stellar/go/strkey" + "github.com/stellar/go/support/contractevents" "github.com/stellar/go/ingest" "github.com/stellar/go/services/horizon/internal/db2/history" @@ -2285,3 +2289,110 @@ func TestDetailsCoversAllOperationTypes(t *testing.T) { _, err := operation.Details() assert.ErrorContains(t, err, "unknown operation type: ") } + +func TestTestInvokeHostFnOperationParticipants(t *testing.T) { + sourceAddress := "GAUJETIZVEP2NRYLUESJ3LS66NVCEGMON4UDCBCSBEVPIID773P2W6AY" + source := xdr.MustMuxedAddress(sourceAddress) + + randomIssuer := keypair.MustRandom() + randomAsset := xdr.MustNewCreditAsset("TESTING", randomIssuer.Address()) + passphrase := "passphrase" + randomAccount := keypair.MustRandom().Address() + + burnEvtAcc := keypair.MustRandom().Address() + mintEvtAcc := keypair.MustRandom().Address() + clawbkEvtAcc := keypair.MustRandom().Address() + transferEvtFromAcc := keypair.MustRandom().Address() + transferEvtToAcc := keypair.MustRandom().Address() + + transferContractEvent := contractevents.GenerateEvent(contractevents.EventTypeTransfer, transferEvtFromAcc, transferEvtToAcc, "", randomAsset, big.NewInt(10000000), passphrase) + mintContractEvent := contractevents.GenerateEvent(contractevents.EventTypeMint, "", mintEvtAcc, randomAccount, randomAsset, big.NewInt(10000000), passphrase) + burnContractEvent := contractevents.GenerateEvent(contractevents.EventTypeBurn, burnEvtAcc, "", randomAccount, randomAsset, big.NewInt(10000000), passphrase) + clawbackContractEvent := contractevents.GenerateEvent(contractevents.EventTypeClawback, clawbkEvtAcc, "", randomAccount, randomAsset, big.NewInt(10000000), passphrase) + + tx1 := ingest.LedgerTransaction{ + UnsafeMeta: xdr.TransactionMeta{ + V: 3, + V3: &xdr.TransactionMetaV3{ + SorobanMeta: &xdr.SorobanTransactionMeta{ + Events: []xdr.ContractEvent{ + transferContractEvent, + burnContractEvent, + mintContractEvent, + clawbackContractEvent, + }, + }, + }, + }, + } + + wrapper1 := transactionOperationWrapper{ + transaction: tx1, + operation: xdr.Operation{ + SourceAccount: &source, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + }, + }, + network: passphrase, + } + + participants, err := wrapper1.Participants() + assert.NoError(t, err) + assert.ElementsMatch(t, + []xdr.AccountId{ + xdr.MustAddress(source.Address()), + xdr.MustAddress(mintEvtAcc), + xdr.MustAddress(burnEvtAcc), + xdr.MustAddress(clawbkEvtAcc), + xdr.MustAddress(transferEvtFromAcc), + xdr.MustAddress(transferEvtToAcc), + }, + participants, + ) + + contractId := [32]byte{} + zeroContractStrKey, err := strkey.Encode(strkey.VersionByteContract, contractId[:]) + assert.NoError(t, err) + + transferContractEvent = contractevents.GenerateEvent(contractevents.EventTypeTransfer, zeroContractStrKey, zeroContractStrKey, "", randomAsset, big.NewInt(10000000), passphrase) + mintContractEvent = contractevents.GenerateEvent(contractevents.EventTypeMint, "", zeroContractStrKey, randomAccount, randomAsset, big.NewInt(10000000), passphrase) + burnContractEvent = contractevents.GenerateEvent(contractevents.EventTypeBurn, zeroContractStrKey, "", randomAccount, randomAsset, big.NewInt(10000000), passphrase) + clawbackContractEvent = contractevents.GenerateEvent(contractevents.EventTypeClawback, zeroContractStrKey, "", randomAccount, randomAsset, big.NewInt(10000000), passphrase) + + tx2 := ingest.LedgerTransaction{ + UnsafeMeta: xdr.TransactionMeta{ + V: 3, + V3: &xdr.TransactionMetaV3{ + SorobanMeta: &xdr.SorobanTransactionMeta{ + Events: []xdr.ContractEvent{ + transferContractEvent, + burnContractEvent, + mintContractEvent, + clawbackContractEvent, + }, + }, + }, + }, + } + + wrapper2 := transactionOperationWrapper{ + transaction: tx2, + operation: xdr.Operation{ + SourceAccount: &source, + Body: xdr.OperationBody{ + Type: xdr.OperationTypeInvokeHostFunction, + }, + }, + network: passphrase, + } + + participants, err = wrapper2.Participants() + assert.NoError(t, err) + assert.ElementsMatch(t, + []xdr.AccountId{ + xdr.MustAddress(source.Address()), + }, + participants, + ) +}