This page was exported from David Olliver [ https://www.davidolliver.com ]
Export date: Wed Dec 12 9:26:02 2018 / +0000 GMT

C#: Send a (XRP) payment using offline signing and Task-based Asynchronous Pattern




WATCH DEMO:

YouTube Video: YouTube.com/watch?v=vzJ9EZpiEx0



In my previous article Parallel Programming with .NET and C# NetStandard 2.0 client for the [Ripple WebSocket APIs] I recommended read the following paper: Task-based Asynchronous Pattern (TAP) : https://www.microsoft.com/en-us/download/details.aspx?id=19957 In case you missed it I will put some basic explanation below:

The Task-based Asynchronous Pattern (TAP) is a new pattern for asynchrony in the .NET Framework.  It is based on the Task and Task<TResult> types in the System.Threading.Tasks namespace, which are used to represent arbitrary asynchronous operations.
An asynchronous method based on the TAP is permitted to do a small amount of work synchronously before it returns the resulting Task. The work should be kept to the minimal amount necessary, performing operations such as validating arguments and initiating the asynchronous operation.  It is likely that asynchronous methods will be invoked from user interface threads, and thus any long-running work in the synchronous up-front portion of an asynchronous method could harm responsiveness.  It is also likely that multiple asynchronous methods will be launched concurrently, and thus any long-running work in the synchronous up-front portion of an asynchronous method could delay the initiation of other asynchronous operations, thereby decreasing benefits of concurrency.

Also to understand the concept of Paralell Programming, please read this article:  https://blogs.msdn.microsoft.com/pfxteam/2012/02/02/await-synchronizationcontext-and-console-apps-part-3/

TOOLS WE NEED





The code which will help you Send a (XRP) payment using offline signing:

First of all if you didn't do it earlier go to XRP Test Net Faucet and Generate your credentials. Once you get the credentials make changes accordingly.

Using Statements (those statements do much more than just getting account balances):
[cc lang="csharp"]
using System.Threading.Tasks;
using System.Windows.Forms;
using RippleDotNet.Extensions;
using RippleDotNet.Json.Converters;
using RippleDotNet.Model.Account;
using RippleDotNet.Model.Ledger.Objects;
using RippleDotNet.Model.Ledger;
using RippleDotNet.Model.Server;
using RippleDotNet.Requests.Account;
using RippleDotNet.Requests.Ledger;
using RippleDotNet.Requests.Transaction;
using RippleDotNet.Responses.Transaction.Interfaces;
using RippleDotNet.Responses.Transaction.TransactionTypes;
using RippleDotNet;
using RippleDotNet.Model.Transaction.Interfaces;
using RippleDotNet.Model;
using RippleDotNet.Model.Transaction;
using RippleDotNet.Model.Transaction.TransactionTypes;
using Ripple.Core.Types;
using Ripple.TxSigning;
using Newtonsoft.Json.Linq;
using Currency = RippleDotNet.Model.Currency;
using System.Threading;
using System.Collections.Concurrent;
[/cc]

Also, you will need AsycPupm Class:

[cc lang="csharp"]
public static class AsyncPump
{
/// Runs the specified asynchronous method.
/// The asynchronous method to execute.
public static void Run(Action asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(true);
SynchronizationContext.SetSynchronizationContext(syncCtx);

// Invoke the function
syncCtx.OperationStarted();
asyncMethod();
syncCtx.OperationCompleted();

// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}

/// Runs the specified asynchronous method.
/// The asynchronous method to execute.
public static void Run(Func asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);

// Invoke the function and alert the context to when it completes
var t = asyncMethod();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}

/// Runs the specified asynchronous method.
/// The asynchronous method to execute.
public static T Run(Func> asyncMethod)
{
if (asyncMethod == null) throw new ArgumentNullException("asyncMethod");

var prevCtx = SynchronizationContext.Current;
try
{
// Establish the new context
var syncCtx = new SingleThreadSynchronizationContext(false);
SynchronizationContext.SetSynchronizationContext(syncCtx);

// Invoke the function and alert the context to when it completes
var t = asyncMethod();
if (t == null) throw new InvalidOperationException("No task provided.");
t.ContinueWith(delegate { syncCtx.Complete(); }, TaskScheduler.Default);

// Pump continuations and propagate any exceptions
syncCtx.RunOnCurrentThread();
return t.GetAwaiter().GetResult();
}
finally { SynchronizationContext.SetSynchronizationContext(prevCtx); }
}

/// Provides a SynchronizationContext that's single-threaded.
private sealed class SingleThreadSynchronizationContext : SynchronizationContext
{
/// The queue of work items.
private readonly BlockingCollection> m_queue =
new BlockingCollection>();
/// The processing thread.
private readonly Thread m_thread = Thread.CurrentThread;
/// The number of outstanding operations.
private int m_operationCount = 0;
/// Whether to track operations m_operationCount.
private readonly bool m_trackOperations;

/// Initializes the context.
/// Whether to track operation count.
internal SingleThreadSynchronizationContext(bool trackOperations)
{
m_trackOperations = trackOperations;
}

/// Dispatches an asynchronous message to the synchronization context.
/// The System.Threading.SendOrPostCallback delegate to call.
/// The object passed to the delegate.
public override void Post(SendOrPostCallback d, object state)
{
if (d == null) throw new ArgumentNullException("d");
m_queue.Add(new KeyValuePair(d, state));
}

/// Not supported.
public override void Send(SendOrPostCallback d, object state)
{
throw new NotSupportedException("Synchronously sending is not supported.");
}

/// Runs an loop to process all queued work items.
public void RunOnCurrentThread()
{
foreach (var workItem in m_queue.GetConsumingEnumerable())
workItem.Key(workItem.Value);
}

/// Notifies the context that no more work will arrive.
public void Complete() { m_queue.CompleteAdding(); }

/// Invoked when an async operation is started.
public override void OperationStarted()
{
if (m_trackOperations)
Interlocked.Increment(ref m_operationCount);
}

/// Invoked when an async operation is completed.
public override void OperationCompleted()
{
if (m_trackOperations &&
Interlocked.Decrement(ref m_operationCount) == 0)
Complete();
}
}
}
[/cc]

And finally the code you were looking for:

[cc lang="csharp"]
public async Task SendPayments()
{
IRippleClient client = new RippleClient("wss://s.altnet.rippletest.net:51233");
client.Connect();
AccountInfo accountInfo = await client.AccountInfo("rp5EciKXsqUb6ZWkFQBGzeGACTsgfkLp6E");
IPaymentTransaction paymentTransaction = new PaymentTransaction();
paymentTransaction.Sequence = accountInfo.AccountData.Sequence;
textBox2.Text = paymentTransaction.Sequence.ToString();
var secret = "xxxxxx";
var unsignedTxJson = @"{
'Account': 'rp5EciKXsqUb6ZWkFQBGzeGACTsgfkLp6E',
'Amount': '18000000',
'Destination': 'rJAGYcpj2TGePnEmKfqmNAfjQFhrBZdzRr',
'Fee': '10',
'Sequence': " + paymentTransaction.Sequence.ToString() + @",
'TransactionType' : 'Payment'
}";
//12700000 = 12.7
//1270000000 = 1,270 XRP
var signed = TxSigner.SignJson(JObject.Parse(unsignedTxJson), secret);
textBox1.Text = signed.Hash + "rn" + signed.TxJson + "rn" + signed.TxBlob;

// TxSigner signer = TxSigner.FromSecret("XXXXX"); //secret is not sent to server, offline signing only
// SignedTx signedTx = signer.SignJson(JObject.Parse(paymentTransaction.ToJson()));

SubmitBlobRequest request = new SubmitBlobRequest();
request.TransactionBlob = signed.TxBlob;

Submit result = await client.SubmitTransactionBlob(request);
textBox3.Text = result.Transaction.Hash;
client.Disconnect();
}
[/cc]
and finally run the AsycPump:
[cc lang="csharp"]
AsyncPump.Run(() => SendPayments());
[/cc]

Don't forget to use your Credentials, including your Secret Key.
Excerpt: The Task-based Asynchronous Pattern (TAP) is a new pattern for asynchrony in the .NET Framework. It is based on the Task and Task types in the System.Threading.Tasks namespace, which are used to represent arbitrary asynchronous operations.
Post date: 2018-11-29 06:15:05
Post date GMT: 2018-11-29 06:15:05
Post modified date: 2018-12-01 08:51:33
Post modified date GMT: 2018-12-01 08:51:33
Powered by [ Universal Post Manager ] plugin. HTML saving format developed by gVectors Team www.gVectors.com