Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/KeetaNet.Anchor/Crypto/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,18 @@ internal Account(WasmRuntime runtime, int handle)
/// <summary>Decrypt <paramref name="ciphertext"/> with the account's private key.</summary>
public byte[] Decrypt(byte[] ciphertext) => Runtime.AccountDecrypt(Handle, ciphertext);

/// <summary>
/// Derive the <paramref name="kind"/> identifier account this account claims
/// at block <paramref name="previous"/> (its opening block when omitted) and
/// operation <paramref name="index"/>. The caller owns the returned handle.
/// </summary>
public Account GenerateIdentifier(IdentifierKind kind, BlockHash? previous = null, int index = 0)
{
byte[] hash = previous?.ToBytes() ?? Array.Empty<byte>();
int handle = Runtime.GenerateIdentifier(Handle, CoreNames.Of(kind), hash, index);

return new Account(Runtime, handle);
}

private protected override void Release(WasmRuntime runtime, int handle) => runtime.AccountFree(handle);
}
85 changes: 85 additions & 0 deletions src/KeetaNet.Anchor/Crypto/BlockFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,91 @@ public BlockOperation SetRep(Account representative)
return new BlockOperation(_runtime, handle);
}

/// <summary>
/// A <c>RECEIVE</c> operation crediting <paramref name="amount"/> base units
/// of <paramref name="token"/> from <paramref name="from"/>. With
/// <paramref name="exact"/> set the send must match the amount exactly;
/// <paramref name="forward"/> redirects the funds onward.
/// </summary>
public BlockOperation Receive(Account from, BigInteger amount, Account token, bool exact = false, Account? forward = null)
{
string value = amount.ToString(System.Globalization.CultureInfo.InvariantCulture);
int handle = _runtime.OpReceive(from.Handle, value, token.Handle, exact, forward?.Handle ?? 0);

return new BlockOperation(_runtime, handle);
}

/// <summary>
/// A <c>SET_INFO</c> operation publishing the account's name, description,
/// and metadata. <paramref name="defaultPermission"/> is required for
/// identifier accounts.
/// </summary>
public BlockOperation SetInfo(string name, string description, string metadata, Permissions? defaultPermission = null)
{
int handle = _runtime.OpSetInfo(name, description, metadata, defaultPermission?.Handle ?? 0);
return new BlockOperation(_runtime, handle);
}

/// <summary>
/// A <c>MODIFY_PERMISSIONS</c> operation applying <paramref name="permissions"/>
/// to <paramref name="principal"/> with <paramref name="method"/>, optionally
/// scoped to <paramref name="target"/> (the block account when omitted).
/// </summary>
public BlockOperation ModifyPermissions(Account principal, Permissions permissions, AdjustMethod method, Account? target = null)
{
int handle = _runtime.OpModifyPermissions(
principal.Handle, permissions.Handle, CoreNames.Of(method), target?.Handle ?? 0);

return new BlockOperation(_runtime, handle);
}

/// <summary>
/// A <c>TOKEN_ADMIN_SUPPLY</c> operation adjusting the block token's supply
/// by <paramref name="amount"/> using <paramref name="method"/>
/// (<see cref="AdjustMethod.Set"/> is not a valid supply adjustment).
/// </summary>
public BlockOperation TokenAdminSupply(BigInteger amount, AdjustMethod method)
{
string value = amount.ToString(System.Globalization.CultureInfo.InvariantCulture);
int handle = _runtime.OpTokenAdminSupply(value, CoreNames.Of(method));

return new BlockOperation(_runtime, handle);
}

/// <summary>A <c>CREATE_IDENTIFIER</c> operation claiming <paramref name="identifier"/>.</summary>
public BlockOperation CreateIdentifier(Account identifier)
{
int handle = _runtime.OpCreateIdentifier(identifier.Handle);
return new BlockOperation(_runtime, handle);
}

/// <summary>
/// A <c>CREATE_IDENTIFIER</c> operation claiming <paramref name="multisig"/>
/// as a multisig account governed by <paramref name="signers"/> with the
/// given signing <paramref name="quorum"/>.
/// </summary>
public BlockOperation CreateMultisig(Account multisig, IReadOnlyList<Account> signers, int quorum)
{
int handle = _runtime.OpCreateMultisig(multisig.Handle, Handles.Of(signers), quorum);
return new BlockOperation(_runtime, handle);
}

/// <summary>A permission set from base <paramref name="flags"/> and optional external bit <paramref name="externalOffsets"/>.</summary>
public Permissions PermissionsFromFlags(IReadOnlyList<BaseFlag> flags, byte[]? externalOffsets = null)
{
string names = string.Join('\n', flags.Select(CoreNames.Of));
int handle = _runtime.PermissionsFromFlags(names, externalOffsets ?? Array.Empty<byte>());

return new Permissions(_runtime, handle);
}

/// <summary>A permission set decoded from its <c>[base, external]</c> hex bitmaps, the ACL transport form.</summary>
public Permissions PermissionsFromBitmaps(string baseBitmap, string externalBitmap)
{
int handle = _runtime.PermissionsFromBitmaps(baseBitmap, externalBitmap);
return new Permissions(_runtime, handle);
}

/// <summary>Decode a signed block from its transport hex.</summary>
public Block ParseHex(string hex)
{
Expand Down
12 changes: 9 additions & 3 deletions src/KeetaNet.Anchor/Crypto/Blocks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ internal Block(WasmRuntime runtime, int handle)
/// <summary>The block's raw transport bytes, as a vote request carries them.</summary>
public byte[] ToBytes() => Runtime.BlockToBytes(Handle);

/// <summary>The block's transport hex encoding.</summary>
public string ToHex() => Runtime.BlockToHex(Handle);

/// <summary>The block's originating account. The caller owns the returned handle.</summary>
public Account GetAccount() => new(Runtime, Runtime.BlockAccount(Handle));

private protected override void Release(WasmRuntime runtime, int handle) => runtime.BlockFree(handle);
}

Expand All @@ -37,10 +43,10 @@ internal BlockOperation(WasmRuntime runtime, int handle)
}

/// <summary>
/// A representative vote decoded from its transport bytes. Internal: votes
/// only ever pass through the transmit flow.
/// A representative vote decoded from its transport bytes. Produced by the
/// transmit flow and the vote reads on <see cref="KeetaClient"/>.
/// </summary>
internal sealed class Vote : WasmObject
public sealed class Vote : WasmObject
{
internal Vote(WasmRuntime runtime, int handle)
: base(runtime, handle)
Expand Down
146 changes: 146 additions & 0 deletions src/KeetaNet.Anchor/Crypto/Permissions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
namespace KeetaNet.Anchor.Crypto;

/// <summary>How a permission or supply adjustment is applied.</summary>
public enum AdjustMethod
{
/// <summary>Grant on top of the existing set, or mint supply.</summary>
Add,
/// <summary>Revoke from the existing set, or burn supply.</summary>
Subtract,
/// <summary>Replace the existing set outright.</summary>
Set,
}

/// <summary>The kind of identifier account an account can derive.</summary>
public enum IdentifierKind
{
/// <summary>A network identifier.</summary>
Network,
/// <summary>A token identifier.</summary>
Token,
/// <summary>A storage identifier.</summary>
Storage,
}

/// <summary>
/// A named base permission bit, matching the reference ledger's offsets.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage(
"Naming",
"CA1711:Identifiers should not have incorrect suffix",
Justification = "BaseFlag is the reference implementations' name for this type")]
public enum BaseFlag
{
/// <summary>Account has access.</summary>
Access = 0,
/// <summary>Account is an owner.</summary>
Owner = 1,
/// <summary>Account is an administrator.</summary>
Admin = 2,
/// <summary>Account can update info.</summary>
UpdateInfo = 3,
/// <summary>Account can send on behalf of the entity.</summary>
SendOnBehalf = 4,
/// <summary>Account can create tokens.</summary>
TokenAdminCreate = 5,
/// <summary>Account can modify token supply.</summary>
TokenAdminSupply = 6,
/// <summary>Account can modify token balances.</summary>
TokenAdminModifyBalance = 7,
/// <summary>Account can create storage accounts.</summary>
StorageCreate = 8,
/// <summary>Storage account can hold the principal token.</summary>
StorageCanHold = 9,
/// <summary>Account can deposit into the storage account.</summary>
StorageDeposit = 10,
/// <summary>Account can delegate permission additions.</summary>
PermissionDelegateAdd = 11,
/// <summary>Account can delegate permission removals.</summary>
PermissionDelegateRemove = 12,
/// <summary>Account can manage certificates.</summary>
ManageCertificate = 13,
/// <summary>Account is a multisig signer.</summary>
MultisigSigner = 14,
}

/// <summary>
/// A ledger permission set: named base flags plus external bit offsets.
/// Created through <see cref="BlockFactory.PermissionsFromFlags"/> or decoded
/// from its <c>[base, external]</c> bitmaps via
/// <see cref="BlockFactory.PermissionsFromBitmaps"/>.
/// </summary>
public sealed class Permissions : WasmObject
{
internal Permissions(WasmRuntime runtime, int handle)
: base(runtime, handle)
{
}

/// <summary>The granted base flags, in ledger offset order.</summary>
public IReadOnlyList<BaseFlag> Flags =>
Lines(Runtime.PermissionsFlags(Handle)).Select(CoreNames.FlagOf).ToArray();

/// <summary>The external permission bit offsets.</summary>
public byte[] ExternalOffsets => Runtime.PermissionsOffsets(Handle);

/// <summary>The <c>[base, external]</c> bitmaps as 0x-prefixed hex, the ACL transport form.</summary>
public IReadOnlyList<string> Bitmaps => Lines(Runtime.PermissionsBitmaps(Handle));

private protected override void Release(WasmRuntime runtime, int handle) => runtime.PermissionsFree(handle);

private static string[] Lines(string joined) =>
joined.Length == 0 ? Array.Empty<string>() : joined.Split('\n');
}

/// <summary>Transport names for the adjust, identifier, and flag enums.</summary>
internal static class CoreNames
{
/// <summary>The base flag wire names, indexed by ledger offset.</summary>
private static readonly string[] FlagNames =
{
"access",
"owner",
"admin",
"update_info",
"send_on_behalf",
"token_admin_create",
"token_admin_supply",
"token_admin_modify_balance",
"storage_create",
"storage_can_hold",
"storage_deposit",
"permission_delegate_add",
"permission_delegate_remove",
"manage_certificate",
"multisig_signer",
};

public static string Of(AdjustMethod method) =>
method switch
{
AdjustMethod.Add => "add",
AdjustMethod.Subtract => "subtract",
_ => "set",
};

public static string Of(IdentifierKind kind) =>
kind switch
{
IdentifierKind.Network => "network",
IdentifierKind.Token => "token",
_ => "storage",
};

public static string Of(BaseFlag flag) => FlagNames[(int)flag];

public static BaseFlag FlagOf(string name)
{
int offset = Array.IndexOf(FlagNames, name);
if (offset < 0)
{
throw new KeetaException("UNKNOWN", $"unknown base permission flag: {name}");
}

return (BaseFlag)offset;
}
}
Loading
Loading