Skip to content

Commit

Permalink
Merge pull request #80 from Concordium/get-block-items
Browse files Browse the repository at this point in the history
Get block items
  • Loading branch information
limemloh authored Jan 25, 2024
2 parents 66cfd00 + 6af2857 commit f9be4df
Show file tree
Hide file tree
Showing 33 changed files with 1,241 additions and 86 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
## Unreleased changes
- Added
- New GRPC-endpoint: `GetBlockItems`
- New transaction `DeployModule`
- The function `Prepare` has been removed from the `AccountTransactionPayload` class, but is implemented for all subclasses except `RawPayload`.
- Added serialization and deserialization for all instances of `AccountTransactionPayload`

- Breaking
- The function `GetTransactionSpecificCost` has been removed from the `AccountTransactionPayload` class.

## 4.2.1
- Bugfix
- Fix wrong build of rust dependencies which made the interops call not work on iOS.

## 4.2.0
- Added
- Deserialization from module schema
- Contract entrypoint messages
- Contract events
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>
50 changes: 50 additions & 0 deletions examples/GetBlockItems/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
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 = await client.GetBlockItems(bi);

Console.WriteLine($"All block items in block {blockItems.BlockHash}: [");
await foreach (var item in blockItems.Response)
{
Console.WriteLine($"{item},");
}
Console.WriteLine("]");
}
}

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">Identifies what block to get the information from.</param>
/// <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 Task<QueryResponse<IAsyncEnumerable<BlockItem>>> GetBlockItems(IBlockHashInput blockHashInput, CancellationToken token = default)
{
var response = this.Raw.GetBlockItems(blockHashInput.Into(), token);
return QueryResponse<IAsyncEnumerable<BlockItem>>.From(response, BlockItem.From, token);
}

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

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

/// <summary>
/// Thrown when deserialization fails but no error message is present. This
/// should, by construction, be impossible.
/// </summary>
public sealed class DeserialNullException : DeserialException
{
internal DeserialNullException() :
base($"Deserialization error: The parsed output is null, but no error was found. This should not be possible.")
{ }
}

11 changes: 11 additions & 0 deletions src/Exceptions/UnexpectedNodeResponseException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Concordium.Sdk.Exceptions;

/// <summary>
/// Thrown if the node sends an invalid response.
/// </summary>
public class UnexpectedNodeResponseException : Exception
{
internal UnexpectedNodeResponseException() :
base($"Unexpected node response received.")
{ }
}
15 changes: 9 additions & 6 deletions src/Helpers/HashCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@ public static class HashCode
{
public static int GetHashCodeByteArray(byte[] array)
{
var hashValue = 31;

foreach (var value in array)
unchecked
{
hashValue = (37 * hashValue) + value.GetHashCode();
}
var hashValue = 31;

return hashValue;
foreach (var value in array)
{
hashValue = (37 * hashValue) + value.GetHashCode();
}

return hashValue;
}
}
}
106 changes: 106 additions & 0 deletions src/Transactions/AccountSignatureMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,110 @@ 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)
{
// The GRPC api states that keys must not exceed 2^8, so this should be safe.
dict.Add(new AccountKeyIndex((byte)s.Key), s.Value.Value.ToByteArray());
}
return Create(dict);
}

/// <summary>Check for equality.</summary>
public bool Equals(AccountSignatureMap? other) => other != null &&
other.GetType().Equals(this.GetType()) &&
this.Signatures.Count == other.Signatures.Count &&
this.Signatures.All(p => p.Value == other.Signatures[p.Key]);

/// <summary>Gets hash code.</summary>
public override int GetHashCode()
{
unchecked
{
var hash = 17;
foreach (var (key, val) in this.Signatures)
{
hash += key.GetHashCode();
hash += Helpers.HashCode.GetHashCodeByteArray(val);
}
return hash;
}
}
}


/// <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
{
/// <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)
{
// The GRPC api states that keys must not exceed 2^8, so this should be safe.
dict.Add(new UpdateKeysIndex((byte)s.Key), s.Value.Value.ToByteArray());
}
return Create(dict);
}

/// <summary>Check for equality.</summary>
public bool Equals(UpdateInstructionSignatureMap? other) => other != null &&
other.GetType().Equals(this.GetType()) &&
this.Signatures.Count == other.Signatures.Count &&
this.Signatures.All(p => p.Value == other.Signatures[p.Key]);

/// <summary>Gets hash code.</summary>
public override int GetHashCode()
{
unchecked
{
var hash = 17;
foreach (var (key, val) in this.Signatures)
{
hash += key.GetHashCode();
hash += Helpers.HashCode.GetHashCodeByteArray(val);
}
return hash;
}
}
}
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, PayloadSize payloadSize) => new(
AccountAddress.From(accountTransactionHeader.Sender),
AccountSequenceNumber.From(accountTransactionHeader.SequenceNumber),
Expiry.From(accountTransactionHeader.Expiry.Value),
EnergyAmount.From(accountTransactionHeader.EnergyAmount),
payloadSize
);
}
Loading

0 comments on commit f9be4df

Please sign in to comment.