Initial import of the CBDDC codebase with docs and tests. Add a .NET-focused gitignore to keep generated artifacts out of source control.
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
Joseph Doherty
2026-02-20 13:03:21 -05:00
commit 08bfc17218
218 changed files with 33910 additions and 0 deletions

View File

@@ -0,0 +1,107 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.Text.Json;
namespace ZB.MOM.WW.CBDDC.Core;
public enum OperationType
{
Put,
Delete
}
public static class OplogEntryExtensions
{
/// <summary>
/// Computes a deterministic hash for the specified oplog entry.
/// </summary>
/// <param name="entry">The oplog entry to hash.</param>
/// <returns>The lowercase hexadecimal SHA-256 hash of the entry.</returns>
public static string ComputeHash(this OplogEntry entry)
{
using var sha256 = System.Security.Cryptography.SHA256.Create();
var sb = new System.Text.StringBuilder();
sb.Append(entry.Collection);
sb.Append('|');
sb.Append(entry.Key);
sb.Append('|');
// Ensure stable string representation for Enum (integer value)
sb.Append(((int)entry.Operation).ToString(System.Globalization.CultureInfo.InvariantCulture));
sb.Append('|');
// Payload excluded from hash to avoid serialization non-determinism
// sb.Append(entry.Payload...);
sb.Append('|');
// Timestamp.ToString() is now Invariant
sb.Append(entry.Timestamp.ToString());
sb.Append('|');
sb.Append(entry.PreviousHash);
var bytes = System.Text.Encoding.UTF8.GetBytes(sb.ToString());
var hashBytes = sha256.ComputeHash(bytes);
// Convert to hex string
return BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant();
}
}
public class OplogEntry
{
/// <summary>
/// Gets the collection name associated with this entry.
/// </summary>
public string Collection { get; }
/// <summary>
/// Gets the document key associated with this entry.
/// </summary>
public string Key { get; }
/// <summary>
/// Gets the operation represented by this entry.
/// </summary>
public OperationType Operation { get; }
/// <summary>
/// Gets the serialized payload for the operation.
/// </summary>
public JsonElement? Payload { get; }
/// <summary>
/// Gets the logical timestamp for this entry.
/// </summary>
public HlcTimestamp Timestamp { get; }
/// <summary>
/// Gets the hash of this entry.
/// </summary>
public string Hash { get; }
/// <summary>
/// Gets the hash of the previous entry in the chain.
/// </summary>
public string PreviousHash { get; }
/// <summary>
/// Initializes a new instance of the <see cref="OplogEntry"/> class.
/// </summary>
/// <param name="collection">The collection name.</param>
/// <param name="key">The document key.</param>
/// <param name="operation">The operation type.</param>
/// <param name="payload">The serialized payload.</param>
/// <param name="timestamp">The logical timestamp.</param>
/// <param name="previousHash">The previous entry hash.</param>
/// <param name="hash">The current entry hash. If null, it is computed.</param>
public OplogEntry(string collection, string key, OperationType operation, JsonElement? payload, HlcTimestamp timestamp, string previousHash, string? hash = null)
{
Collection = collection;
Key = key;
Operation = operation;
Payload = payload;
Timestamp = timestamp;
PreviousHash = previousHash ?? string.Empty;
Hash = hash ?? this.ComputeHash();
}
/// <summary>
/// Verifies if the stored Hash matches the content.
/// </summary>
public bool IsValid()
{
return Hash == this.ComputeHash();
}
}