diff --git a/processors/contract/contract_data.go b/processors/contract/contract_data.go index 1c94e95e75..0f54d5ee41 100644 --- a/processors/contract/contract_data.go +++ b/processors/contract/contract_data.go @@ -128,8 +128,8 @@ func (t *TransformContractDataStruct) TransformContractData(ledgerChange ingest. ledgerSequence := header.Header.LedgerSeq - outputKey, outputKeyDecoded := SerializeScVal(contractData.Key) - outputVal, outputValDecoded := SerializeScVal(contractData.Val) + outputKey, outputKeyDecoded := contractData.Key.Serialize() + outputVal, outputValDecoded := contractData.Val.Serialize() outputContractDataXDR, err := xdr.MarshalBase64(contractData) if err != nil { diff --git a/processors/contract/contract_events.go b/processors/contract/contract_events.go index 23395a7145..848b67be9c 100644 --- a/processors/contract/contract_events.go +++ b/processors/contract/contract_events.go @@ -1,13 +1,11 @@ package contract import ( - "encoding/base64" "fmt" "time" "github.com/stellar/go/ingest" "github.com/stellar/go/processors/utils" - "github.com/stellar/go/strkey" "github.com/stellar/go/toid" "github.com/stellar/go/xdr" ) @@ -65,20 +63,17 @@ func TransformContractEvent(transaction ingest.LedgerTransaction, lhe xdr.Ledger outputType := event.Type outputTypeString := event.Type.String() - eventTopics := getEventTopics(event.Body) - outputTopics, outputTopicsDecoded := SerializeScValArray(eventTopics) + eventTopics := event.Body.GetTopics() + outputTopics, outputTopicsDecoded := xdr.SerializeScValArray(eventTopics) outputTopicsJson["topics"] = outputTopics outputTopicsDecodedJson["topics_decoded"] = outputTopicsDecoded - eventData := getEventData(event.Body) - outputData, outputDataDecoded := SerializeScVal(eventData) + eventData := event.Body.GetData() + outputData, outputDataDecoded := eventData.Serialize() - // Convert the xdrContactId to string - // TODO: https://stellarorg.atlassian.net/browse/HUBBLE-386 this should be a stellar/go/xdr function + // Convert the xdr ContractId to string if event.ContractId != nil { - contractId := *event.ContractId - contractIdByte, _ := contractId.MarshalBinary() - outputContractId, _ = strkey.Encode(strkey.VersionByteContract, contractIdByte) + outputContractId, _ = event.ContractId.ToContractAddress() } outputContractEventXDR, err := xdr.MarshalBase64(contractEvent) @@ -112,60 +107,8 @@ func TransformContractEvent(transaction ingest.LedgerTransaction, lhe xdr.Ledger return transformedContractEvents, nil } -// TODO this should be a stellar/go/xdr function -func getEventTopics(eventBody xdr.ContractEventBody) []xdr.ScVal { - switch eventBody.V { - case 0: - contractEventV0 := eventBody.MustV0() - return contractEventV0.Topics - default: - panic("unsupported event body version: " + string(eventBody.V)) - } -} - -// TODO this should be a stellar/go/xdr function -func getEventData(eventBody xdr.ContractEventBody) xdr.ScVal { - switch eventBody.V { - case 0: - contractEventV0 := eventBody.MustV0() - return contractEventV0.Data - default: - panic("unsupported event body version: " + string(eventBody.V)) - } -} - -// TODO this should also be used in the operations processor -func SerializeScVal(scVal xdr.ScVal) (map[string]string, map[string]string) { - serializedData := map[string]string{} - serializedData["value"] = "n/a" - serializedData["type"] = "n/a" - - serializedDataDecoded := map[string]string{} - serializedDataDecoded["value"] = "n/a" - serializedDataDecoded["type"] = "n/a" - - if scValTypeName, ok := scVal.ArmForSwitch(int32(scVal.Type)); ok { - serializedData["type"] = scValTypeName - serializedDataDecoded["type"] = scValTypeName - if raw, err := scVal.MarshalBinary(); err == nil { - serializedData["value"] = base64.StdEncoding.EncodeToString(raw) - serializedDataDecoded["value"] = scVal.String() - } - } - - return serializedData, serializedDataDecoded -} - -// TODO this should also be used in the operations processor -func SerializeScValArray(scVals []xdr.ScVal) ([]map[string]string, []map[string]string) { - data := make([]map[string]string, 0, len(scVals)) - dataDecoded := make([]map[string]string, 0, len(scVals)) +// getEventTopics and getEventData functions have been moved to xdr/event.go +// as GetTopics() and GetData() methods on ContractEventBody - for _, scVal := range scVals { - serializedData, serializedDataDecoded := SerializeScVal(scVal) - data = append(data, serializedData) - dataDecoded = append(dataDecoded, serializedDataDecoded) - } - - return data, dataDecoded -} +// SerializeScVal and SerializeScValArray have been moved to xdr/scval_helpers.go +// as methods on ScVal for better reusability across processors diff --git a/xdr/event.go b/xdr/event.go index 2ae97a0d2c..94228281a5 100644 --- a/xdr/event.go +++ b/xdr/event.go @@ -21,3 +21,25 @@ func (eb ContractEventBody) String() string { func (de DiagnosticEvent) String() string { return fmt.Sprintf("%s, successful call: %t", de.Event, de.InSuccessfulContractCall) } + +// GetTopics extracts the topics from a contract event body. +// This is a helper function to abstract the versioning of ContractEventBody. +func (eb ContractEventBody) GetTopics() []ScVal { + switch eb.V { + case 0: + return eb.MustV0().Topics + default: + panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V)) + } +} + +// GetData extracts the data from a contract event body. +// This is a helper function to abstract the versioning of ContractEventBody. +func (eb ContractEventBody) GetData() ScVal { + switch eb.V { + case 0: + return eb.MustV0().Data + default: + panic("unsupported event body version: " + fmt.Sprintf("%d", eb.V)) + } +} diff --git a/xdr/hash.go b/xdr/hash.go index 2a15c18c9c..dc9a047d32 100644 --- a/xdr/hash.go +++ b/xdr/hash.go @@ -1,6 +1,10 @@ package xdr -import "encoding/hex" +import ( + "encoding/hex" + + "github.com/stellar/go/strkey" +) func (h Hash) HexString() string { return hex.EncodeToString(h[:]) @@ -17,3 +21,9 @@ func (s Hash) Equals(o Hash) bool { } return true } + +// ToContractAddress converts a Hash to a Stellar contract address string. +// Returns the contract address in strkey format (C...) and an error if encoding fails. +func (h Hash) ToContractAddress() (string, error) { + return strkey.Encode(strkey.VersionByteContract, h[:]) +} diff --git a/xdr/scval_helpers.go b/xdr/scval_helpers.go new file mode 100644 index 0000000000..665d270d56 --- /dev/null +++ b/xdr/scval_helpers.go @@ -0,0 +1,43 @@ +package xdr + +import "encoding/base64" + +// SerializeScVal converts an ScVal to two map representations: +// 1. A map with base64-encoded value and type +// 2. A map with human-readable decoded value and type +// This is useful for data export and analytics purposes. +func (scVal ScVal) Serialize() (map[string]string, map[string]string) { + serializedData := map[string]string{} + serializedData["value"] = "n/a" + serializedData["type"] = "n/a" + + serializedDataDecoded := map[string]string{} + serializedDataDecoded["value"] = "n/a" + serializedDataDecoded["type"] = "n/a" + + if scValTypeName, ok := scVal.ArmForSwitch(int32(scVal.Type)); ok { + serializedData["type"] = scValTypeName + serializedDataDecoded["type"] = scValTypeName + if raw, err := scVal.MarshalBinary(); err == nil { + serializedData["value"] = base64.StdEncoding.EncodeToString(raw) + serializedDataDecoded["value"] = scVal.String() + } + } + + return serializedData, serializedDataDecoded +} + +// SerializeScValArray converts an array of ScVal to two arrays of map representations. +// Each ScVal is serialized using the Serialize method. +func SerializeScValArray(scVals []ScVal) ([]map[string]string, []map[string]string) { + data := make([]map[string]string, 0, len(scVals)) + dataDecoded := make([]map[string]string, 0, len(scVals)) + + for _, scVal := range scVals { + serializedData, serializedDataDecoded := scVal.Serialize() + data = append(data, serializedData) + dataDecoded = append(dataDecoded, serializedDataDecoded) + } + + return data, dataDecoded +}