Bind Eve receipts to immutable invocations
This commit is contained in:
parent
351f70245b
commit
4167fc32ca
12 changed files with 65 additions and 13 deletions
|
|
@ -14,6 +14,10 @@ export interface EveCommandReceipt {
|
|||
}[];
|
||||
issuedAtUtc?: string;
|
||||
sourceVersion: number;
|
||||
/**
|
||||
* Canonical digest of the immutable invocation envelope finalized by this receipt.
|
||||
*/
|
||||
invocationHash?: string;
|
||||
navigation?: {
|
||||
verseId: string;
|
||||
authorityRuntimeId?: string;
|
||||
|
|
|
|||
|
|
@ -622,6 +622,11 @@ export declare const eveContractSchemas: {
|
|||
readonly type: "integer";
|
||||
readonly minimum: 0;
|
||||
};
|
||||
readonly invocationHash: {
|
||||
readonly type: "string";
|
||||
readonly minLength: 1;
|
||||
readonly description: "Canonical digest of the immutable invocation envelope finalized by this receipt.";
|
||||
};
|
||||
readonly navigation: {
|
||||
readonly type: "object";
|
||||
readonly required: readonly ["verseId", "surfaceId"];
|
||||
|
|
@ -630,6 +635,10 @@ export declare const eveContractSchemas: {
|
|||
readonly type: "string";
|
||||
readonly minLength: 1;
|
||||
};
|
||||
readonly authorityRuntimeId: {
|
||||
readonly type: "string";
|
||||
readonly minLength: 1;
|
||||
};
|
||||
readonly providerId: {
|
||||
readonly type: "string";
|
||||
};
|
||||
|
|
|
|||
|
|
@ -746,6 +746,11 @@ export const eveContractSchemas = {
|
|||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"invocationHash": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Canonical digest of the immutable invocation envelope finalized by this receipt."
|
||||
},
|
||||
"navigation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
@ -757,6 +762,10 @@ export const eveContractSchemas = {
|
|||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"authorityRuntimeId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"providerId": {
|
||||
"type": "string"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -16,6 +16,10 @@ diagnostics?: {
|
|||
}[]
|
||||
issuedAtUtc?: string
|
||||
sourceVersion: number
|
||||
/**
|
||||
* Canonical digest of the immutable invocation envelope finalized by this receipt.
|
||||
*/
|
||||
invocationHash?: string
|
||||
navigation?: {
|
||||
verseId: string
|
||||
authorityRuntimeId?: string
|
||||
|
|
|
|||
|
|
@ -746,6 +746,11 @@ export const eveContractSchemas = {
|
|||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"invocationHash": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Canonical digest of the immutable invocation envelope finalized by this receipt."
|
||||
},
|
||||
"navigation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ test("accepts canonical provider, surface, command, invocation, and receipt docu
|
|||
providerId: "voidbot.swarm",
|
||||
surfaceId: "voidbot.swarm",
|
||||
sourceVersion: 1,
|
||||
invocationHash: "sha256:command-1",
|
||||
}).state, "reconciled");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Description>Portable Eve surface and provider contracts.</Description>
|
||||
<PackageId>GameCult.Eve.Surface</PackageId>
|
||||
<Version>0.3.3</Version>
|
||||
<Version>0.3.4</Version>
|
||||
<Authors>GameCult</Authors>
|
||||
<PackageLicenseExpression>MPL-2.0</PackageLicenseExpression>
|
||||
<PackageProjectUrl>https://github.com/GameCult/Eve</PackageProjectUrl>
|
||||
|
|
|
|||
|
|
@ -366,8 +366,9 @@ namespace GameCult.Eve.Surface
|
|||
string message,
|
||||
string issuedAtUtc,
|
||||
long sourceVersion,
|
||||
EveSurfaceNavigationTarget? navigation = null)
|
||||
: this(SchemaId, receiptId, commandId, command, state, ownerRepo, authority, providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation)
|
||||
EveSurfaceNavigationTarget? navigation = null,
|
||||
string invocationHash = "")
|
||||
: this(SchemaId, receiptId, commandId, command, state, ownerRepo, authority, providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation, invocationHash)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +386,8 @@ namespace GameCult.Eve.Surface
|
|||
string message,
|
||||
string issuedAtUtc,
|
||||
long sourceVersion,
|
||||
EveSurfaceNavigationTarget? navigation = null)
|
||||
EveSurfaceNavigationTarget? navigation = null,
|
||||
string invocationHash = "")
|
||||
{
|
||||
Schema = string.IsNullOrWhiteSpace(schema) ? SchemaId : schema;
|
||||
ReceiptId = receiptId ?? "";
|
||||
|
|
@ -400,6 +402,7 @@ namespace GameCult.Eve.Surface
|
|||
IssuedAtUtc = issuedAtUtc ?? "";
|
||||
SourceVersion = sourceVersion;
|
||||
Navigation = navigation;
|
||||
InvocationHash = invocationHash ?? "";
|
||||
}
|
||||
|
||||
[Key(0)] public string Schema { get; }
|
||||
|
|
@ -415,6 +418,7 @@ namespace GameCult.Eve.Surface
|
|||
[Key(10)] public string IssuedAtUtc { get; }
|
||||
[Key(11)] public long SourceVersion { get; }
|
||||
[Key(12)] public EveSurfaceNavigationTarget? Navigation { get; }
|
||||
[Key(13)] public string InvocationHash { get; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -424,7 +428,7 @@ namespace GameCult.Eve.Surface
|
|||
public sealed class EveCommandReceiptCompatibilityFormatter :
|
||||
IMessagePackFormatter<EveCommandReceiptDocument?>
|
||||
{
|
||||
private const int LegacyFieldCount = 13;
|
||||
private const int LegacyFieldCount = 14;
|
||||
private const int RequiredAndScalarFieldCount = 12;
|
||||
|
||||
public void Serialize(
|
||||
|
|
@ -434,9 +438,9 @@ namespace GameCult.Eve.Surface
|
|||
{
|
||||
if (value == null) { writer.WriteNil(); return; }
|
||||
|
||||
writer.WriteMapHeader(value.Navigation == null
|
||||
? RequiredAndScalarFieldCount
|
||||
: RequiredAndScalarFieldCount + 1);
|
||||
var optionalFields = (value.Navigation == null ? 0 : 1) +
|
||||
(string.IsNullOrWhiteSpace(value.InvocationHash) ? 0 : 1);
|
||||
writer.WriteMapHeader(RequiredAndScalarFieldCount + optionalFields);
|
||||
Write(ref writer, "schema", value.Schema);
|
||||
Write(ref writer, "receiptId", value.ReceiptId);
|
||||
Write(ref writer, "commandId", value.CommandId);
|
||||
|
|
@ -454,6 +458,8 @@ namespace GameCult.Eve.Surface
|
|||
writer.Write("navigation");
|
||||
WriteNavigation(ref writer, value.Navigation);
|
||||
}
|
||||
if (!string.IsNullOrWhiteSpace(value.InvocationHash))
|
||||
Write(ref writer, "invocationHash", value.InvocationHash);
|
||||
}
|
||||
|
||||
public EveCommandReceiptDocument? Deserialize(
|
||||
|
|
@ -483,10 +489,11 @@ namespace GameCult.Eve.Surface
|
|||
var issuedAtUtc = ReadString(ref reader, fields, 10);
|
||||
var sourceVersion = fields > 11 ? reader.ReadInt64() : 0;
|
||||
var navigation = fields > 12 ? ReadNavigation(ref reader, options) : null;
|
||||
var invocationHash = ReadString(ref reader, fields, 13);
|
||||
for (var index = LegacyFieldCount; index < fields; index++) reader.Skip();
|
||||
return new EveCommandReceiptDocument(
|
||||
schema, receiptId, commandId, command, state, ownerRepo, authority,
|
||||
providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation);
|
||||
providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation, invocationHash);
|
||||
}
|
||||
finally { reader.Depth--; }
|
||||
}
|
||||
|
|
@ -511,6 +518,7 @@ namespace GameCult.Eve.Surface
|
|||
var issuedAtUtc = "";
|
||||
long sourceVersion = 0;
|
||||
EveSurfaceNavigationTarget? navigation = null;
|
||||
var invocationHash = "";
|
||||
var fields = reader.ReadMapHeader();
|
||||
for (var index = 0; index < fields; index++)
|
||||
{
|
||||
|
|
@ -529,13 +537,14 @@ namespace GameCult.Eve.Surface
|
|||
case "issuedAtUtc": issuedAtUtc = ReadString(ref reader); break;
|
||||
case "sourceVersion": sourceVersion = reader.ReadInt64(); break;
|
||||
case "navigation": navigation = ReadNavigation(ref reader, options); break;
|
||||
case "invocationHash": invocationHash = ReadString(ref reader); break;
|
||||
default: reader.Skip(); break;
|
||||
}
|
||||
}
|
||||
|
||||
return new EveCommandReceiptDocument(
|
||||
schema, receiptId, commandId, command, state, ownerRepo, authority,
|
||||
providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation);
|
||||
providerId, surfaceId, message, issuedAtUtc, sourceVersion, navigation, invocationHash);
|
||||
}
|
||||
finally { reader.Depth--; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "org.gamecult.eve.surface",
|
||||
"displayName": "Eve Surface Contract",
|
||||
"version": "0.3.3",
|
||||
"version": "0.3.4",
|
||||
"unity": "2021.3",
|
||||
"description": "Renderer-neutral C# contracts and builders for gamecult.eve.surface.v1 documents.",
|
||||
"dependencies": {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,11 @@
|
|||
"type": "integer",
|
||||
"minimum": 0
|
||||
},
|
||||
"invocationHash": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"description": "Canonical digest of the immutable invocation envelope finalized by this receipt."
|
||||
},
|
||||
"navigation": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
|||
|
|
@ -209,7 +209,8 @@ public sealed class EveSurfaceSerializationCompatibilityTests
|
|||
"aetheria.pilot",
|
||||
"interactive-world",
|
||||
new[] { "cultnet+tcp://odin.gamecult.example:3076" },
|
||||
"commander-daemon"));
|
||||
"commander-daemon"),
|
||||
"sha256:launch-envelope");
|
||||
|
||||
var bytes = MessagePackSerializer.Serialize(document, Options);
|
||||
var reader = new MessagePackReader(bytes);
|
||||
|
|
@ -217,7 +218,7 @@ public sealed class EveSurfaceSerializationCompatibilityTests
|
|||
var restored = MessagePackSerializer.Deserialize<EveCommandReceiptDocument>(bytes, Options);
|
||||
|
||||
Assert.That(reader.NextMessagePackType, Is.EqualTo(MessagePackType.Map));
|
||||
Assert.That(reader.ReadMapHeader(), Is.EqualTo(13));
|
||||
Assert.That(reader.ReadMapHeader(), Is.EqualTo(14));
|
||||
Assert.That(wireJson.RootElement.GetProperty("navigation").ValueKind, Is.EqualTo(JsonValueKind.Object));
|
||||
Assert.That(restored.Navigation, Is.Not.Null);
|
||||
Assert.That(restored.Navigation!.VerseId, Is.EqualTo("gamecult.aetheria"));
|
||||
|
|
@ -225,6 +226,7 @@ public sealed class EveSurfaceSerializationCompatibilityTests
|
|||
Assert.That(restored.Navigation.SurfaceId, Is.EqualTo("aetheria.pilot"));
|
||||
Assert.That(restored.Navigation.SurfaceKind, Is.EqualTo("interactive-world"));
|
||||
Assert.That(restored.Navigation.RendezvousEndpoints, Is.EqualTo(new[] { "cultnet+tcp://odin.gamecult.example:3076" }));
|
||||
Assert.That(restored.InvocationHash, Is.EqualTo("sha256:launch-envelope"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
|
@ -263,6 +265,7 @@ public sealed class EveSurfaceSerializationCompatibilityTests
|
|||
Assert.That(restored.Navigation, Is.Not.Null);
|
||||
Assert.That(restored.Navigation!.SurfaceId, Is.EqualTo("aetheria.pilot"));
|
||||
Assert.That(restored.Navigation.AuthorityRuntimeId, Is.Empty);
|
||||
Assert.That(restored.InvocationHash, Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -289,7 +292,9 @@ public sealed class EveSurfaceSerializationCompatibilityTests
|
|||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(wireJson.RootElement.TryGetProperty("navigation", out _), Is.False);
|
||||
Assert.That(wireJson.RootElement.TryGetProperty("invocationHash", out _), Is.False);
|
||||
Assert.That(restored.Navigation, Is.Null);
|
||||
Assert.That(restored.InvocationHash, Is.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\packages\org.gamecult.eve.surface\GameCult.Eve.Surface.csproj" />
|
||||
<ProjectReference Include="$(CultLibRoot)\src\GameCult.Mesh\GameCult.Mesh.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
Loading…
Reference in a new issue