Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get block items #80

Merged
merged 25 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c9058fb
Added type for `PendingUpdate`
rasmus-kirk Oct 13, 2023
3c9e39a
Working on adding account transactions
rasmus-kirk Oct 26, 2023
1901ee8
Merge remote-tracking branch 'origin/more-grpc-endpoints' into get-bl…
rasmus-kirk Oct 26, 2023
b7d47a5
Worked on adding credential deployment
rasmus-kirk Oct 26, 2023
cf13b89
Did more stuff
rasmus-kirk Nov 2, 2023
c34ce3b
Implemented serialization+deserialization for `deployModule`
rasmus-kirk Nov 7, 2023
44b5841
Fixed DeployModule equality and cleanup
rasmus-kirk Nov 14, 2023
8860563
Added deserialization of TransferWithMemo
rasmus-kirk Nov 14, 2023
134a4ea
Deserial now works for `TransferWithMemo` and `RegisterData`
rasmus-kirk Nov 21, 2023
a6dda78
Moved `BlockItem` and added RawPayload
rasmus-kirk Nov 21, 2023
7404cb6
Merge branch 'main' into get-block-items
rasmus-kirk Nov 28, 2023
30838ed
Self-review
rasmus-kirk Nov 28, 2023
b659229
Pleased formatter
rasmus-kirk Nov 28, 2023
193d694
Added changelog
rasmus-kirk Nov 28, 2023
55f386a
Merge branch 'main' into get-block-items
rasmus-kirk Nov 28, 2023
6b773df
Fixed example
rasmus-kirk Nov 28, 2023
2e8fd9a
Addressed some comments
rasmus-kirk Dec 5, 2023
6864086
handle "impossible" serialization cases with exception
rasmus-kirk Dec 5, 2023
ed878ad
updated changelog and example
rasmus-kirk Dec 5, 2023
c3f3e6f
Addressed comment: Fixed PayloadSize calculation
rasmus-kirk Dec 12, 2023
f6dd00b
Merge remote-tracking branch 'origin/main' into get-block-items
rasmus-kirk Dec 12, 2023
eefd1fd
Addressed comments
rasmus-kirk Dec 19, 2023
466ce49
Addressed comments
rasmus-kirk Jan 23, 2024
126206f
Made `Helpers.HashCode.GetHashCodeByteArray` unchecked
rasmus-kirk Jan 23, 2024
6af2857
Addressed more comments
rasmus-kirk Jan 23, 2024
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
## Unreleased changes
- Added
- New GRPC-endpoints: `GetBlocks`, `GetFinalizedBlocks`, `GetBranches`, `GetAncestors`, `GetBlockPendingUpdates`
- New GRPC-endpoints: `GetBlocks`, `GetFinalizedBlocks`, `GetBranches`, `GetAncestors`, `GetBlockPendingUpdates`, `GetBlockItems`
- New transaction `DeployModule`
- Added serialization and deserialization for all instances of `AccountTransactionPayload`
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
- Added helpers to get new type `ContractIdentifier` on `ReceiveName` and `ContractName`. This new type only holds the contract name part of `ReceiveName` and `ContractName`.
Also added helper to get entrypoint on `ReceiveName`.

Expand Down
18 changes: 18 additions & 0 deletions examples/GetBlockItems/GetBlockItems.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Concordium.Sdk.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>

</Project>
48 changes: 48 additions & 0 deletions examples/GetBlockItems/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using CommandLine;
using Concordium.Sdk.Client;
using Concordium.Sdk.Types;

// We disable these warnings since CommandLine needs to set properties in options
// but we don't want to give default values.
#pragma warning disable CS8618

namespace GetBlockItems;

internal sealed class GetBlocksOptions
{
[Option(HelpText = "URL representing the endpoint where the gRPC V2 API is served.",
Default = "http://node.testnet.concordium.com:20000/")]
public string Endpoint { get; set; }
[Option(
'b',
"block-hash",
HelpText = "Block hash of the block. Defaults to LastFinal."
)]
public string BlockHash { get; set; }
}

public static class Program
{
/// <summary>
/// Example how to use <see cref="ConcordiumClient.GetBlockItems"/>
/// </summary>
public static async Task Main(string[] args) =>
await Parser.Default
.ParseArguments<GetBlocksOptions>(args)
.WithParsedAsync(Run);

private static async Task Run(GetBlocksOptions o)
{
using var client = new ConcordiumClient(new Uri(o.Endpoint), new ConcordiumClientOptions());

IBlockHashInput bi = o.BlockHash != null ? new Given(BlockHash.From(o.BlockHash)) : new LastFinal();

var blockItems = client.GetBlockItems(bi);

await foreach (var item in blockItems)
{
Console.WriteLine($"Blockitem: {item}");
}
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
}
}

1 change: 0 additions & 1 deletion examples/GetBlockPendingUpdates/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ private static async Task Run(GetBlockPendingUpdatesOptions o)
await foreach (var update in updates.Response)
{
Console.WriteLine($"Pending update: {update}");

}
}
}
17 changes: 17 additions & 0 deletions src/Client/ConcordiumClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using BakerId = Concordium.Sdk.Types.BakerId;
using BlockHash = Concordium.Sdk.Types.BlockHash;
using BlockInfo = Concordium.Sdk.Types.BlockInfo;
using BlockItem = Concordium.Sdk.Transactions.BlockItem;
using BlockItemSummary = Concordium.Sdk.Types.BlockItemSummary;
using Branch = Concordium.Sdk.Types.Branch;
using ConsensusInfo = Concordium.Sdk.Types.ConsensusInfo;
Expand Down Expand Up @@ -742,5 +743,21 @@ await Task.WhenAll(response.ResponseHeadersAsync, response.ResponseAsync)
.ConfigureAwait(false);
}

/// <summary>
/// Get the items of a block.
/// </summary>
/// <param name="blockHashInput">Block hash from where smart contract information will be given.</param>
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
/// <param name="token">Cancellation token</param>
/// <returns>A stream of block items.</returns>
/// <exception cref="RpcException">
/// RPC error occurred, access <see cref="RpcException.StatusCode"/> for more information.
/// <see cref="StatusCode.Unimplemented"/> indicates that this endpoint is disabled in the node.
/// </exception>
public IAsyncEnumerable<BlockItem> GetBlockItems(IBlockHashInput blockHashInput, CancellationToken token = default)
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
{
var response = this.Raw.GetBlockItems(blockHashInput.Into(), token);
return response.ResponseStream.ReadAllAsync(token).Select(BlockItem.From);
}

public void Dispose() => this.Raw.Dispose();
}
12 changes: 12 additions & 0 deletions src/Exceptions/DeserialException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace Concordium.Sdk.Exceptions;

/// <summary>
/// Thrown when deserialization fails and is explicitly meant not to.
/// </summary>
public sealed class DeserialException : Exception
{
internal DeserialException(string errorMessage) :
base($"Deserialization error: {errorMessage}")
{ }
}

71 changes: 71 additions & 0 deletions src/Helpers/Deserialization.cs
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System.Buffers.Binary;

namespace Concordium.Sdk.Helpers;

/// <summary>
/// Helpers for deserializing data.
/// </summary>
public static class Deserial
{
/// <summary>
/// Creates a ushort from a byte array.
/// </summary>
public static bool TryDeserialU16(byte[] input, int offset, out (ushort? Uint, string? Error) output)
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
{
if (input.Length < sizeof(ushort))
{
var msg = $"Invalid length in TryDeserialU32. Must be longer than {sizeof(ushort)}, but was {input.Length}";
output = (null, msg);
return false;
}

var offset_input = input.Skip(offset).ToArray();

var bytes = offset_input.Take(sizeof(ushort)).ToArray();

output = (BinaryPrimitives.ReadUInt16BigEndian(bytes), null);
return true;
}

/// <summary>
/// Creates a uint from a byte array.
/// </summary>
public static bool TryDeserialU32(byte[] input, int offset, out (uint? Uint, string? Error) output)
{
if (input.Length < sizeof(uint))
{
var msg = $"Invalid length in TryDeserialU32. Must be longer than 4, but was {input.Length}";
output = (null, msg);
return false;
}

var offset_input = input.Skip(offset).ToArray();

var bytes = offset_input.Take(sizeof(uint)).ToArray();

output = (BinaryPrimitives.ReadUInt32BigEndian(bytes), null);
return true;
}

/// <summary>
/// Creates a ulong from a byte array.
/// </summary>
public static bool TryDeserialU64(byte[] input, int offset, out (ulong? Ulong, string? Error) output)
{
if (input.Length < sizeof(ulong))
{
var msg = $"Invalid length in TryDeserialU32. Must be longer than {sizeof(ulong)}, but was {input.Length}";
output = (null, msg);
return false;
}

var offset_input = input.Skip(offset).ToArray();

var bytes = offset_input.Take(sizeof(ulong)).ToArray();

output = (BinaryPrimitives.ReadUInt64BigEndian(bytes), null);
return true;
}
}


62 changes: 62 additions & 0 deletions src/Transactions/AccountSignatureMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,66 @@ public Grpc.V2.AccountSignatureMap ToProto()
}
return accountSignatureMap;
}

internal static AccountSignatureMap From(Grpc.V2.AccountSignatureMap map)
{
var dict = new Dictionary<AccountKeyIndex, byte[]>();
foreach (var s in map.Signatures)
{
dict.Add(new AccountKeyIndex((byte)s.Key), s.Value.Value.ToByteArray());
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
}
return Create(dict);
}
}


/// <summary>Index of a key in an authorizations update payload.</summary>
public sealed record UpdateKeysIndex(byte Value);

/// <summary>A map from <see cref="UpdateKeysIndex"/> to signatures.</summary>
public sealed record UpdateInstructionSignatureMap
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>Internal representation of the map.</summary>
public ImmutableDictionary<UpdateKeysIndex, byte[]> Signatures { get; init; }

/// <summary>Initializes a new instance of the <see cref="UpdateInstructionSignatureMap"/> class.</summary>
/// <param name="signatures">A map from update key indices to signatures.</param>
private UpdateInstructionSignatureMap(Dictionary<UpdateKeysIndex, byte[]> signatures) => this.Signatures = signatures.ToImmutableDictionary();

/// <summary>Creates a new instance of the <see cref="UpdateInstructionSignatureMap"/> class.</summary>
/// <param name="signatures">A map from update key indices to signatures.</param>
/// <exception cref="ArgumentException">A signature is not 64 bytes.</exception>
public static UpdateInstructionSignatureMap Create(Dictionary<UpdateKeysIndex, byte[]> signatures)
{
// Signatures are 64-byte ed25519 signatures and therefore 64 bytes.
if (signatures.Values.Any(signature => signature.Length != 64))
{
throw new ArgumentException($"Signature should be {64} bytes.");
}
return new UpdateInstructionSignatureMap(signatures);
}

/// <summary>Converts the update signature map to its corresponding protocol buffer message instance.</summary>
public Grpc.V2.SignatureMap ToProto()
{
var signatureMap = new Grpc.V2.SignatureMap();
foreach (var s in this.Signatures)
{
signatureMap.Signatures.Add(
s.Key.Value,
new Grpc.V2.Signature() { Value = Google.Protobuf.ByteString.CopyFrom(s.Value) }
);
}
return signatureMap;
}

internal static UpdateInstructionSignatureMap From(Grpc.V2.SignatureMap map)
{
var dict = new Dictionary<UpdateKeysIndex, byte[]>();
foreach (var s in map.Signatures)
{
dict.Add(new UpdateKeysIndex((byte)s.Key), s.Value.Value.ToByteArray());
}
return Create(dict);
}
}
11 changes: 11 additions & 0 deletions src/Transactions/AccountTransactionHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,15 @@ public Grpc.V2.AccountTransactionHeader ToProto() =>
Expiry = this.Expiry.ToProto(),
EnergyAmount = new Grpc.V2.Energy() { Value = this.MaxEnergyCost.Value }
};

/// <summary>
/// Creates an account transaction header from its corresponding protocol buffer message instance.
/// </summary>
internal static AccountTransactionHeader From(Grpc.V2.AccountTransactionHeader accountTransactionHeader) => new(
AccountAddress.From(accountTransactionHeader.Sender),
AccountSequenceNumber.From(accountTransactionHeader.SequenceNumber),
Expiry.From(accountTransactionHeader.Expiry.Value),
EnergyAmount.From(accountTransactionHeader.EnergyAmount),
new PayloadSize((uint)accountTransactionHeader.CalculateSize())
rasmus-kirk marked this conversation as resolved.
Show resolved Hide resolved
);
}
Loading
Loading