Skip to content

Commit

Permalink
Merge pull request #55 from Concordium/fix-grpc-account-conversion
Browse files Browse the repository at this point in the history
Fix issue when converting from gRPC Address
  • Loading branch information
Søren Schwartz authored Jul 24, 2023
2 parents 4fe0be2 + 58d1543 commit b1abdfd
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 53 deletions.
7 changes: 2 additions & 5 deletions examples/GetBlockItemStatus/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@ internal sealed class GetBlockItemSummaryOptions
[Option(HelpText = "Transaction hash to lookup", Required = true)]
public string TransactionHash { get; set; }

[Option(HelpText = "URL representing the endpoint where the gRPC V2 API is served.", Default = "http://node.testnet.concordium.com")]
[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(HelpText = "Port for the gRPC V2 API.", Default = 20000)]
public int Port { get; set; }
}

internal static class ExampleHelpers
Expand All @@ -40,7 +37,7 @@ public static class Program
/// An example showing how one can query transaction status from a node.
/// </summary>
/// <param name="args">GetBlockItemSummaryOptions
/// Example: --endpoint http://node.testnet.concordium.com --transactionhash 143ca4183d0bb204000ad08e0fd5792985c808861b97f3b81cb9016ad39d09d2 --port 20000
/// Example: --endpoint http://node.testnet.concordium.com:20000 --transactionhash 143ca4183d0bb204000ad08e0fd5792985c808861b97f3b81cb9016ad39d09d2
/// </param>
public static async Task Main(string[] args)
{
Expand Down
3 changes: 1 addition & 2 deletions src/Types/Address.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Concordium.Sdk.Exceptions;
using Google.Protobuf;

namespace Concordium.Sdk.Types;

Expand Down Expand Up @@ -40,7 +39,7 @@ internal static class AddressFactory
internal static IAddress From(Grpc.V2.Address address) =>
address.TypeCase switch
{
Grpc.V2.Address.TypeOneofCase.Account => AccountAddress.From(address.Account.ToByteArray()),
Grpc.V2.Address.TypeOneofCase.Account => AccountAddress.From(address.Account),
Grpc.V2.Address.TypeOneofCase.Contract => ContractAddress.From(address.Contract),
Grpc.V2.Address.TypeOneofCase.None => throw new MissingEnumException<Grpc.V2.Address.TypeOneofCase>(
address.TypeCase),
Expand Down
15 changes: 7 additions & 8 deletions src/Types/RejectReason.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using Concordium.Sdk.Exceptions;
using Google.Protobuf;

namespace Concordium.Sdk.Types;

Expand All @@ -20,7 +19,7 @@ internal static IRejectReason From(Grpc.V2.RejectReason other) =>
Grpc.V2.RejectReason.ReasonOneofCase.ModuleHashAlreadyExists => new ModuleHashAlreadyExists(
new ModuleReference(other.ModuleHashAlreadyExists.Value)),
Grpc.V2.RejectReason.ReasonOneofCase.InvalidAccountReference => new InvalidAccountReference(
AccountAddress.From(other.InvalidAccountReference.Value.ToByteArray())),
AccountAddress.From(other.InvalidAccountReference)),
Grpc.V2.RejectReason.ReasonOneofCase.InvalidInitMethod => new InvalidInitMethod(
new ModuleReference(other.InvalidInitMethod.ModuleRef.Value),
ContractName.From(other.InvalidInitMethod.InitName)),
Expand All @@ -46,7 +45,7 @@ internal static IRejectReason From(Grpc.V2.RejectReason other) =>
Grpc.V2.RejectReason.ReasonOneofCase.AlreadyABaker => new AlreadyABaker(
new BakerId(new AccountIndex(other.AlreadyABaker.Value))),
Grpc.V2.RejectReason.ReasonOneofCase.NotABaker => new NotABaker(
AccountAddress.From(other.NotABaker.ToByteArray())),
AccountAddress.From(other.NotABaker)),
Grpc.V2.RejectReason.ReasonOneofCase.InsufficientBalanceForBakerStake =>
new InsufficientBalanceForBakerStake(),
Grpc.V2.RejectReason.ReasonOneofCase.StakeUnderMinimumThresholdForBaking =>
Expand All @@ -63,20 +62,20 @@ internal static IRejectReason From(Grpc.V2.RejectReason other) =>
new InvalidEncryptedAmountTransferProof(),
Grpc.V2.RejectReason.ReasonOneofCase.InvalidTransferToPublicProof => new InvalidTransferToPublicProof(),
Grpc.V2.RejectReason.ReasonOneofCase.EncryptedAmountSelfTransfer => new EncryptedAmountSelfTransfer(
AccountAddress.From(other.EncryptedAmountSelfTransfer.ToByteArray())),
AccountAddress.From(other.EncryptedAmountSelfTransfer)),
Grpc.V2.RejectReason.ReasonOneofCase.InvalidIndexOnEncryptedTransfer =>
new InvalidIndexOnEncryptedTransfer(),
Grpc.V2.RejectReason.ReasonOneofCase.ZeroScheduledAmount => new ZeroScheduledAmount(),
Grpc.V2.RejectReason.ReasonOneofCase.NonIncreasingSchedule => new NonIncreasingSchedule(),
Grpc.V2.RejectReason.ReasonOneofCase.FirstScheduledReleaseExpired => new FirstScheduledReleaseExpired(),
Grpc.V2.RejectReason.ReasonOneofCase.ScheduledSelfTransfer => new ScheduledSelfTransfer(
AccountAddress.From(other.ScheduledSelfTransfer.ToByteArray())),
AccountAddress.From(other.ScheduledSelfTransfer)),
Grpc.V2.RejectReason.ReasonOneofCase.InvalidCredentials => new InvalidCredentials(),
Grpc.V2.RejectReason.ReasonOneofCase.DuplicateCredIds => new DuplicateCredIds(other.DuplicateCredIds.Ids
.Select(id => id.ToByteArray())
.Select(id => id.Value.ToByteArray())
.ToList()),
Grpc.V2.RejectReason.ReasonOneofCase.NonExistentCredIds => new NonExistentCredIds(other.NonExistentCredIds
.Ids.Select(id => id.ToByteArray())
.Ids.Select(id => id.Value.ToByteArray())
.ToList()),
Grpc.V2.RejectReason.ReasonOneofCase.RemoveFirstCredential => new RemoveFirstCredential(),
Grpc.V2.RejectReason.ReasonOneofCase.CredentialHolderDidNotSign => new CredentialHolderDidNotSign(),
Expand All @@ -97,7 +96,7 @@ internal static IRejectReason From(Grpc.V2.RejectReason other) =>
Grpc.V2.RejectReason.ReasonOneofCase.InsufficientDelegationStake => new InsufficientDelegationStake(),
Grpc.V2.RejectReason.ReasonOneofCase.DelegatorInCooldown => new DelegatorInCooldown(),
Grpc.V2.RejectReason.ReasonOneofCase.NotADelegator => new NotADelegator(
AccountAddress.From(other.NotADelegator.Value.ToByteArray())),
AccountAddress.From(other.NotADelegator)),
Grpc.V2.RejectReason.ReasonOneofCase.DelegationTargetNotABaker => new DelegationTargetNotABaker(
new BakerId(new AccountIndex(other.DelegationTargetNotABaker.Value))),
Grpc.V2.RejectReason.ReasonOneofCase.StakeOverMaximumThresholdForPool =>
Expand Down
33 changes: 33 additions & 0 deletions tests/IntegrationTests/Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System.Reflection;
using System.Text.Json;
using Concordium.Sdk.Client;
using Concordium.Sdk.Transactions;
using Concordium.Sdk.Types;
using Xunit.Abstractions;

namespace Concordium.Sdk.Tests.IntegrationTests;
Expand All @@ -28,6 +30,37 @@ protected Tests(ITestOutputHelper output)
this.Client = new ConcordiumClient(new Uri(uri), new ConcordiumClientOptions());
}

protected async Task<TransactionStatusFinalized> AwaitFinalization(TransactionHash txHash, CancellationToken token)
{
while (true)
{
if (!token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}

var transactionStatus = await this.Client.GetBlockItemStatusAsync(txHash, token);

switch (transactionStatus)
{
case TransactionStatusFinalized transactionStatusFinalized:
return transactionStatusFinalized;
default:
await Task.Delay(TimeSpan.FromSeconds(1), token);
break;
}
}
}

protected async Task<TransactionHash> Transfer(ITransactionSigner account, AccountAddress sender, AccountTransactionPayload transactionPayload, CancellationToken token)
{
var (accountSequenceNumber, _) = await this.Client.GetNextAccountSequenceNumberAsync(sender, token);
var preparedAccountTransaction = transactionPayload.Prepare(sender, accountSequenceNumber, Expiry.AtMinutesFromNow(30));
var signedTransfer = preparedAccountTransaction.Sign(account);
var txHash = await this.Client.SendAccountTransactionAsync(signedTransfer, token);
return txHash;
}

protected string GetString(string name) => this.GetConfiguration(name).GetString()!;

private JsonElement GetConfiguration(string name)
Expand Down
46 changes: 8 additions & 38 deletions tests/IntegrationTests/Types/OnChainDataTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,23 @@
using Xunit.Abstractions;
using AccountAddress = Concordium.Sdk.Types.AccountAddress;
using AccountTransactionDetails = Concordium.Sdk.Types.AccountTransactionDetails;
using AccountTransactionPayload = Concordium.Sdk.Transactions.AccountTransactionPayload;
using TransactionHash = Concordium.Sdk.Types.TransactionHash;

namespace Concordium.Sdk.Tests.IntegrationTests.Types;

[Trait("Category", "IntegrationTests")]
[Collection("Using Wallet")]
public sealed class OnChainDataTests : Tests
{
private const int Timeout = 120_000;
public OnChainDataTests(ITestOutputHelper output) : base(output)
{
}

[Fact(Timeout = 30_000)]
[Fact(Timeout = Timeout)]
public async Task WhenTransferWithoutMemo_ThenNull()
{
using var cts = new CancellationTokenSource(30_000);
// Arrange
using var cts = new CancellationTokenSource(Timeout);

var filePath = this.GetString("walletPath");
var walletData = await File.ReadAllTextAsync(filePath, cts.Token);
Expand All @@ -42,11 +43,11 @@ public async Task WhenTransferWithoutMemo_ThenNull()
}


[Fact(Timeout = 60_000)]
[Fact(Timeout = Timeout)]
public async Task GivenMemo_WhenTransfer_ThenMemoAbleToParse()
{
// Arrange
using var cts = new CancellationTokenSource(60_000);
using var cts = new CancellationTokenSource(Timeout);

var filePath = this.GetString("walletPath");
var walletData = await File.ReadAllTextAsync(filePath, cts.Token);
Expand Down Expand Up @@ -79,38 +80,7 @@ private static AccountTransfer ValidateArrangementOutcome(TransactionStatusFinal
finalized.State.Summary.Details.Should().BeOfType<AccountTransactionDetails>();
var details = finalized.State.Summary.Details as AccountTransactionDetails;
details!.Effects.Should().BeOfType<AccountTransfer>();
var transfer = details!.Effects as AccountTransfer;
var transfer = details.Effects as AccountTransfer;
return transfer!;
}

private async Task<TransactionStatusFinalized> AwaitFinalization(TransactionHash txHash, CancellationToken token)
{
while (true)
{
if (!token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}

var transactionStatus = await this.Client.GetBlockItemStatusAsync(txHash, token);

switch (transactionStatus)
{
case TransactionStatusFinalized transactionStatusFinalized:
return transactionStatusFinalized;
default:
await Task.Delay(TimeSpan.FromSeconds(1), token);
break;
}
}
}

private async Task<TransactionHash> Transfer(ITransactionSigner account, AccountAddress sender, AccountTransactionPayload transactionPayload, CancellationToken token)
{
var (accountSequenceNumber, _) = await this.Client.GetNextAccountSequenceNumberAsync(sender, token);
var preparedAccountTransaction = transactionPayload.Prepare(sender, accountSequenceNumber, Expiry.AtMinutesFromNow(30));
var signedTransfer = preparedAccountTransaction.Sign(account);
var txHash = await this.Client.SendAccountTransactionAsync(signedTransfer, token);
return txHash;
}
}
47 changes: 47 additions & 0 deletions tests/IntegrationTests/Types/RejectReasonTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Concordium.Sdk.Transactions;
using Concordium.Sdk.Types;
using Concordium.Sdk.Wallets;
using FluentAssertions;
using Xunit.Abstractions;

namespace Concordium.Sdk.Tests.IntegrationTests.Types;

[Trait("Category", "IntegrationTests")]
[Collection("Using Wallet")]
public sealed class RejectReasonTests : Tests
{
private const int Timeout = 120_000;
public RejectReasonTests(ITestOutputHelper output) : base(output)
{
}

[Fact(Timeout = Timeout)]
public async Task WhenTransferWithInsufficientAmount_ThenReject()
{
// Arrange
using var cts = new CancellationTokenSource(Timeout);

var filePath = this.GetString("walletPath");
var walletData = await File.ReadAllTextAsync(filePath, cts.Token);
var account = WalletAccount.FromWalletKeyExportFormat(walletData);
var sender = account.AccountAddress;

var to = this.GetString("transferTo");
var receiver = AccountAddress.From(to);

var toBigTransfer = new Transfer(CcdAmount.FromMicroCcd(ulong.MaxValue), receiver);

// Act
var txHash = await this.Transfer(account, sender, toBigTransfer, cts.Token);
var finalized = await this.AwaitFinalization(txHash, cts.Token);

// Assert
finalized.State.Summary.Details.Should().BeOfType<AccountTransactionDetails>();
var details = finalized.State.Summary.Details as AccountTransactionDetails;
details!.Effects.Should().BeOfType<None>();
var none = details.Effects as None;
none!.RejectReason.Should().BeOfType<AmountTooLarge>();
var amountToLarge = none.RejectReason as AmountTooLarge;
amountToLarge!.Address.Should().BeOfType<AccountAddress>();
}
}
37 changes: 37 additions & 0 deletions tests/UnitTests/Types/AddressTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using Concordium.Grpc.V2;
using Concordium.Sdk.Types;
using FluentAssertions;
using Google.Protobuf;
using Xunit;
using AccountAddress = Concordium.Sdk.Types.AccountAddress;

namespace Concordium.Sdk.Tests.UnitTests.Types;

public sealed class AddressTests
{
[Fact]
public void WhenGivenGrpcAccountAddress_ThenMap()
{
// Arrange
const string expected = "TIDFSgXWdUVHEOXdeTONB5WGRL9mN8JdEHjvgOThaIE=";
var fromBase64 = ByteString.FromBase64(expected);

var address = new Address(new Address
{
Account = new Grpc.V2.AccountAddress
{
Value = fromBase64
}
});

// Act
var from = AddressFactory.From(address);

// Assert
from.Should().BeOfType<AccountAddress>();
var accountAddress = from as AccountAddress;
var actual = Convert.ToBase64String(accountAddress!.AsSpan());
actual.Should().Be(expected);
}
}

0 comments on commit b1abdfd

Please sign in to comment.