Skip to content

Commit

Permalink
added inistial work on unstable api https://paritytech.github.io/json…
Browse files Browse the repository at this point in the history
  • Loading branch information
darkfriend77 committed Oct 24, 2023
1 parent feb1d8b commit a903b09
Show file tree
Hide file tree
Showing 9 changed files with 387 additions and 100 deletions.
166 changes: 67 additions & 99 deletions Substrate.NetApi.TestNode/ExtrinsicsTest.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;
using NUnit.Framework.Internal;
using Schnorrkel.Keys;
using Substrate.NetApi.Model.Extrinsics;
using Substrate.NetApi.Model.Rpc;
using Substrate.NetApi.Model.Types;
using Substrate.NetApi.Model.Types.Base;
using Substrate.NetApi.Model.Types.Primitive;
using NUnit.Framework;
using Schnorrkel.Keys;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Substrate.NetApi.TestNode
{
public class ExtrinsicsTest
{
public MiniSecret MiniSecretAlice => new MiniSecret(Utils.HexToByteArray("0xe5be9a5092b81bca64be81d212e7f2f9eba183bb7a90954f7b76361f6edb5c0a"), ExpandMode.Ed25519);
public Account Alice => Account.Build(KeyType.Sr25519, MiniSecretAlice.ExpandToSecret().ToBytes(), MiniSecretAlice.GetPair().Public.Key);

public MiniSecret MiniSecretBob => new MiniSecret(Utils.HexToByteArray("0x398f0c28f98885e046333d4a41c19cee4c37368a9832c6502f6cfd182e2aef89"), ExpandMode.Ed25519);
public Account Bob => Account.Build(KeyType.Sr25519, MiniSecretBob.ExpandToSecret().ToBytes(), MiniSecretBob.GetPair().Public.Key);

Expand Down Expand Up @@ -67,119 +65,89 @@ public async Task TearDownAsync()
}

/// <summary>
/// Extrinsic Remark test
/// Extrinsic Submit And Watch
/// </summary>
/// <returns></returns>
[Test]
public async Task Extrinsic_RemarkTestAsync()
public async Task Extrinsic_SubmitAndWatchExtrinsicAsync()
{
var method = new Method(0, "System", 0, "remark", new byte[] { 0x04, 0xFF });

var taskCompletionSource = new TaskCompletionSource<(bool, Hash)>();
await _substrateClient.Author.SubmitAndWatchExtrinsicAsync((string subscriptionId, ExtrinsicStatus extrinsicUpdate) => Callback(subscriptionId, extrinsicUpdate, taskCompletionSource), method, Alice, _chargeType, 64, CancellationToken.None);

var finished = await Task.WhenAny(taskCompletionSource.Task, Task.Delay(TimeSpan.FromMinutes(1))); // 5 minutes or any appropriate timeout
await _substrateClient.Author.SubmitAndWatchExtrinsicAsync((string subscriptionId, ExtrinsicStatus extrinsicUpdate) =>
{
if (extrinsicUpdate.ExtrinsicState == ExtrinsicState.Finalized ||
extrinsicUpdate.ExtrinsicState == ExtrinsicState.Dropped ||
extrinsicUpdate.ExtrinsicState == ExtrinsicState.Invalid)
{
taskCompletionSource.SetResult((true, extrinsicUpdate.Hash));
}
}, method, Alice, _chargeType, 64, CancellationToken.None);

var finished = await Task.WhenAny(taskCompletionSource.Task, Task.Delay(TimeSpan.FromMinutes(1)));
Assert.AreEqual(taskCompletionSource.Task, finished, "Test timed out waiting for final callback");
}

/// <summary>
/// Transaction Unstable Submit And Watch
/// </summary>
/// <returns></returns>
[Test]
public async Task Extrinsic_FailTestAsync()
public async Task Extrinsic_TransactionUnstableSubmitAndWatchAsync()
{

var method = new Method(0, "System", 1, "set_heap_pages", new U64(999).Encode());
var method = new Method(0, "System", 0, "remark", new byte[] { 0x04, 0xFF });

var taskCompletionSource = new TaskCompletionSource<(bool, Hash)>();

var account = Bob;

var subscriptionId = await _substrateClient.Author.SubmitAndWatchExtrinsicAsync((string subscriptionId, ExtrinsicStatus extrinsicUpdate) => Callback(subscriptionId, extrinsicUpdate, taskCompletionSource), method, account, _chargeType, 64, CancellationToken.None);

var finished = await Task.WhenAny(taskCompletionSource.Task, Task.Delay(TimeSpan.FromMinutes(1))); // 5 minutes or any appropriate timeout
Assert.AreEqual(taskCompletionSource.Task, finished, "Test timed out waiting for final callback");

var block = await _substrateClient.Chain.GetBlockAsync((await taskCompletionSource.Task).Item2);

var accountExtrinsics = block.Block.Extrinsics.Where(p => p.Signed && p.Account.Value == account.Value);
Assert.AreEqual(1, accountExtrinsics.Count());

}

/// <summary>
/// Extrinsic Transfer Callback test
/// </summary>
/// <param name="subscriptionId"></param>
/// <param name="extrinsicUpdate"></param>
/// <param name="taskCompletionSource"></param>
private static void Callback(string subscriptionId, ExtrinsicStatus extrinsicUpdate, TaskCompletionSource<(bool, Hash)> taskCompletionSource)
{
ActionExtrinsicUpdate(subscriptionId, extrinsicUpdate);
if (extrinsicUpdate.ExtrinsicState == ExtrinsicState.Finalized ||
extrinsicUpdate.ExtrinsicState == ExtrinsicState.Dropped ||
extrinsicUpdate.ExtrinsicState == ExtrinsicState.Invalid)
_ = await _substrateClient.Unstable.TransactionUnstableSubmitAndWatchAsync((string subscriptionId, TransactionEventInfo extrinsicUpdate) =>
{
taskCompletionSource.SetResult((true, extrinsicUpdate.Hash));
}
if (extrinsicUpdate.TransactionEvent == TransactionEvent.Finalized ||
extrinsicUpdate.TransactionEvent == TransactionEvent.Dropped ||
extrinsicUpdate.TransactionEvent == TransactionEvent.Invalid ||
extrinsicUpdate.TransactionEvent == TransactionEvent.Error)
{
taskCompletionSource.SetResult((true, extrinsicUpdate.Hash));
}
}, method, Alice, _chargeType, 64, CancellationToken.None);

var finished = await Task.WhenAny(taskCompletionSource.Task, Task.Delay(TimeSpan.FromMinutes(1)));
Assert.AreEqual(taskCompletionSource.Task, finished, "Test timed out waiting for final callback");
}

/// <summary>
/// Simple extrinsic tester
/// Transaction Unstable Unwatch
/// </summary>
/// <param name="subscriptionId"></param>
/// <param name="extrinsicUpdate"></param>
private static void ActionExtrinsicUpdate(string subscriptionId, ExtrinsicStatus extrinsicUpdate)
/// <returns></returns>
[Test, Timeout(10000)] // Timeout after 10 seconds
public async Task Extrinsic_TransactionUnstableUnwatchAsync()
{
if (subscriptionId == null || subscriptionId.Length == 0)
var method = new Method(0, "System", 0, "remark", new byte[] { 0x04, 0xFF });
var cancellationTokenSource = new CancellationTokenSource();

var taskCompletionSource = new TaskCompletionSource<bool>();
var subscriptionId = await _substrateClient.Unstable.TransactionUnstableSubmitAndWatchAsync(
(subscriptionId, extrinsicUpdate) =>
{
{
if (extrinsicUpdate.TransactionEvent != TransactionEvent.Validated)
{
taskCompletionSource.SetResult(true);
}
}
},
method, Alice, _chargeType, 64, cancellationTokenSource.Token);

var unsubscribed = await _substrateClient.Unstable.TransactionUnstableUnwatchAsync(subscriptionId);
Assert.IsTrue(unsubscribed, "Unsubscribing from transaction should be successful.");

// Optionally: wait for the callback to be called, which should not happen
var callbackCalled = await Task.WhenAny(taskCompletionSource.Task, Task.Delay(500));
if (callbackCalled == taskCompletionSource.Task)
{
Assert.IsTrue(false);
Assert.Fail("Callback should not be called after unsubscribing.");
}

switch (extrinsicUpdate.ExtrinsicState)
{
case ExtrinsicState.Future:
Assert.IsTrue(false);
break;

case ExtrinsicState.Ready:
Assert.IsTrue(true);
break;

case ExtrinsicState.Dropped:
Assert.IsTrue(false);
break;

case ExtrinsicState.Invalid:
Assert.IsTrue(false);
break;

case ExtrinsicState.Broadcast:
Assert.IsTrue(extrinsicUpdate.Broadcast != null);
break;

case ExtrinsicState.InBlock:
Assert.IsTrue(extrinsicUpdate.Hash.Value.Length > 0);
break;

case ExtrinsicState.Retracted:
Assert.IsTrue(extrinsicUpdate.Hash.Value.Length > 0);
break;

case ExtrinsicState.FinalityTimeout:
Assert.IsTrue(extrinsicUpdate.Hash.Value.Length > 0);
break;

case ExtrinsicState.Finalized:
Assert.IsTrue(extrinsicUpdate.Hash.Value.Length > 0);
break;

case ExtrinsicState.Usurped:
Assert.IsTrue(extrinsicUpdate.Hash.Value.Length > 0);
break;

default:
Assert.IsTrue(false);
break;

}
// Cleanup if needed
cancellationTokenSource.Cancel();
}
}
}
1 change: 1 addition & 0 deletions Substrate.NetApi/Model/Rpc/ExtrinsicStatus.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Newtonsoft.Json;
using Substrate.NetApi.Model.Types.Base;
using System;

namespace Substrate.NetApi.Model.Rpc
{
Expand Down
36 changes: 36 additions & 0 deletions Substrate.NetApi/Model/Rpc/TransactionEventInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Substrate.NetApi.Model.Types.Base;

namespace Substrate.NetApi.Model.Rpc
{
public enum TransactionEvent
{
Validated,

Broadcasted,

BestChainBlockIncluded,

Finalized,

Error,

Invalid,

Dropped
}

public sealed class TransactionEventInfo
{
public TransactionEvent TransactionEvent { get; set; }

public uint? NumPeers { get; set; }

public Hash Hash { get; set; }

public uint? Index { get; set; }

public bool? Broadcasted { get; set; }

public string Error { get; set; }
}
}
68 changes: 68 additions & 0 deletions Substrate.NetApi/Modules/Contracts/IUnstableCalls.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Substrate.NetApi.Model.Extrinsics;
using Substrate.NetApi.Model.Rpc;
using Substrate.NetApi.Model.Types;
using Substrate.NetApi.Model.Types.Base;

namespace Substrate.NetApi.Modules.Contracts
{
public interface IUnstableCalls
{
/// <summary>
/// Submit and subscribe to watch an extrinsic until unsubscribed
/// </summary>
/// <param name="callback"></param>
/// <param name="method"></param>
/// <param name="account"></param>
/// <param name="charge"></param>
/// <param name="lifeTime"></param>
/// <returns></returns>
Task<string> TransactionUnstableSubmitAndWatchAsync(Action<string, TransactionEventInfo> callback, Method method, Account account, ChargeType charge, uint lifeTime);

/// <summary>
/// Submit and subscribe to watch an extrinsic until unsubscribed
/// </summary>
/// <param name="callback"></param>
/// <param name="method"></param>
/// <param name="account"></param>
/// <param name="charge"></param>
/// <param name="lifeTime"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<string> TransactionUnstableSubmitAndWatchAsync(Action<string, TransactionEventInfo> callback, Method method, Account account, ChargeType charge, uint lifeTime, CancellationToken token);

/// <summary>
/// Submit and subscribe to watch an extrinsic until unsubscribed
/// </summary>
/// <param name="callback"></param>
/// <param name="parameters"></param>
/// <returns></returns>
Task<string> TransactionUnstableSubmitAndWatchAsync(Action<string, TransactionEventInfo> callback, string parameters);

/// <summary>
/// Submit and subscribe to watch an extrinsic until unsubscribed
/// </summary>
/// <param name="callback"></param>
/// <param name="parameters"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<string> TransactionUnstableSubmitAndWatchAsync(Action<string, TransactionEventInfo> callback, string parameters, CancellationToken token);

/// <summary>
/// Unsuscribe to given subscription id
/// </summary>
/// <param name="subscriptionId"></param>
/// <returns></returns>
Task<bool> TransactionUnstableUnwatchAsync(string subscriptionId);

/// <summary>
/// Unsuscribe to given subscription id
/// </summary>
/// <param name="subscriptionId"></param>
/// <param name="token"></param>
/// <returns></returns>
Task<bool> TransactionUnstableUnwatchAsync(string subscriptionId, CancellationToken token);
}
}
Loading

0 comments on commit a903b09

Please sign in to comment.