diff --git a/CHANGELOG.md b/CHANGELOG.md index 1411dd225..6b47c68bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,10 @@ _WORK IN PROGRESS_ - Added package details to `/satus/entity/details` endpoint. - New endpoint `/statistics/validators/uptime` returns validator uptime data. - New endpoint `/state/key-value/data` returns entries of requested KeyValueStore. +- Return programmatic json with type names for: + - key-value key and data in `/state/key-value/data` endpoint + - non fungible data in `/state/non-fungible/data` endpoint + - events in `/transaction/committed-details` and `/stream/transactions` endpoints. ------- diff --git a/Directory.Packages.props b/Directory.Packages.props index bac272f2b..80526f50b 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -33,4 +33,4 @@ - + \ No newline at end of file diff --git a/src/RadixDlt.CoreApiSdk/Model/TypeInfoModuleFieldTypeInfoSubstate.cs b/src/RadixDlt.CoreApiSdk/Model/TypeInfoModuleFieldTypeInfoSubstate.cs index 32050791a..521441d0f 100644 --- a/src/RadixDlt.CoreApiSdk/Model/TypeInfoModuleFieldTypeInfoSubstate.cs +++ b/src/RadixDlt.CoreApiSdk/Model/TypeInfoModuleFieldTypeInfoSubstate.cs @@ -63,6 +63,7 @@ */ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; namespace RadixDlt.CoreApiSdk.Model; @@ -78,4 +79,30 @@ public IEnumerable GetEntityAddresses() return Enumerable.Empty(); } + + public bool TryGetObjectInstanceSchema([NotNullWhen(true)] out InstanceSchema instanceSchema) + { + instanceSchema = null; + + if (Value.Details is ObjectTypeInfoDetails objectTypeInfoDetails && objectTypeInfoDetails.InstanceSchema != null) + { + instanceSchema = objectTypeInfoDetails.InstanceSchema; + return true; + } + + return false; + } + + public bool TryGetKeyValueStoreSchema([NotNullWhen(true)] out KeyValueStoreSchema keyValueStoreSchema) + { + keyValueStoreSchema = null; + + if (Value.Details is KeyValueStoreTypeInfoDetails keyValueStoreTypeInfoDetails && keyValueStoreTypeInfoDetails.KeyValueStoreInfo.KvStoreSchema != null) + { + keyValueStoreSchema = keyValueStoreTypeInfoDetails.KeyValueStoreInfo.KvStoreSchema; + return true; + } + + return false; + } } diff --git a/src/RadixDlt.NetworkGateway.Abstractions/Model/Extensions.cs b/src/RadixDlt.NetworkGateway.Abstractions/Model/Extensions.cs index 00413de69..3ec6a56ed 100644 --- a/src/RadixDlt.NetworkGateway.Abstractions/Model/Extensions.cs +++ b/src/RadixDlt.NetworkGateway.Abstractions/Model/Extensions.cs @@ -80,4 +80,14 @@ public static ObjectModuleId ToInternalModel(this CoreModel.ObjectModuleId objec _ => throw new UnreachableException($"Didn't expect {objectModuleId} value"), }; } + + public static SborTypeKind ToInternalModel(this CoreModel.LocalTypeIndex.KindEnum indexKind) + { + return indexKind switch + { + CoreModel.LocalTypeIndex.KindEnum.SchemaLocal => SborTypeKind.SchemaLocal, + CoreModel.LocalTypeIndex.KindEnum.WellKnown => SborTypeKind.WellKnown, + _ => throw new UnreachableException($"Didn't expect {indexKind} value"), + }; + } } diff --git a/src/RadixDlt.NetworkGateway.Abstractions/Model/SborTypeKind.cs b/src/RadixDlt.NetworkGateway.Abstractions/Model/SborTypeKind.cs new file mode 100644 index 000000000..09608fa7d --- /dev/null +++ b/src/RadixDlt.NetworkGateway.Abstractions/Model/SborTypeKind.cs @@ -0,0 +1,71 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +namespace RadixDlt.NetworkGateway.Abstractions.Model; + +public enum SborTypeKind +{ + WellKnown, + SchemaLocal, +} diff --git a/src/RadixDlt.NetworkGateway.GatewayApi/gateway-api-schema.yaml b/src/RadixDlt.NetworkGateway.GatewayApi/gateway-api-schema.yaml index c5bf6920c..dc5c09faa 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApi/gateway-api-schema.yaml +++ b/src/RadixDlt.NetworkGateway.GatewayApi/gateway-api-schema.yaml @@ -897,11 +897,11 @@ components: type: object required: - raw_hex - - raw_json + - programmatic_json properties: raw_hex: type: string - raw_json: + programmatic_json: type: object EntityMetadataCollection: @@ -2317,15 +2317,15 @@ components: StateKeyValueStoreDataResponseItem: type: object required: - - key_hex - - value_hex + - key + - value - last_updated_at_state_version - is_locked properties: - key_hex: - $ref: "#/components/schemas/HexString" - value_hex: - $ref: "#/components/schemas/HexString" + key: + $ref: "#/components/schemas/ScryptoSborValue" + value: + $ref: "#/components/schemas/ScryptoSborValue" last_updated_at_state_version: $ref: "#/components/schemas/LastUpdatedAtStateVersion" is_locked: diff --git a/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/EntityMetadataItemValue.cs b/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/EntityMetadataItemValue.cs index d82eb987b..77de37f65 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/EntityMetadataItemValue.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/EntityMetadataItemValue.cs @@ -104,9 +104,9 @@ protected EntityMetadataItemValue() { } /// Initializes a new instance of the class. /// /// rawHex (required). - /// rawJson (required). + /// json (required). /// typed (required). - public EntityMetadataItemValue(string rawHex = default(string), Object rawJson = default(Object), MetadataTypedValue typed = default(MetadataTypedValue)) + public EntityMetadataItemValue(string rawHex = default(string), Object json = default(Object), MetadataTypedValue typed = default(MetadataTypedValue)) { // to ensure "rawHex" is required (not null) if (rawHex == null) @@ -114,12 +114,12 @@ protected EntityMetadataItemValue() { } throw new ArgumentNullException("rawHex is a required property for EntityMetadataItemValue and cannot be null"); } this.RawHex = rawHex; - // to ensure "rawJson" is required (not null) - if (rawJson == null) + // to ensure "json" is required (not null) + if (json == null) { - throw new ArgumentNullException("rawJson is a required property for EntityMetadataItemValue and cannot be null"); + throw new ArgumentNullException("json is a required property for EntityMetadataItemValue and cannot be null"); } - this.RawJson = rawJson; + this.Json = json; // to ensure "typed" is required (not null) if (typed == null) { @@ -135,10 +135,10 @@ protected EntityMetadataItemValue() { } public string RawHex { get; set; } /// - /// Gets or Sets RawJson + /// Gets or Sets Json /// - [DataMember(Name = "raw_json", IsRequired = true, EmitDefaultValue = true)] - public Object RawJson { get; set; } + [DataMember(Name = "json", IsRequired = true, EmitDefaultValue = true)] + public Object Json { get; set; } /// /// Gets or Sets Typed @@ -155,7 +155,7 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("class EntityMetadataItemValue {\n"); sb.Append(" RawHex: ").Append(RawHex).Append("\n"); - sb.Append(" RawJson: ").Append(RawJson).Append("\n"); + sb.Append(" Json: ").Append(Json).Append("\n"); sb.Append(" Typed: ").Append(Typed).Append("\n"); sb.Append("}\n"); return sb.ToString(); @@ -198,9 +198,9 @@ public bool Equals(EntityMetadataItemValue input) this.RawHex.Equals(input.RawHex)) ) && ( - this.RawJson == input.RawJson || - (this.RawJson != null && - this.RawJson.Equals(input.RawJson)) + this.Json == input.Json || + (this.Json != null && + this.Json.Equals(input.Json)) ) && ( this.Typed == input.Typed || @@ -222,9 +222,9 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.RawHex.GetHashCode(); } - if (this.RawJson != null) + if (this.Json != null) { - hashCode = (hashCode * 59) + this.RawJson.GetHashCode(); + hashCode = (hashCode * 59) + this.Json.GetHashCode(); } if (this.Typed != null) { diff --git a/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/ScryptoSborValue.cs b/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/ScryptoSborValue.cs index cb54e3570..079de32cc 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/ScryptoSborValue.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/ScryptoSborValue.cs @@ -104,8 +104,8 @@ protected ScryptoSborValue() { } /// Initializes a new instance of the class. /// /// rawHex (required). - /// rawJson (required). - public ScryptoSborValue(string rawHex = default(string), Object rawJson = default(Object)) + /// json (required). + public ScryptoSborValue(string rawHex = default(string), Object json = default(Object)) { // to ensure "rawHex" is required (not null) if (rawHex == null) @@ -113,12 +113,12 @@ protected ScryptoSborValue() { } throw new ArgumentNullException("rawHex is a required property for ScryptoSborValue and cannot be null"); } this.RawHex = rawHex; - // to ensure "rawJson" is required (not null) - if (rawJson == null) + // to ensure "json" is required (not null) + if (json == null) { - throw new ArgumentNullException("rawJson is a required property for ScryptoSborValue and cannot be null"); + throw new ArgumentNullException("json is a required property for ScryptoSborValue and cannot be null"); } - this.RawJson = rawJson; + this.Json = json; } /// @@ -128,10 +128,10 @@ protected ScryptoSborValue() { } public string RawHex { get; set; } /// - /// Gets or Sets RawJson + /// Gets or Sets Json /// - [DataMember(Name = "raw_json", IsRequired = true, EmitDefaultValue = true)] - public Object RawJson { get; set; } + [DataMember(Name = "json", IsRequired = true, EmitDefaultValue = true)] + public Object Json { get; set; } /// /// Returns the string presentation of the object @@ -142,7 +142,7 @@ public override string ToString() StringBuilder sb = new StringBuilder(); sb.Append("class ScryptoSborValue {\n"); sb.Append(" RawHex: ").Append(RawHex).Append("\n"); - sb.Append(" RawJson: ").Append(RawJson).Append("\n"); + sb.Append(" Json: ").Append(Json).Append("\n"); sb.Append("}\n"); return sb.ToString(); } @@ -184,9 +184,9 @@ public bool Equals(ScryptoSborValue input) this.RawHex.Equals(input.RawHex)) ) && ( - this.RawJson == input.RawJson || - (this.RawJson != null && - this.RawJson.Equals(input.RawJson)) + this.Json == input.Json || + (this.Json != null && + this.Json.Equals(input.Json)) ); } @@ -203,9 +203,9 @@ public override int GetHashCode() { hashCode = (hashCode * 59) + this.RawHex.GetHashCode(); } - if (this.RawJson != null) + if (this.Json != null) { - hashCode = (hashCode * 59) + this.RawJson.GetHashCode(); + hashCode = (hashCode * 59) + this.Json.GetHashCode(); } return hashCode; } diff --git a/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/StateKeyValueStoreDataResponseItem.cs b/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/StateKeyValueStoreDataResponseItem.cs index 03624ed76..87ad15b34 100644 --- a/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/StateKeyValueStoreDataResponseItem.cs +++ b/src/RadixDlt.NetworkGateway.GatewayApiSdk/generated/Model/StateKeyValueStoreDataResponseItem.cs @@ -103,41 +103,39 @@ protected StateKeyValueStoreDataResponseItem() { } /// /// Initializes a new instance of the class. /// - /// Hex-encoded binary blob. (required). - /// Hex-encoded binary blob. (required). + /// key (required). + /// value (required). /// TBD (required). /// isLocked (required). - public StateKeyValueStoreDataResponseItem(string keyHex = default(string), string valueHex = default(string), long lastUpdatedAtStateVersion = default(long), bool isLocked = default(bool)) + public StateKeyValueStoreDataResponseItem(ScryptoSborValue key = default(ScryptoSborValue), ScryptoSborValue value = default(ScryptoSborValue), long lastUpdatedAtStateVersion = default(long), bool isLocked = default(bool)) { - // to ensure "keyHex" is required (not null) - if (keyHex == null) + // to ensure "key" is required (not null) + if (key == null) { - throw new ArgumentNullException("keyHex is a required property for StateKeyValueStoreDataResponseItem and cannot be null"); + throw new ArgumentNullException("key is a required property for StateKeyValueStoreDataResponseItem and cannot be null"); } - this.KeyHex = keyHex; - // to ensure "valueHex" is required (not null) - if (valueHex == null) + this.Key = key; + // to ensure "value" is required (not null) + if (value == null) { - throw new ArgumentNullException("valueHex is a required property for StateKeyValueStoreDataResponseItem and cannot be null"); + throw new ArgumentNullException("value is a required property for StateKeyValueStoreDataResponseItem and cannot be null"); } - this.ValueHex = valueHex; + this.Value = value; this.LastUpdatedAtStateVersion = lastUpdatedAtStateVersion; this.IsLocked = isLocked; } /// - /// Hex-encoded binary blob. + /// Gets or Sets Key /// - /// Hex-encoded binary blob. - [DataMember(Name = "key_hex", IsRequired = true, EmitDefaultValue = true)] - public string KeyHex { get; set; } + [DataMember(Name = "key", IsRequired = true, EmitDefaultValue = true)] + public ScryptoSborValue Key { get; set; } /// - /// Hex-encoded binary blob. + /// Gets or Sets Value /// - /// Hex-encoded binary blob. - [DataMember(Name = "value_hex", IsRequired = true, EmitDefaultValue = true)] - public string ValueHex { get; set; } + [DataMember(Name = "value", IsRequired = true, EmitDefaultValue = true)] + public ScryptoSborValue Value { get; set; } /// /// TBD @@ -160,8 +158,8 @@ public override string ToString() { StringBuilder sb = new StringBuilder(); sb.Append("class StateKeyValueStoreDataResponseItem {\n"); - sb.Append(" KeyHex: ").Append(KeyHex).Append("\n"); - sb.Append(" ValueHex: ").Append(ValueHex).Append("\n"); + sb.Append(" Key: ").Append(Key).Append("\n"); + sb.Append(" Value: ").Append(Value).Append("\n"); sb.Append(" LastUpdatedAtStateVersion: ").Append(LastUpdatedAtStateVersion).Append("\n"); sb.Append(" IsLocked: ").Append(IsLocked).Append("\n"); sb.Append("}\n"); @@ -200,14 +198,14 @@ public bool Equals(StateKeyValueStoreDataResponseItem input) } return ( - this.KeyHex == input.KeyHex || - (this.KeyHex != null && - this.KeyHex.Equals(input.KeyHex)) + this.Key == input.Key || + (this.Key != null && + this.Key.Equals(input.Key)) ) && ( - this.ValueHex == input.ValueHex || - (this.ValueHex != null && - this.ValueHex.Equals(input.ValueHex)) + this.Value == input.Value || + (this.Value != null && + this.Value.Equals(input.Value)) ) && ( this.LastUpdatedAtStateVersion == input.LastUpdatedAtStateVersion || @@ -228,13 +226,13 @@ public override int GetHashCode() unchecked // Overflow is fine, just wrap { int hashCode = 41; - if (this.KeyHex != null) + if (this.Key != null) { - hashCode = (hashCode * 59) + this.KeyHex.GetHashCode(); + hashCode = (hashCode * 59) + this.Key.GetHashCode(); } - if (this.ValueHex != null) + if (this.Value != null) { - hashCode = (hashCode * 59) + this.ValueHex.GetHashCode(); + hashCode = (hashCode * 59) + this.Value.GetHashCode(); } hashCode = (hashCode * 59) + this.LastUpdatedAtStateVersion.GetHashCode(); hashCode = (hashCode * 59) + this.IsLocked.GetHashCode(); diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/CommonDbContext.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/CommonDbContext.cs index 9cd2ea7cd..5f0c7a8c2 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/CommonDbContext.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/CommonDbContext.cs @@ -117,6 +117,8 @@ internal abstract class CommonDbContext : DbContext public DbSet EntityStateHistory => Set(); + public DbSet ValidatorStateHistory => Set(); + public DbSet ValidatorKeyHistory => Set(); public DbSet ValidatorActiveSetHistory => Set(); @@ -139,6 +141,10 @@ internal abstract class CommonDbContext : DbContext public DbSet ValidatorEmissionStatistics => Set(); + public DbSet NonFungibleDataSchemaHistory => Set(); + + public DbSet KeyValueStoreSchemaHistory => Set(); + public CommonDbContext(DbContextOptions options) : base(options) { @@ -163,6 +169,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) modelBuilder.HasPostgresEnum(); modelBuilder.HasPostgresEnum(); modelBuilder.HasPostgresEnum(); + modelBuilder.HasPostgresEnum(); HookupTransactions(modelBuilder); HookupPendingTransactions(modelBuilder); @@ -354,6 +361,9 @@ private static void HookupHistory(ModelBuilder modelBuilder) modelBuilder.Entity() .HasIndex(e => new { e.EntityId, e.FromStateVersion }); + modelBuilder.Entity() + .HasIndex(e => new { EntityId = e.ValidatorEntityId, e.FromStateVersion }); + modelBuilder.Entity() .HasIndex(e => new { e.PackageEntityId, e.FromStateVersion }); @@ -363,6 +373,9 @@ private static void HookupHistory(ModelBuilder modelBuilder) modelBuilder.Entity() .HasIndex(e => new { e.PackageEntityId, e.FromStateVersion }); + modelBuilder.Entity() + .HasIndex(e => new { e.SchemaHash, e.FromStateVersion }); + modelBuilder.Entity() .HasIndex(e => new { e.ValidatorEntityId, e.FromStateVersion }); @@ -377,6 +390,12 @@ private static void HookupHistory(ModelBuilder modelBuilder) modelBuilder.Entity() .HasIndex(e => new { e.KeyValueStoreEntityId, e.Key, e.FromStateVersion }); + + modelBuilder.Entity() + .HasIndex(e => new { EntityId = e.KeyValueStoreEntityId, e.FromStateVersion }); + + modelBuilder.Entity() + .HasIndex(e => new { e.EntityId, e.FromStateVersion }); } private static void HookupStatistics(ModelBuilder modelBuilder) diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/CustomTypes.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/CustomTypes.cs index f193edb4e..816b6d5b9 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/CustomTypes.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/CustomTypes.cs @@ -108,6 +108,7 @@ public static void EnsureConfigured() NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); NpgsqlConnection.GlobalTypeMapper.MapEnum(); + NpgsqlConnection.GlobalTypeMapper.MapEnum(); #pragma warning restore CS0618 _configured = true; diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/GatewayModelExtensions.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/GatewayModelExtensions.cs index 903b1e64f..758e212b6 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/GatewayModelExtensions.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/GatewayModelExtensions.cs @@ -119,8 +119,8 @@ public static GatewayModel.PublicKey ToGatewayPublicKey(this ValidatorPublicKeyH }; } - public static GatewayModel.CommittedTransactionInfo ToGatewayModel(this LedgerTransaction lt, GatewayModel.TransactionCommittedDetailsOptIns optIns, - Dictionary entityIdToAddressMap) + public static GatewayModel.CommittedTransactionInfo ToGatewayModel( + this LedgerTransaction lt, GatewayModel.TransactionCommittedDetailsOptIns optIns, Dictionary entityIdToAddressMap, List? events) { string? payloadHashHex = null; string? intentHashHex = null; @@ -143,7 +143,7 @@ public static GatewayModel.CommittedTransactionInfo ToGatewayModel(this LedgerTr FeeSummary = optIns.ReceiptFeeSummary ? new JRaw(lt.EngineReceipt.FeeSummary) : null, NextEpoch = lt.EngineReceipt.NextEpoch != null ? new JRaw(lt.EngineReceipt.NextEpoch) : null, StateUpdates = optIns.ReceiptStateChanges ? new JRaw(lt.EngineReceipt.StateUpdates) : null, - Events = optIns.ReceiptEvents && lt.EngineReceipt.Events != null ? new JRaw(lt.EngineReceipt.Events) : null, + Events = events?.Select(x => new JRaw(x)).ToList(), }; return new GatewayModel.CommittedTransactionInfo( diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/PostgresLedgerExtenderService.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/PostgresLedgerExtenderService.cs index 7618b44f2..f8b4d1b64 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/PostgresLedgerExtenderService.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/PostgresLedgerExtenderService.cs @@ -556,7 +556,16 @@ private async Task ProcessTransactions(ReadWriteDbContext db ErrorMessage = committedTransaction.Receipt.ErrorMessage, Output = committedTransaction.Receipt.Output != null ? JsonConvert.SerializeObject(committedTransaction.Receipt.Output) : null, NextEpoch = committedTransaction.Receipt.NextEpoch?.ToJson(), - Events = committedTransaction.Receipt.Events != null ? JsonConvert.SerializeObject(committedTransaction.Receipt.Events) : null, + EventsSbors = committedTransaction.Receipt.Events?.Select(x => x.Data.GetDataBytes()).ToArray() ?? Array.Empty(), + EventSchemaHashes = + committedTransaction.Receipt.Events?.Select(x => ((CoreModel.PackageTypePointer)x.Type.TypePointer).SchemaHash.ConvertFromHex()).ToArray() + ?? Array.Empty(), + EventTypeIndexes = + committedTransaction.Receipt.Events?.Select(x => ((CoreModel.PackageTypePointer)x.Type.TypePointer).LocalTypeIndex.Index).ToArray() + ?? Array.Empty(), + EventSborTypeKinds = + committedTransaction.Receipt.Events?.Select(x => ((CoreModel.PackageTypePointer)x.Type.TypePointer).LocalTypeIndex.Kind.ToInternalModel()).ToArray().ToArray() + ?? Array.Empty(), }; ledgerTransactionsToAdd.Add(ledgerTransaction); @@ -589,7 +598,6 @@ private async Task ProcessTransactions(ReadWriteDbContext db var sw = Stopwatch.StartNew(); var knownDbEntities = await readHelper.ExistingEntitiesFor(referencedEntities, token); - dbReadDuration += sw.Elapsed; foreach (var knownDbEntity in knownDbEntities.Values) @@ -735,11 +743,14 @@ private async Task ProcessTransactions(ReadWriteDbContext db var resourceSupplyChanges = new List(); var validatorSetChanges = new List(); var entityStateToAdd = new List(); + var validatorStateToAdd = new List(); var keyValueStoreEntryHistoryToAdd = new List(); var componentMethodRoyaltiesToAdd = new List(); var packageBlueprintHistoryToAdd = new Dictionary(); var packageCodeHistoryToAdd = new List(); var packageSchemaHistoryToAdd = new List(); + var nonFungibleSchemaHistoryToAdd = new List(); + var keyVaulueStoreSchemaHistoryToAdd = new List(); var validatorKeyHistoryToAdd = new Dictionary(); // TODO follow Pointer+ordered List pattern to ensure proper order of ingestion var accountDefaultDepositRuleHistoryToAdd = new List(); var accountResourceDepositRuleHistoryToAdd = new List(); @@ -867,12 +878,12 @@ private async Task ProcessTransactions(ReadWriteDbContext db Key = lookup.PublicKey, }; - entityStateToAdd.Add(new EntityStateHistory + validatorStateToAdd.Add(new ValidatorStateHistory { - Id = sequences.EntityStateHistorySequence++, + Id = sequences.ValidatorStateHistorySequence++, FromStateVersion = stateVersion, - EntityId = referencedEntities.Get((EntityAddress)substateId.EntityAddress).DatabaseId, - State = validator.ToJson(), + ValidatorEntityId = referencedEntities.Get((EntityAddress)substateId.EntityAddress).DatabaseId, + State = validator.Value.ToJson(), }); } @@ -1048,6 +1059,42 @@ private async Task ProcessTransactions(ReadWriteDbContext db IsLocked = methodRoyaltyEntry.IsLocked, }); } + + if (substateData is CoreModel.TypeInfoModuleFieldTypeInfoSubstate typeInfoSubstate) + { + if (typeInfoSubstate.TryGetObjectInstanceSchema(out var instanceSchema)) + { + if (instanceSchema.ProvidedTypes.Count != 1) + { + throw new NotSupportedException("Expected non fungible data with only one data type entry."); + } + + nonFungibleSchemaHistoryToAdd.Add(new NonFungibleSchemaHistory + { + Id = sequences.NonFungibleSchemaHistorySequence++, + FromStateVersion = stateVersion, + EntityId = referencedEntity.DatabaseId, + Schema = instanceSchema.Schema.SborData.Hex.ConvertFromHex(), + SborTypeKind = instanceSchema.ProvidedTypes[0].Kind.ToInternalModel(), + TypeIndex = instanceSchema.ProvidedTypes[0].Index, + }); + } + + if (typeInfoSubstate.TryGetKeyValueStoreSchema(out var keyValueStoreSchema)) + { + keyVaulueStoreSchemaHistoryToAdd.Add(new KeyValueStoreSchemaHistory + { + Id = sequences.KeyValueSchemaHistorySequence++, + FromStateVersion = stateVersion, + KeyValueStoreEntityId = referencedEntity.DatabaseId, + Schema = keyValueStoreSchema.Schema.SborData.Hex.ConvertFromHex(), + KeySborTypeKind = keyValueStoreSchema.KeyType.Kind.ToInternalModel(), + KeyTypeIndex = keyValueStoreSchema.KeyType.Index, + ValueSborTypeKind = keyValueStoreSchema.ValueType.Kind.ToInternalModel(), + ValueTypeIndex = keyValueStoreSchema.ValueType.Index, + }); + } + } } foreach (var deletedSubstate in stateUpdates.DeletedSubstates) @@ -1091,6 +1138,7 @@ private async Task ProcessTransactions(ReadWriteDbContext db // TODO "deposit" and "withdrawal" events should be used to alter entity_resource_aggregated_vaults_history table (drop tmp_tmp_remove_me_once_tx_events_become_available column) var eventEmitterEntity = referencedEntities.Get((EntityAddress)methodEventEmitter.Entity.EntityAddress); + using var decodedEvent = EventDecoder.DecodeEvent(@event, _networkConfigurationProvider.GetNetworkId()); if (EventDecoder.TryGetValidatorEmissionsAppliedEvent(decodedEvent, out var validatorUptimeEvent)) @@ -1786,6 +1834,7 @@ void AggregateEntityResourceVaultInternal(long entityId, long resourceEntityId, rowsInserted += await writeHelper.CopyLedgerTransaction(ledgerTransactionsToAdd, token); rowsInserted += await writeHelper.CopyLedgerTransactionMarkers(ledgerTransactionMarkersToAdd, token); rowsInserted += await writeHelper.CopyEntityStateHistory(entityStateToAdd, token); + rowsInserted += await writeHelper.CopyValidatorStateHistory(validatorStateToAdd, token); rowsInserted += await writeHelper.CopyEntityMetadataHistory(entityMetadataHistoryToAdd, token); rowsInserted += await writeHelper.CopyEntityMetadataAggregateHistory(entityMetadataAggregateHistoryToAdd, token); rowsInserted += await writeHelper.CopyEntityRoleAssignmentsOwnerRoleHistory(entityAccessRulesOwnerRoleHistoryToAdd, token); @@ -1809,6 +1858,8 @@ void AggregateEntityResourceVaultInternal(long entityId, long resourceEntityId, rowsInserted += await writeHelper.CopyAccountDefaultDepositRuleHistory(accountDefaultDepositRuleHistoryToAdd, token); rowsInserted += await writeHelper.CopyAccountResourceDepositRuleHistory(accountResourceDepositRuleHistoryToAdd, token); rowsInserted += await writeHelper.CopyValidatorEmissionStatistics(validatorEmissionStatisticsToAdd, token); + rowsInserted += await writeHelper.CopyNonFungibleDataSchemaHistory(nonFungibleSchemaHistoryToAdd, token); + rowsInserted += await writeHelper.CopyKeyValueStoreSchemaHistory(keyVaulueStoreSchemaHistoryToAdd, token); await writeHelper.UpdateSequences(sequences, token); dbWriteDuration += sw.Elapsed; diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/ReadHelper.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/ReadHelper.cs index 77aa55208..470bc45fc 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/ReadHelper.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/ReadHelper.cs @@ -563,6 +563,7 @@ public async Task LoadSequences(CancellationToken token) nextval('account_default_deposit_rule_history_id_seq') AS AccountDefaultDepositRuleHistorySequence, nextval('account_resource_deposit_rule_history_id_seq') AS AccountResourceDepositRuleHistorySequence, nextval('entity_state_history_id_seq') AS EntityStateHistorySequence, + nextval('validator_state_history_id_seq') AS ValidatorStateHistorySequence, nextval('entities_id_seq') AS EntitySequence, nextval('entity_metadata_history_id_seq') AS EntityMetadataHistorySequence, nextval('entity_metadata_aggregate_history_id_seq') AS EntityMetadataAggregateHistorySequence, @@ -585,7 +586,9 @@ public async Task LoadSequences(CancellationToken token) nextval('package_code_history_id_seq') AS PackageCodeHistorySequence, nextval('package_schema_history_id_seq') AS PackageSchemaHistorySequence, nextval('key_value_store_entry_history_id_seq') AS KeyValueStoreEntryHistorySequence, - nextval('validator_emission_statistics_id_seq') AS ValidatorEmissionStatisticsSequence", + nextval('validator_emission_statistics_id_seq') AS ValidatorEmissionStatisticsSequence, + nextval('non_fungible_schema_history_id_seq') AS NonFungibleSchemaHistorySequence, + nextval('key_value_store_schema_history_id_seq') AS KeyValueSchemaHistorySequence", cancellationToken: token); return await _connection.QueryFirstAsync(cd); diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/SequencesHolder.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/SequencesHolder.cs index 467f73f32..044d75775 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/SequencesHolder.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/SequencesHolder.cs @@ -72,6 +72,8 @@ internal class SequencesHolder public long EntityStateHistorySequence { get; set; } + public long ValidatorStateHistorySequence { get; set; } + public long EntitySequence { get; set; } public long EntityMetadataHistorySequence { get; set; } @@ -117,4 +119,8 @@ internal class SequencesHolder public long KeyValueStoreEntryHistorySequence { get; set; } public long ValidatorEmissionStatisticsSequence { get; set; } + + public long NonFungibleSchemaHistorySequence { get; set; } + + public long KeyValueSchemaHistorySequence { get; set; } } diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/WriteHelper.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/WriteHelper.cs index a775171e1..c17df1882 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/WriteHelper.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/LedgerExtension/WriteHelper.cs @@ -179,7 +179,7 @@ public async Task CopyLedgerTransaction(ICollection enti return 0; } - await using var writer = await _connection.BeginBinaryImportAsync("COPY ledger_transactions (state_version, message, epoch, round_in_epoch, index_in_epoch, index_in_round, is_end_of_epoch, fee_paid, tip_paid, affected_global_entities, round_timestamp, created_timestamp, normalized_round_timestamp, raw_payload, receipt_state_updates, receipt_status, receipt_fee_summary, receipt_error_message, receipt_output, receipt_next_epoch, receipt_events, discriminator, payload_hash, intent_hash, signed_intent_hash) FROM STDIN (FORMAT BINARY)", token); + await using var writer = await _connection.BeginBinaryImportAsync("COPY ledger_transactions (state_version, message, epoch, round_in_epoch, index_in_epoch, index_in_round, is_end_of_epoch, fee_paid, tip_paid, affected_global_entities, round_timestamp, created_timestamp, normalized_round_timestamp, raw_payload, receipt_state_updates, receipt_status, receipt_fee_summary, receipt_error_message, receipt_output, receipt_next_epoch, receipt_event_sbors, receipt_event_schema_hashes, receipt_event_type_indexes, receipt_event_sbor_type_kinds, discriminator, payload_hash, intent_hash, signed_intent_hash) FROM STDIN (FORMAT BINARY)", token); foreach (var lt in entities) { @@ -207,7 +207,10 @@ public async Task CopyLedgerTransaction(ICollection enti await writer.WriteAsync(lt.EngineReceipt.ErrorMessage, NpgsqlDbType.Text, token); await writer.WriteAsync(lt.EngineReceipt.Output, NpgsqlDbType.Jsonb, token); await writer.WriteAsync(lt.EngineReceipt.NextEpoch, NpgsqlDbType.Jsonb, token); - await writer.WriteAsync(lt.EngineReceipt.Events, NpgsqlDbType.Jsonb, token); + await writer.WriteAsync(lt.EngineReceipt.EventsSbors, NpgsqlDbType.Array | NpgsqlDbType.Bytea, token); + await writer.WriteAsync(lt.EngineReceipt.EventSchemaHashes, NpgsqlDbType.Array | NpgsqlDbType.Bytea, token); + await writer.WriteAsync(lt.EngineReceipt.EventTypeIndexes, NpgsqlDbType.Array | NpgsqlDbType.Integer, token); + await writer.WriteAsync(lt.EngineReceipt.EventSborTypeKinds, "sbor_type_kind[]", token); await writer.WriteAsync(discriminator, "ledger_transaction_type", token); switch (lt) @@ -421,16 +424,39 @@ public async Task CopyEntityRoleAssignmentsAggregateHistory(List CopyEntityStateHistory(ICollection entities, CancellationToken token) + public async Task CopyValidatorStateHistory(ICollection stateHistory, CancellationToken token) { - if (!entities.Any()) + if (!stateHistory.Any()) + { + return 0; + } + + await using var writer = await _connection.BeginBinaryImportAsync("COPY validator_state_history (id, from_state_version, validator_entity_id, state) FROM STDIN (FORMAT BINARY)", token); + + foreach (var e in stateHistory) + { + await writer.StartRowAsync(token); + await writer.WriteAsync(e.Id, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.FromStateVersion, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.ValidatorEntityId, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.State, NpgsqlDbType.Jsonb, token); + } + + await writer.CompleteAsync(token); + + return stateHistory.Count; + } + + public async Task CopyEntityStateHistory(ICollection entityStateHistory, CancellationToken token) + { + if (!entityStateHistory.Any()) { return 0; } await using var writer = await _connection.BeginBinaryImportAsync("COPY entity_state_history (id, from_state_version, entity_id, state) FROM STDIN (FORMAT BINARY)", token); - foreach (var e in entities) + foreach (var e in entityStateHistory) { await writer.StartRowAsync(token); await writer.WriteAsync(e.Id, NpgsqlDbType.Bigint, token); @@ -441,7 +467,7 @@ public async Task CopyEntityStateHistory(ICollection en await writer.CompleteAsync(token); - return entities.Count; + return entityStateHistory.Count; } public async Task CopyValidatorKeyHistory(ICollection entities, CancellationToken token) @@ -919,6 +945,58 @@ public async Task CopyKeyValueStoreEntryHistory(List CopyNonFungibleDataSchemaHistory(ICollection entities, CancellationToken token) + { + if (!entities.Any()) + { + return 0; + } + + await using var writer = await _connection.BeginBinaryImportAsync("COPY non_fungible_schema_history (id, from_state_version, entity_id, schema, sbor_type_kind, type_index) FROM STDIN (FORMAT BINARY)", token); + + foreach (var e in entities) + { + await writer.StartRowAsync(token); + await writer.WriteAsync(e.Id, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.FromStateVersion, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.EntityId, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.Schema, NpgsqlDbType.Bytea, token); + await writer.WriteAsync(e.SborTypeKind, "sbor_type_kind", token); + await writer.WriteAsync(e.TypeIndex, NpgsqlDbType.Integer, token); + } + + await writer.CompleteAsync(token); + + return entities.Count; + } + + public async Task CopyKeyValueStoreSchemaHistory(ICollection entities, CancellationToken token) + { + if (!entities.Any()) + { + return 0; + } + + await using var writer = await _connection.BeginBinaryImportAsync("COPY key_value_store_schema_history (id, from_state_version, key_value_store_entity_id, schema, key_sbor_type_kind, key_type_index, value_sbor_type_kind, value_type_index) FROM STDIN (FORMAT BINARY)", token); + + foreach (var e in entities) + { + await writer.StartRowAsync(token); + await writer.WriteAsync(e.Id, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.FromStateVersion, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.KeyValueStoreEntityId, NpgsqlDbType.Bigint, token); + await writer.WriteAsync(e.Schema, NpgsqlDbType.Bytea, token); + await writer.WriteAsync(e.KeySborTypeKind, "sbor_type_kind", token); + await writer.WriteAsync(e.KeyTypeIndex, NpgsqlDbType.Integer, token); + await writer.WriteAsync(e.ValueSborTypeKind, "sbor_type_kind", token); + await writer.WriteAsync(e.ValueTypeIndex, NpgsqlDbType.Integer, token); + } + + await writer.CompleteAsync(token); + + return entities.Count; + } + public async Task UpdateSequences(SequencesHolder sequences, CancellationToken token) { var cd = new CommandDefinition( @@ -927,6 +1005,7 @@ public async Task UpdateSequences(SequencesHolder sequences, CancellationToken t setval('account_default_deposit_rule_history_id_seq', @accountDefaultDepositRuleHistorySequence), setval('account_resource_deposit_rule_history_id_seq', @accountResourceDepositRuleHistorySequence), setval('entity_state_history_id_seq', @entityStateHistorySequence), + setval('validator_state_history_id_seq', @validatorStateHistorySequence), setval('entities_id_seq', @entitySequence), setval('entity_metadata_history_id_seq', @entityMetadataHistorySequence), setval('entity_metadata_aggregate_history_id_seq', @entityMetadataAggregateHistorySequence), @@ -949,12 +1028,15 @@ public async Task UpdateSequences(SequencesHolder sequences, CancellationToken t setval('package_code_history_id_seq', @packageCodeHistorySequence), setval('package_schema_history_id_seq', @packageSchemaHistorySequence), setval('key_value_store_entry_history_id_seq', @keyValueStoreEntryHistorySequence), - setval('validator_emission_statistics_id_seq', @validatorEmissionStatisticsSequence)", + setval('validator_emission_statistics_id_seq', @validatorEmissionStatisticsSequence), + setval('non_fungible_schema_history_id_seq', @NonFungibleSchemaHistorySequence), + setval('key_value_store_schema_history_id_seq', @KeyValueSchemaHistorySequence)", parameters: new { accountDefaultDepositRuleHistorySequence = sequences.AccountDefaultDepositRuleHistorySequence, accountResourceDepositRuleHistorySequence = sequences.AccountResourceDepositRuleHistorySequence, entityStateHistorySequence = sequences.EntityStateHistorySequence, + validatorStateHistorySequence = sequences.ValidatorStateHistorySequence, entitySequence = sequences.EntitySequence, entityMetadataHistorySequence = sequences.EntityMetadataHistorySequence, entityMetadataAggregateHistorySequence = sequences.EntityMetadataAggregateHistorySequence, @@ -978,6 +1060,8 @@ public async Task UpdateSequences(SequencesHolder sequences, CancellationToken t packageSchemaHistorySequence = sequences.PackageSchemaHistorySequence, keyValueStoreEntryHistorySequence = sequences.KeyValueStoreEntryHistorySequence, validatorEmissionStatisticsSequence = sequences.ValidatorEmissionStatisticsSequence, + nonFungibleSchemaHistorySequence = sequences.NonFungibleSchemaHistorySequence, + keyValueSchemaHistorySequence = sequences.KeyValueSchemaHistorySequence, }, cancellationToken: token); diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230724103308_InitialCreate.Designer.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230731142208_InitialCreate.Designer.cs similarity index 93% rename from src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230724103308_InitialCreate.Designer.cs rename to src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230731142208_InitialCreate.Designer.cs index 783305619..1201c8f19 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230724103308_InitialCreate.Designer.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230731142208_InitialCreate.Designer.cs @@ -62,7 +62,7 @@ * permissions under this License. */ -// +// using System; using System.Collections.Generic; using System.Numerics; @@ -81,7 +81,7 @@ namespace RadixDlt.NetworkGateway.PostgresIntegration.Migrations { [DbContext(typeof(MigrationsDbContext))] - [Migration("20230724103308_InitialCreate")] + [Migration("20230731142208_InitialCreate")] partial class InitialCreate { /// @@ -107,6 +107,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "pending_transaction_status", new[] { "submitted_or_known_in_node_mempool", "missing", "rejected_temporarily", "rejected_permanently", "committed_success", "committed_failure" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public_key_type", new[] { "ecdsa_secp256k1", "eddsa_ed25519" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "resource_type", new[] { "fungible", "non_fungible" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "sbor_type_kind", new[] { "well_known", "schema_local" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.AccountDefaultDepositRuleHistory", b => @@ -674,6 +675,51 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("key_value_store_entry_history"); }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.KeyValueStoreSchemaHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FromStateVersion") + .HasColumnType("bigint") + .HasColumnName("from_state_version"); + + b.Property("KeySborTypeKind") + .HasColumnType("sbor_type_kind") + .HasColumnName("key_sbor_type_kind"); + + b.Property("KeyTypeIndex") + .HasColumnType("integer") + .HasColumnName("key_type_index"); + + b.Property("KeyValueStoreEntityId") + .HasColumnType("bigint") + .HasColumnName("key_value_store_entity_id"); + + b.Property("Schema") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("schema"); + + b.Property("ValueSborTypeKind") + .HasColumnType("sbor_type_kind") + .HasColumnName("value_sbor_type_kind"); + + b.Property("ValueTypeIndex") + .HasColumnType("integer") + .HasColumnName("value_type_index"); + + b.HasKey("Id"); + + b.HasIndex("KeyValueStoreEntityId", "FromStateVersion"); + + b.ToTable("key_value_store_schema_history"); + }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.LedgerTransaction", b => { b.Property("StateVersion") @@ -919,6 +965,43 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("non_fungible_id_store_history"); }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.NonFungibleSchemaHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EntityId") + .HasColumnType("bigint") + .HasColumnName("entity_id"); + + b.Property("FromStateVersion") + .HasColumnType("bigint") + .HasColumnName("from_state_version"); + + b.Property("SborTypeKind") + .HasColumnType("sbor_type_kind") + .HasColumnName("sbor_type_kind"); + + b.Property("Schema") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("schema"); + + b.Property("TypeIndex") + .HasColumnType("integer") + .HasColumnName("type_index"); + + b.HasKey("Id"); + + b.HasIndex("EntityId", "FromStateVersion"); + + b.ToTable("non_fungible_schema_history"); + }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.PackageBlueprintHistory", b => { b.Property("Id") @@ -1043,6 +1126,8 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.HasIndex("PackageEntityId", "FromStateVersion"); + b.HasIndex("SchemaHash", "FromStateVersion"); + b.ToTable("package_schema_history"); }); @@ -1277,6 +1362,35 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) b.ToTable("validator_public_key_history"); }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.ValidatorStateHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FromStateVersion") + .HasColumnType("bigint") + .HasColumnName("from_state_version"); + + b.Property("State") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("state"); + + b.Property("ValidatorEntityId") + .HasColumnType("bigint") + .HasColumnName("validator_entity_id"); + + b.HasKey("Id"); + + b.HasIndex("ValidatorEntityId", "FromStateVersion"); + + b.ToTable("validator_state_history"); + }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.GlobalAccessControllerEntity", b => { b.HasBaseType("RadixDlt.NetworkGateway.PostgresIntegration.Models.Entity"); @@ -1869,9 +1983,25 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("receipt_error_message"); - b1.Property("Events") - .HasColumnType("jsonb") - .HasColumnName("receipt_events"); + b1.Property("EventSborTypeKinds") + .IsRequired() + .HasColumnType("sbor_type_kind[]") + .HasColumnName("receipt_event_sbor_type_kinds"); + + b1.Property("EventSchemaHashes") + .IsRequired() + .HasColumnType("bytea[]") + .HasColumnName("receipt_event_schema_hashes"); + + b1.Property("EventTypeIndexes") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("receipt_event_type_indexes"); + + b1.Property("EventsSbors") + .IsRequired() + .HasColumnType("bytea[]") + .HasColumnName("receipt_event_sbors"); b1.Property("FeeSummary") .IsRequired() diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230724103308_InitialCreate.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230731142208_InitialCreate.cs similarity index 91% rename from src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230724103308_InitialCreate.cs rename to src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230731142208_InitialCreate.cs index 91b7a23e5..fbe73f6ba 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230724103308_InitialCreate.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/20230731142208_InitialCreate.cs @@ -62,7 +62,7 @@ * permissions under this License. */ -using System; +using System; using System.Collections.Generic; using System.Numerics; using Microsoft.EntityFrameworkCore.Migrations; @@ -96,7 +96,8 @@ protected override void Up(MigrationBuilder migrationBuilder) .Annotation("Npgsql:Enum:package_vm_type", "native,scrypto_v1") .Annotation("Npgsql:Enum:pending_transaction_status", "submitted_or_known_in_node_mempool,missing,rejected_temporarily,rejected_permanently,committed_success,committed_failure") .Annotation("Npgsql:Enum:public_key_type", "ecdsa_secp256k1,eddsa_ed25519") - .Annotation("Npgsql:Enum:resource_type", "fungible,non_fungible"); + .Annotation("Npgsql:Enum:resource_type", "fungible,non_fungible") + .Annotation("Npgsql:Enum:sbor_type_kind", "well_known,schema_local"); migrationBuilder.CreateTable( name: "account_default_deposit_rule_history", @@ -368,6 +369,25 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_key_value_store_entry_history", x => x.id); }); + migrationBuilder.CreateTable( + name: "key_value_store_schema_history", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + from_state_version = table.Column(type: "bigint", nullable: false), + key_value_store_entity_id = table.Column(type: "bigint", nullable: false), + schema = table.Column(type: "bytea", nullable: false), + key_sbor_type_kind = table.Column(type: "sbor_type_kind", nullable: false), + key_type_index = table.Column(type: "integer", nullable: false), + value_sbor_type_kind = table.Column(type: "sbor_type_kind", nullable: false), + value_type_index = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_key_value_store_schema_history", x => x.id); + }); + migrationBuilder.CreateTable( name: "ledger_transaction_markers", columns: table => new @@ -412,7 +432,10 @@ protected override void Up(MigrationBuilder migrationBuilder) receipt_next_epoch = table.Column(type: "jsonb", nullable: true), receipt_output = table.Column(type: "jsonb", nullable: true), receipt_error_message = table.Column(type: "text", nullable: true), - receipt_events = table.Column(type: "jsonb", nullable: true), + receipt_event_sbors = table.Column(type: "bytea[]", nullable: false), + receipt_event_schema_hashes = table.Column(type: "bytea[]", nullable: false), + receipt_event_type_indexes = table.Column(type: "integer[]", nullable: false), + receipt_event_sbor_type_kinds = table.Column(type: "sbor_type_kind[]", nullable: false), discriminator = table.Column(type: "ledger_transaction_type", nullable: false), payload_hash = table.Column(type: "bytea", nullable: true), intent_hash = table.Column(type: "bytea", nullable: true), @@ -488,6 +511,23 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_non_fungible_id_store_history", x => x.id); }); + migrationBuilder.CreateTable( + name: "non_fungible_schema_history", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + from_state_version = table.Column(type: "bigint", nullable: false), + entity_id = table.Column(type: "bigint", nullable: false), + schema = table.Column(type: "bytea", nullable: false), + sbor_type_kind = table.Column(type: "sbor_type_kind", nullable: false), + type_index = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_non_fungible_schema_history", x => x.id); + }); + migrationBuilder.CreateTable( name: "package_blueprint_history", columns: table => new @@ -619,6 +659,21 @@ protected override void Up(MigrationBuilder migrationBuilder) table.PrimaryKey("PK_validator_public_key_history", x => x.id); }); + migrationBuilder.CreateTable( + name: "validator_state_history", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + from_state_version = table.Column(type: "bigint", nullable: false), + validator_entity_id = table.Column(type: "bigint", nullable: false), + state = table.Column(type: "jsonb", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_validator_state_history", x => x.id); + }); + migrationBuilder.CreateTable( name: "validator_active_set_history", columns: table => new @@ -726,6 +781,11 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "key_value_store_entry_history", columns: new[] { "key_value_store_entity_id", "key", "from_state_version" }); + migrationBuilder.CreateIndex( + name: "IX_key_value_store_schema_history_key_value_store_entity_id_fr~", + table: "key_value_store_schema_history", + columns: new[] { "key_value_store_entity_id", "from_state_version" }); + migrationBuilder.CreateIndex( name: "IX_ledger_transaction_markers_entity_id_state_version", table: "ledger_transaction_markers", @@ -790,6 +850,11 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "non_fungible_id_store_history", columns: new[] { "non_fungible_resource_entity_id", "from_state_version" }); + migrationBuilder.CreateIndex( + name: "IX_non_fungible_schema_history_entity_id_from_state_version", + table: "non_fungible_schema_history", + columns: new[] { "entity_id", "from_state_version" }); + migrationBuilder.CreateIndex( name: "IX_package_blueprint_history_package_entity_id_from_state_vers~", table: "package_blueprint_history", @@ -805,6 +870,11 @@ protected override void Up(MigrationBuilder migrationBuilder) table: "package_schema_history", columns: new[] { "package_entity_id", "from_state_version" }); + migrationBuilder.CreateIndex( + name: "IX_package_schema_history_schema_hash_from_state_version", + table: "package_schema_history", + columns: new[] { "schema_hash", "from_state_version" }); + migrationBuilder.CreateIndex( name: "IX_pending_transactions_intent_hash", table: "pending_transactions", @@ -850,6 +920,11 @@ protected override void Up(MigrationBuilder migrationBuilder) name: "IX_validator_public_key_history_validator_entity_id_key_type_k~", table: "validator_public_key_history", columns: new[] { "validator_entity_id", "key_type", "key" }); + + migrationBuilder.CreateIndex( + name: "IX_validator_state_history_validator_entity_id_from_state_vers~", + table: "validator_state_history", + columns: new[] { "validator_entity_id", "from_state_version" }); } /// @@ -900,6 +975,9 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "key_value_store_entry_history"); + migrationBuilder.DropTable( + name: "key_value_store_schema_history"); + migrationBuilder.DropTable( name: "ledger_transaction_markers"); @@ -918,6 +996,9 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "non_fungible_id_store_history"); + migrationBuilder.DropTable( + name: "non_fungible_schema_history"); + migrationBuilder.DropTable( name: "package_blueprint_history"); @@ -939,6 +1020,9 @@ protected override void Down(MigrationBuilder migrationBuilder) migrationBuilder.DropTable( name: "validator_emission_statistics"); + migrationBuilder.DropTable( + name: "validator_state_history"); + migrationBuilder.DropTable( name: "validator_public_key_history"); } diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/MigrationsDbContextModelSnapshot.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/MigrationsDbContextModelSnapshot.cs index 81adb1026..9a133fe83 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/MigrationsDbContextModelSnapshot.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Migrations/MigrationsDbContextModelSnapshot.cs @@ -62,7 +62,7 @@ * permissions under this License. */ -// +// using System; using System.Collections.Generic; using System.Numerics; @@ -104,6 +104,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "pending_transaction_status", new[] { "submitted_or_known_in_node_mempool", "missing", "rejected_temporarily", "rejected_permanently", "committed_success", "committed_failure" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "public_key_type", new[] { "ecdsa_secp256k1", "eddsa_ed25519" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "resource_type", new[] { "fungible", "non_fungible" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "sbor_type_kind", new[] { "well_known", "schema_local" }); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.AccountDefaultDepositRuleHistory", b => @@ -671,6 +672,51 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("key_value_store_entry_history"); }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.KeyValueStoreSchemaHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FromStateVersion") + .HasColumnType("bigint") + .HasColumnName("from_state_version"); + + b.Property("KeySborTypeKind") + .HasColumnType("sbor_type_kind") + .HasColumnName("key_sbor_type_kind"); + + b.Property("KeyTypeIndex") + .HasColumnType("integer") + .HasColumnName("key_type_index"); + + b.Property("KeyValueStoreEntityId") + .HasColumnType("bigint") + .HasColumnName("key_value_store_entity_id"); + + b.Property("Schema") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("schema"); + + b.Property("ValueSborTypeKind") + .HasColumnType("sbor_type_kind") + .HasColumnName("value_sbor_type_kind"); + + b.Property("ValueTypeIndex") + .HasColumnType("integer") + .HasColumnName("value_type_index"); + + b.HasKey("Id"); + + b.HasIndex("KeyValueStoreEntityId", "FromStateVersion"); + + b.ToTable("key_value_store_schema_history"); + }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.LedgerTransaction", b => { b.Property("StateVersion") @@ -916,6 +962,43 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("non_fungible_id_store_history"); }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.NonFungibleSchemaHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("EntityId") + .HasColumnType("bigint") + .HasColumnName("entity_id"); + + b.Property("FromStateVersion") + .HasColumnType("bigint") + .HasColumnName("from_state_version"); + + b.Property("SborTypeKind") + .HasColumnType("sbor_type_kind") + .HasColumnName("sbor_type_kind"); + + b.Property("Schema") + .IsRequired() + .HasColumnType("bytea") + .HasColumnName("schema"); + + b.Property("TypeIndex") + .HasColumnType("integer") + .HasColumnName("type_index"); + + b.HasKey("Id"); + + b.HasIndex("EntityId", "FromStateVersion"); + + b.ToTable("non_fungible_schema_history"); + }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.PackageBlueprintHistory", b => { b.Property("Id") @@ -1040,6 +1123,8 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasIndex("PackageEntityId", "FromStateVersion"); + b.HasIndex("SchemaHash", "FromStateVersion"); + b.ToTable("package_schema_history"); }); @@ -1274,6 +1359,35 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("validator_public_key_history"); }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.ValidatorStateHistory", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("FromStateVersion") + .HasColumnType("bigint") + .HasColumnName("from_state_version"); + + b.Property("State") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("state"); + + b.Property("ValidatorEntityId") + .HasColumnType("bigint") + .HasColumnName("validator_entity_id"); + + b.HasKey("Id"); + + b.HasIndex("ValidatorEntityId", "FromStateVersion"); + + b.ToTable("validator_state_history"); + }); + modelBuilder.Entity("RadixDlt.NetworkGateway.PostgresIntegration.Models.GlobalAccessControllerEntity", b => { b.HasBaseType("RadixDlt.NetworkGateway.PostgresIntegration.Models.Entity"); @@ -1866,9 +1980,25 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("text") .HasColumnName("receipt_error_message"); - b1.Property("Events") - .HasColumnType("jsonb") - .HasColumnName("receipt_events"); + b1.Property("EventSborTypeKinds") + .IsRequired() + .HasColumnType("sbor_type_kind[]") + .HasColumnName("receipt_event_sbor_type_kinds"); + + b1.Property("EventSchemaHashes") + .IsRequired() + .HasColumnType("bytea[]") + .HasColumnName("receipt_event_schema_hashes"); + + b1.Property("EventTypeIndexes") + .IsRequired() + .HasColumnType("integer[]") + .HasColumnName("receipt_event_type_indexes"); + + b1.Property("EventsSbors") + .IsRequired() + .HasColumnType("bytea[]") + .HasColumnName("receipt_event_sbors"); b1.Property("FeeSummary") .IsRequired() diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/KeyVaulueStoreSchemaHistory.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/KeyVaulueStoreSchemaHistory.cs new file mode 100644 index 000000000..414248b44 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/KeyVaulueStoreSchemaHistory.cs @@ -0,0 +1,98 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +using RadixDlt.NetworkGateway.Abstractions.Model; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RadixDlt.NetworkGateway.PostgresIntegration.Models; + +[Table("key_value_store_schema_history")] +internal class KeyValueStoreSchemaHistory +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("from_state_version")] + public long FromStateVersion { get; set; } + + [Column("key_value_store_entity_id")] + public long KeyValueStoreEntityId { get; set; } + + [Column("schema")] + public byte[] Schema { get; set; } + + [Column("key_sbor_type_kind")] + public SborTypeKind KeySborTypeKind { get; set; } + + [Column("key_type_index")] + public int KeyTypeIndex { get; set; } + + [Column("value_sbor_type_kind")] + public SborTypeKind ValueSborTypeKind { get; set; } + + [Column("value_type_index")] + public int ValueTypeIndex { get; set; } +} diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/LedgerTransaction.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/LedgerTransaction.cs index 2cb08f3d0..e8d8c21b9 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/LedgerTransaction.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/LedgerTransaction.cs @@ -163,8 +163,17 @@ internal class TransactionReceipt [Column("receipt_error_message")] public string? ErrorMessage { get; set; } - [Column("receipt_events", TypeName = "jsonb")] - public string? Events { get; set; } + [Column("receipt_event_sbors")] + public byte[][] EventsSbors { get; set; } + + [Column("receipt_event_schema_hashes")] + public byte[][] EventSchemaHashes { get; set; } + + [Column("receipt_event_type_indexes")] + public int[] EventTypeIndexes { get; set; } + + [Column("receipt_event_sbor_type_kinds")] + public SborTypeKind[] EventSborTypeKinds { get; set; } } internal class GenesisLedgerTransaction : LedgerTransaction diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/NonFungibleSchemaHistory.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/NonFungibleSchemaHistory.cs new file mode 100644 index 000000000..509bf1afd --- /dev/null +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/NonFungibleSchemaHistory.cs @@ -0,0 +1,92 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +using RadixDlt.NetworkGateway.Abstractions.Model; +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RadixDlt.NetworkGateway.PostgresIntegration.Models; + +[Table("non_fungible_schema_history")] +internal class NonFungibleSchemaHistory +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("from_state_version")] + public long FromStateVersion { get; set; } + + [Column("entity_id")] + public long EntityId { get; set; } + + [Column("schema")] + public byte[] Schema { get; set; } + + [Column("sbor_type_kind")] + public SborTypeKind SborTypeKind { get; set; } + + [Column("type_index")] + public int TypeIndex { get; set; } +} diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/ValidatorStateHistory.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/ValidatorStateHistory.cs new file mode 100644 index 000000000..be531880a --- /dev/null +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Models/ValidatorStateHistory.cs @@ -0,0 +1,85 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace RadixDlt.NetworkGateway.PostgresIntegration.Models; + +[Table("validator_state_history")] +internal class ValidatorStateHistory +{ + [Key] + [Column("id")] + public long Id { get; set; } + + [Column("from_state_version")] + public long FromStateVersion { get; set; } + + [Column("validator_entity_id")] + public long ValidatorEntityId { get; set; } + + [Column("state", TypeName = "jsonb")] + public string State { get; set; } +} diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/ScryptoSborUtils.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/ScryptoSborUtils.cs index 767134e26..56b1c9785 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/ScryptoSborUtils.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/ScryptoSborUtils.cs @@ -63,7 +63,9 @@ */ using Newtonsoft.Json.Linq; +using RadixDlt.NetworkGateway.Abstractions; using RadixDlt.NetworkGateway.Abstractions.Extensions; +using RadixDlt.NetworkGateway.Abstractions.Model; using System; using System.Linq; using GatewayModel = RadixDlt.NetworkGateway.GatewayApiSdk.Model; @@ -80,14 +82,24 @@ public static string GetNonFungibleId(string input) return stringNfid; } - public static GatewayModel.ScryptoSborValue NonFungibleDataToGatewayScryptoSbor(byte[] rawScryptoSbor, byte networkId) + public static string DataToProgrammaticJson(byte[] data, byte[] schemaBytes, SborTypeKind keyTypeKind, int schemaIndex, byte networkId) { - var stringRepresentation = ToolkitModel.RadixEngineToolkitUniffiMethods.SborDecodeToStringRepresentation(rawScryptoSbor.ToList(), ToolkitModel.SerializationMode.PROGRAMMATIC, networkId, null); + ToolkitModel.LocalTypeIndex typeIndex = keyTypeKind switch + { + SborTypeKind.SchemaLocal => new ToolkitModel.LocalTypeIndex.SchemaLocalIndex((ulong)schemaIndex), + SborTypeKind.WellKnown => new ToolkitModel.LocalTypeIndex.WellKnown((byte)schemaIndex), + _ => throw new ArgumentOutOfRangeException(nameof(keyTypeKind), keyTypeKind, null), + }; - return new GatewayModel.ScryptoSborValue( - rawHex: rawScryptoSbor.ToHex(), - rawJson: JObject.Parse(stringRepresentation) - ); + var schema = new ToolkitModel.Schema(typeIndex, schemaBytes.ToList()); + + var stringRepresentation = ToolkitModel.RadixEngineToolkitUniffiMethods.SborDecodeToStringRepresentation( + data.ToList(), + ToolkitModel.SerializationMode.PROGRAMMATIC, + networkId, + schema); + + return stringRepresentation; } public static GatewayModel.MetadataTypedValue DecodeToGatewayMetadataItemValue(byte[] rawScryptoSbor, byte networkId) diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/ServiceCollectionExtensions.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/ServiceCollectionExtensions.cs index 602a5798e..a63fcf7a1 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/ServiceCollectionExtensions.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/ServiceCollectionExtensions.cs @@ -115,6 +115,7 @@ internal static IServiceCollection AddNpgsqlDataSourceHolder(this IServiceCol dataSourceBuilder.MapEnum(); dataSourceBuilder.MapEnum(); dataSourceBuilder.MapEnum(); + dataSourceBuilder.MapEnum(); return new NpgsqlDataSourceHolder(dataSourceBuilder.Build()); }, diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/EntityStateQuerier.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/EntityStateQuerier.cs index 69ff19467..2fd6dfaeb 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/EntityStateQuerier.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/EntityStateQuerier.cs @@ -71,6 +71,7 @@ using RadixDlt.NetworkGateway.Abstractions; using RadixDlt.NetworkGateway.Abstractions.Addressing; using RadixDlt.NetworkGateway.Abstractions.Extensions; +using RadixDlt.NetworkGateway.Abstractions.Model; using RadixDlt.NetworkGateway.Abstractions.Numerics; using RadixDlt.NetworkGateway.GatewayApi.Configuration; using RadixDlt.NetworkGateway.GatewayApi.Exceptions; @@ -79,6 +80,7 @@ using RadixDlt.NetworkGateway.PostgresIntegration.Models; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Threading; @@ -157,6 +159,7 @@ public EntityStateQuerier(INetworkConfigurationProvider networkConfigurationProv var metadata = await GetMetadataSlices(entities.Select(e => e.Id).ToArray(), 0, _endpointConfiguration.Value.DefaultPageSize, ledgerState, token); var accessRulesHistory = await GetAccessRulesHistory(resourceEntities, componentEntities, ledgerState, token); var stateHistory = await GetStateHistory(componentEntities, ledgerState, token); + var resourcesSupplyData = await GetResourcesSupplyData(resourceEntities.Select(x => x.Id).ToArray(), ledgerState, token); var packageBlueprintHistory = await GetPackageBlueprintHistory(packageEntities.Select(e => e.Id).ToArray(), ledgerState, token); var packageCodeHistory = await GetPackageCodeHistory(packageEntities.Select(e => e.Id).ToArray(), ledgerState, token); @@ -271,7 +274,6 @@ public EntityStateQuerier(INetworkConfigurationProvider networkConfigurationProv case ComponentEntity ce: stateHistory.TryGetValue(ce.Id, out var state); accessRulesHistory.TryGetValue(ce.Id, out var accessRules); - var componentRoyaltyVaultBalance = royaltyVaultsBalance?.SingleOrDefault(x => x.OwnerEntityId == ce.Id)?.Balance; details = new GatewayModel.StateEntityDetailsResponseComponentDetails( @@ -562,6 +564,16 @@ FROM most_recent_non_fungible_id_store_history_slice hs { var entity = await GetEntity(resourceAddress, ledgerState, token); + var nonFungibleDataSchema = await _dbContext.NonFungibleDataSchemaHistory + .Where(x => x.EntityId == entity.Id && x.FromStateVersion <= ledgerState.StateVersion) + .OrderByDescending(x => x.FromStateVersion) + .FirstOrDefaultAsync(token); + + if (nonFungibleDataSchema == null) + { + throw new UnreachableException("No schema found for nonfungible resource: {resourceAddress}"); + } + var cd = new CommandDefinition( commandText: @" SELECT nfid.non_fungible_id AS NonFungibleId, md.is_deleted AS IsDeleted, md.data AS Data, md.from_state_version AS DataLastUpdatedAtStateVersion @@ -593,9 +605,12 @@ ORDER BY nfid.from_state_version DESC continue; } + var programmaticJson = ScryptoSborUtils.DataToProgrammaticJson(vm.Data, nonFungibleDataSchema.Schema, + nonFungibleDataSchema.SborTypeKind, nonFungibleDataSchema.TypeIndex, _networkConfigurationProvider.GetNetworkId()); + items.Add(new GatewayModel.StateNonFungibleDetailsResponseItem( nonFungibleId: vm.NonFungibleId, - data: ScryptoSborUtils.NonFungibleDataToGatewayScryptoSbor(vm.Data, _networkConfigurationProvider.GetNetworkId()), + data: new GatewayModel.ScryptoSborValue(vm.Data.ToHex(), new JRaw(programmaticJson)), lastUpdatedAtStateVersion: vm.DataLastUpdatedAtStateVersion)); } @@ -675,8 +690,8 @@ LIMIT 1 ) evh ON TRUE INNER JOIN LATERAL ( SELECT state, from_state_version - FROM entity_state_history - WHERE entity_id = variables.validator_entity_id AND from_state_version <= @stateVersion + FROM validator_state_history + WHERE validator_entity_id = variables.validator_entity_id AND from_state_version <= @stateVersion ORDER BY from_state_version DESC LIMIT 1 ) esh ON true @@ -766,6 +781,16 @@ LIMIT 1 public async Task KeyValueStoreData(EntityAddress keyValueStoreAddress, IList keys, GatewayModel.LedgerState ledgerState, CancellationToken token = default) { var keyValueStore = await GetEntity(keyValueStoreAddress, ledgerState, token); + var keyValueStoreSchema = await _dbContext.KeyValueStoreSchemaHistory + .Where(x => x.KeyValueStoreEntityId == keyValueStore.Id && x.FromStateVersion <= ledgerState.StateVersion) + .OrderByDescending(x => x.FromStateVersion) + .FirstOrDefaultAsync(token); + + if (keyValueStoreSchema == null) + { + throw new UnreachableException($"Missing key value store schema for :{keyValueStoreAddress}"); + } + var dbKeys = keys.Distinct().Select(k => (byte[])k).ToList(); var entries = await _dbContext.KeyValueStoreEntryHistory @@ -793,9 +818,15 @@ LIMIT 1 continue; } + var keyJson = ScryptoSborUtils.DataToProgrammaticJson(e.Key, keyValueStoreSchema.Schema, keyValueStoreSchema.KeySborTypeKind, + keyValueStoreSchema.KeyTypeIndex, _networkConfigurationProvider.GetNetworkId()); + + var valueJson = ScryptoSborUtils.DataToProgrammaticJson(e.Value, keyValueStoreSchema.Schema, keyValueStoreSchema.ValueSborTypeKind, + keyValueStoreSchema.ValueTypeIndex, _networkConfigurationProvider.GetNetworkId()); + items.Add(new GatewayModel.StateKeyValueStoreDataResponseItem( - keyHex: e.Key.ToHex(), - valueHex: e.Value.ToHex(), + key: new GatewayModel.ScryptoSborValue(e.Key.ToHex(), new JRaw(keyJson)), + value: new GatewayModel.ScryptoSborValue(e.Value.ToHex(), new JRaw(valueJson)), lastUpdatedAtStateVersion: e.FromStateVersion, isLocked: e.IsLocked)); } @@ -971,7 +1002,7 @@ INNER JOIN LATERAL UNNEST(metadata_slice) WITH ORDINALITY AS metadata_join(id, o var value = ScryptoSborUtils.DecodeToGatewayMetadataItemValue(vm.Value, _networkConfigurationProvider.GetNetworkId()); var stringRepresentation = ToolkitModel.RadixEngineToolkitUniffiMethods.ScryptoSborDecodeToStringRepresentation(vm.Value.ToList(), ToolkitModel.SerializationMode.PROGRAMMATIC, _networkConfigurationProvider.GetNetworkId(), null); - var entityMetadataItemValue = new GatewayModel.EntityMetadataItemValue(vm.Value.ToHex(), JObject.Parse(stringRepresentation), value); + var entityMetadataItemValue = new GatewayModel.EntityMetadataItemValue(vm.Value.ToHex(), new JRaw(stringRepresentation), value); result[vm.EntityId].Items.Add(new GatewayModel.EntityMetadataItem(vm.Key, entityMetadataItemValue, vm.IsLocked, vm.FromStateVersion)); } @@ -1036,7 +1067,7 @@ LIMIT 1 var value = ScryptoSborUtils.DecodeToGatewayMetadataItemValue(mh.Value, _networkConfigurationProvider.GetNetworkId()); var stringRepresentation = ToolkitModel.RadixEngineToolkitUniffiMethods.ScryptoSborDecodeToStringRepresentation(mh.Value.ToList(), ToolkitModel.SerializationMode.PROGRAMMATIC, _networkConfigurationProvider.GetNetworkId(), null); - var entityMetadataItemValue = new GatewayModel.EntityMetadataItemValue(mh.Value.ToHex(), JObject.Parse(stringRepresentation), value); + var entityMetadataItemValue = new GatewayModel.EntityMetadataItemValue(mh.Value.ToHex(), new JRaw(stringRepresentation), value); result[mh.EntityId].Items.Add(new GatewayModel.EntityMetadataItem(mh.Key, entityMetadataItemValue, mh.IsLocked, mh.FromStateVersion)); } diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/TransactionQuerier.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/TransactionQuerier.cs index 84e23b563..8c892b6e0 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/TransactionQuerier.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/TransactionQuerier.cs @@ -69,6 +69,7 @@ using RadixDlt.NetworkGateway.GatewayApi.Services; using RadixDlt.NetworkGateway.PostgresIntegration.Interceptors; using RadixDlt.NetworkGateway.PostgresIntegration.Models; +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -82,11 +83,13 @@ internal class TransactionQuerier : ITransactionQuerier { private readonly ReadOnlyDbContext _dbContext; private readonly ReadWriteDbContext _rwDbContext; + private readonly INetworkConfigurationProvider _networkConfigurationProvider; - public TransactionQuerier(ReadOnlyDbContext dbContext, ReadWriteDbContext rwDbContext) + public TransactionQuerier(ReadOnlyDbContext dbContext, ReadWriteDbContext rwDbContext, INetworkConfigurationProvider networkConfigurationProvider) { _dbContext = dbContext; _rwDbContext = rwDbContext; + _networkConfigurationProvider = networkConfigurationProvider; } public async Task GetTransactionStream(TransactionStreamPageRequest request, GatewayModel.LedgerState atLedgerState, CancellationToken token = default) @@ -322,7 +325,8 @@ public async Task> LookupPendingTransactionsByIn return pendingTransactions.Select(pt => new StatusLookupResult(pt.PayloadHash.ToHex(), pt.Status.ToGatewayModel(), pt.LastFailureReason)).ToArray(); } - private async Task> GetTransactions(List transactionStateVersions, GatewayModel.TransactionCommittedDetailsOptIns optIns, CancellationToken token) + private async Task> GetTransactions( + List transactionStateVersions, GatewayModel.TransactionCommittedDetailsOptIns optIns, CancellationToken token) { var transactions = await _dbContext.LedgerTransactions .Where(ult => transactionStateVersions.Contains(ult.StateVersion)) @@ -330,10 +334,50 @@ public async Task> LookupPendingTransactionsByIn var entityIdToAddressMap = await GetEntityAddresses(transactions.SelectMany(x => x.AffectedGlobalEntities).ToList(), token); - return transactions - .OrderBy(lt => transactionStateVersions.IndexOf(lt.StateVersion)) - .Select(lt => lt.ToGatewayModel(optIns, entityIdToAddressMap)) + var schemaHashes = transactions + .Where(x => x.EngineReceipt.EventSchemaHashes.Any()) + .SelectMany(x => x.EngineReceipt.EventSchemaHashes) .ToList(); + + Dictionary schemas = new Dictionary(); + + if (optIns.ReceiptEvents && schemaHashes.Any()) + { + schemas = await _dbContext.PackageSchemaHistory + .Where(x => schemaHashes.Contains(x.SchemaHash)) + .ToDictionaryAsync(x => (ValueBytes)x.SchemaHash, x => x.Schema, token); + } + + List mappedTransactions = new List(); + var networkId = _networkConfigurationProvider.GetNetworkId(); + + foreach (var transaction in transactions.OrderBy(lt => transactionStateVersions.IndexOf(lt.StateVersion))) + { + if (!optIns.ReceiptEvents || schemaHashes?.Any() == false) + { + mappedTransactions.Add(transaction.ToGatewayModel(optIns, entityIdToAddressMap, null)); + } + else + { + List events = new List(); + + foreach (var @event in transaction.EngineReceipt.GetEvents()) + { + var schemaFound = schemas.TryGetValue(@event.SchemaHash, out var schema); + + if (!schemaFound) + { + throw new UnreachableException($"Unable to find schema for given hash {Convert.ToHexString(@event.SchemaHash)}"); + } + + events.Add(ScryptoSborUtils.DataToProgrammaticJson(@event.Data, schema!, @event.KeyTypeKind, @event.TypeIndex, networkId)); + } + + mappedTransactions.Add(transaction.ToGatewayModel(optIns, entityIdToAddressMap, events)); + } + } + + return mappedTransactions; } private async Task> GetEntityIds(List addresses, CancellationToken token = default) diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/VirtualEntityMetadataProvider.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/VirtualEntityMetadataProvider.cs index e4ef1d820..12a5ea3e1 100644 --- a/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/VirtualEntityMetadataProvider.cs +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/Services/VirtualEntityMetadataProvider.cs @@ -124,8 +124,8 @@ public EntityMetadataCollection GetVirtualEntityMetadata(EntityAddress virtualEn return new EntityMetadataCollection(2, null, null, new List { - new("owner_keys", new EntityMetadataItemValue(ownerKeysRawHex, JObject.Parse(ownerKeysJson), ScryptoSborUtils.ConvertToolkitMetadataToGateway(ownedKeysItem))), - new("owner_badge", new EntityMetadataItemValue(ownerBadgeRawHex, JObject.Parse(ownerBadgeJson), ScryptoSborUtils.ConvertToolkitMetadataToGateway(ownerBadgeItem))), + new("owner_keys", new EntityMetadataItemValue(ownerKeysRawHex, new JRaw(ownerKeysJson), ScryptoSborUtils.ConvertToolkitMetadataToGateway(ownedKeysItem))), + new("owner_badge", new EntityMetadataItemValue(ownerBadgeRawHex, new JRaw(ownerBadgeJson), ScryptoSborUtils.ConvertToolkitMetadataToGateway(ownerBadgeItem))), } ); } diff --git a/src/RadixDlt.NetworkGateway.PostgresIntegration/TransactionReceiptExtensions.cs b/src/RadixDlt.NetworkGateway.PostgresIntegration/TransactionReceiptExtensions.cs new file mode 100644 index 000000000..92ccd7bf7 --- /dev/null +++ b/src/RadixDlt.NetworkGateway.PostgresIntegration/TransactionReceiptExtensions.cs @@ -0,0 +1,91 @@ +/* Copyright 2021 Radix Publishing Ltd incorporated in Jersey (Channel Islands). + * + * Licensed under the Radix License, Version 1.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of the License at: + * + * radixfoundation.org/licenses/LICENSE-v1 + * + * The Licensor hereby grants permission for the Canonical version of the Work to be + * published, distributed and used under or by reference to the Licensor’s trademark + * Radix ® and use of any unregistered trade names, logos or get-up. + * + * The Licensor provides the Work (and each Contributor provides its Contributions) on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, + * including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, + * MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. + * + * Whilst the Work is capable of being deployed, used and adopted (instantiated) to create + * a distributed ledger it is your responsibility to test and validate the code, together + * with all logic and performance of that code under all foreseeable scenarios. + * + * The Licensor does not make or purport to make and hereby excludes liability for all + * and any representation, warranty or undertaking in any form whatsoever, whether express + * or implied, to any entity or person, including any representation, warranty or + * undertaking, as to the functionality security use, value or other characteristics of + * any distributed ledger nor in respect the functioning or value of any tokens which may + * be created stored or transferred using the Work. The Licensor does not warrant that the + * Work or any use of the Work complies with any law or regulation in any territory where + * it may be implemented or used or that it will be appropriate for any specific purpose. + * + * Neither the licensor nor any current or former employees, officers, directors, partners, + * trustees, representatives, agents, advisors, contractors, or volunteers of the Licensor + * shall be liable for any direct or indirect, special, incidental, consequential or other + * losses of any kind, in tort, contract or otherwise (including but not limited to loss + * of revenue, income or profits, or loss of use or data, or loss of reputation, or loss + * of any economic or other opportunity of whatsoever nature or howsoever arising), arising + * out of or in connection with (without limitation of any use, misuse, of any ledger system + * or use made or its functionality or any performance or operation of any code or protocol + * caused by bugs or programming or logic errors or otherwise); + * + * A. any offer, purchase, holding, use, sale, exchange or transmission of any + * cryptographic keys, tokens or assets created, exchanged, stored or arising from any + * interaction with the Work; + * + * B. any failure in a transmission or loss of any token or assets keys or other digital + * artefacts due to errors in transmission; + * + * C. bugs, hacks, logic errors or faults in the Work or any communication; + * + * D. system software or apparatus including but not limited to losses caused by errors + * in holding or transmitting tokens by any third-party; + * + * E. breaches or failure of security including hacker attacks, loss or disclosure of + * password, loss of private key, unauthorised use or misuse of such passwords or keys; + * + * F. any losses including loss of anticipated savings or other benefits resulting from + * use of the Work or any changes to the Work (however implemented). + * + * You are solely responsible for; testing, validating and evaluation of all operation + * logic, functionality, security and appropriateness of using the Work for any commercial + * or non-commercial purpose and for any reproduction or redistribution by You of the + * Work. You assume all risks associated with Your use of the Work and the exercise of + * permissions under this License. + */ + +using RadixDlt.NetworkGateway.Abstractions.Model; +using RadixDlt.NetworkGateway.PostgresIntegration.Models; +using System.Collections.Generic; + +namespace RadixDlt.NetworkGateway.PostgresIntegration; + +internal record TransactionReceiptEventData(byte[] Data, byte[] SchemaHash, int TypeIndex, SborTypeKind KeyTypeKind); + +internal static class TransactionReceiptExtensions +{ + public static List GetEvents(this TransactionReceipt transactionReceipt) + { + var result = new List(); + + for (var i = 0; i < transactionReceipt.EventsSbors.Length; ++i) + { + var eventData = transactionReceipt.EventsSbors[i]; + var schemaHash = transactionReceipt.EventSchemaHashes[i]; + var index = transactionReceipt.EventTypeIndexes[i]; + var typeKind = transactionReceipt.EventSborTypeKinds[i]; + + result.Add(new TransactionReceiptEventData(eventData, schemaHash, index, typeKind)); + } + + return result; + } +}