ビルトインCライトニングプラグイン.ネット
24340 ワード
最近、私は修正されましたmy own Lapps 仕事をするc-lightning plugin と一緒に仕事をするためにLND でもc-lightning ( What are Lapps? ).
私は誰かがそれをやって見つけることができなかった.ここでは、方法のガイドラインを残します.
私もa sample application , それで、あなたが説明よりサンプルに興味があるならば、そこへ行ってください.私の申請はF - CUNKですが、Cサンプルで書かれています.言語は異なりますが、基本的なアプローチはどちらの場合も同じです.
この場合、1つのクラスがあると仮定します
こんにちはと呼ばれるRPCメソッドを公開したい. を含むメッセージを返します ユーザからRPCパラメータとして渡された名前を含むメッセージを返します. 露光されたrpcの数は今後増加する可能性がある.
まず、通常のJSONRPC 2.0サーバーとして機能するアプリケーションを作成します.
これを行う標準的な方法はStreamJsonRpc MSによって書かれる図書館.
HTTP経由で通常のWebサーバーとして使用する場合は、this sample は良いリファレンスです.
しかし、Cライトニングのプラグインは
このように見える
Cライトニングプラグインは、メッセージを区切るために改行を使用するので、我々は使用します
また、もし通信相手がC≧でない場合(我々の場合ではない)、指定するのは良いことです
通常、RPCサーバはマルチスレッドモードで動作しますが、stdIN/Outputを並列に出力するとメッセージが破損します.
この問題を解決する最も簡単な方法は、要求を順次処理することです.
この場合、最も簡単な方法は
プラグインの場合、RPC呼び出しに関連したパフォーマンスの問題はまれであるので、我々はすべてのメソッドのためにセマフォを獲得することによって、書き込みを制限することに決めました.
Cライトニングプラグインは2つのRPCメソッドを公開しなければなりません:
前者はプラグインからCライトニングへの情報を伝えることであり、後者はCライトニングが開始されたときに情報を迂回することである.
多くの処理ロジックを自動的に他のプラグインのメソッド定義から生成することができますので、我々は物事を十分に一般的に保つために反射を使用します.
例えば、
オブジェクトはフィールド名
この場合、私たちは、Cの稲妻側によってスタートアップオプションgivinを結合するだけです
セット
これは、initメッセージが受信されるまで、サーバーの起動プロセスを中断するために後で使用されます.
Cライトニングとプラグインが標準入出力を介して通信するという事実は、ログを標準出力に送信することはできません.
既存のCライトニングプラグインはC -ライトニングにメッセージを送ります
これにより、Cライトニング側でログを統一し、読みやすくする.
. NETにバンドルされます.
これらの問題を解決するにはI have created my own ILoggerProvider .
Cライトニングは起動時にユーザが指定したプラグインを起動し、環境変数
このように、この環境変数の有無は、アプリケーションがプラグインとして実行されるべきであるかどうかをプログラムで決定するために使用することができる.
私の場合、2つの場所でこの変数を参照します
一つ目はホストの設定です.
の実行を遅らせる
最後に、プラグインを実行可能な単一のバイナリとしてコンパイルする必要があります.
したがって、我々はSingle file Executable Compilation 特徴.ネット
そして、それ!
あなたは
最初は、今回私がしたことをライブラリに変換して公開していますが、それをするのが難しくないので、initプロセスが自動化するのが難しいので、ドキュメントと例をここに残しておきます.
ライブラリにしたい場合は、おそらく最良の方法はSource Generator
私は誰かがそれをやって見つけることができなかった.ここでは、方法のガイドラインを残します.
私もa sample application , それで、あなたが説明よりサンプルに興味があるならば、そこへ行ってください.私の申請はF - CUNKですが、Cサンプルで書かれています.言語は異なりますが、基本的なアプローチはどちらの場合も同じです.
この場合、1つのクラスがあると仮定します
MyAwesomeRpcServer
, これはRPC呼び出しのメソッドハンドラを持っています.このように始まる
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using StreamJsonRpc;
namespace HelloWorldPlugin.Server
{
public class MyAwesomeRpcServerOptions
{
public string GreeterName { get; set; } = "World";
public bool IsInitiated { get; set; }
}
public class MyAwesomeRpcServer
{
private readonly MyAwesomeRpcServerOptions _opts;
public MyAwesomeRpcServer(MyAwesomeRpcServerOptions opts)
{
_opts = opts;
}
[JsonRpcMethod("hello")]
public async Task<string> HelloAsync(string name)
{
return $"hello!! {name}! This is {_opts.GreeterName} !!";
}
}
}
要件はGreeterName
コマンドライン引数として渡されます.1 . StreamJSONRPCを使用してJSONRPCServerを作成します。
まず、通常のJSONRPC 2.0サーバーとして機能するアプリケーションを作成します.
これを行う標準的な方法はStreamJsonRpc MSによって書かれる図書館.
HTTP経由で通常のWebサーバーとして使用する場合は、this sample は良いリファレンスです.
しかし、Cライトニングのプラグインは
lightningd
標準入力/出力によるそれ自体Console.In
and Console.Out
輸送用.このように見える
var rpcServer = new MyAwesomeRpcServer();
var formatter = new JsonMessageFormatter();
var handler = new NewLineDelimitedMessageHandler(Console.OpenStandardOutput(), Console.OpenStandardInput(), formatter);
var rpc = new JsonRpc(handler);
rpc.AddLocalRpcTarget(rpcServer, new JsonRpcTargetOptions());
rpc.ExceptionStrategy = ExceptionProcessing.CommonErrorData;
rpc.StartListening();
JSON RPC 2.0仕様では、それぞれのメッセージとトランスポートを分離する方法を指定しません.Cライトニングプラグインは、メッセージを区切るために改行を使用するので、我々は使用します
NewLineDelimitedMessageHandler
メッセージハンドラとして.また、もし通信相手がC≧でない場合(我々の場合ではない)、指定するのは良いことです
ExceptionStrategy
ASExceptionProcessing.CommonErrorData
.read / write操作スレッドを安全にします。
通常、RPCサーバはマルチスレッドモードで動作しますが、stdIN/Outputを並列に出力するとメッセージが破損します.
この問題を解決する最も簡単な方法は、要求を順次処理することです.
この場合、最も簡単な方法は
AsyncSemaphore
, ASdescribed in the official guidelines .プラグインの場合、RPC呼び出しに関連したパフォーマンスの問題はまれであるので、我々はすべてのメソッドのためにセマフォを獲得することによって、書き込みを制限することに決めました.
3 .メソッドの自動生成
Cライトニングプラグインは2つのRPCメソッドを公開しなければなりません:
getmanifest
and init
.前者はプラグインからCライトニングへの情報を伝えることであり、後者はCライトニングが開始されたときに情報を迂回することである.
多くの処理ロジックを自動的に他のプラグインのメソッド定義から生成することができますので、我々は物事を十分に一般的に保つために反射を使用します.
例えば、
getmanifest
このように見える [JsonRpcMethod("getmanifest")]
public async Task<ManifestDto> GetManifestAsync(bool allowDeprecatedApis = false, object? otherParams = null)
{
using var releaser = await _semaphore.EnterAsync();
var userDefinedMethodInfo =
this
.GetType()
.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(m => !m.IsSpecialName && !String.Equals(m.Name, "initasync", StringComparison.OrdinalIgnoreCase) && !String.Equals(m.Name, "getmanifestasync", StringComparison.OrdinalIgnoreCase));
var methods =
userDefinedMethodInfo
.Select(m =>
{
var argSpec = m.GetParameters();
var numDefaults =
argSpec.Count(s => s.HasDefaultValue);
var keywordArgsStartIndex = argSpec.Length - numDefaults;
var args =
argSpec.Where(s =>
!string.Equals(s.Name, "plugin", StringComparison.OrdinalIgnoreCase) &&
!string.Equals(s.Name, "request", StringComparison.OrdinalIgnoreCase))
.Select((s, i) => i < keywordArgsStartIndex ? s.Name : $"[{s.Name}]");
var name = m.Name.ToLowerInvariant();
if (name.EndsWith("async"))
name = name[..^5];
return new RPCMethodDTO
{
Name = name,
Usage = string.Join(' ', args),
Description = _rpcDescriptions[name].Item1,
LongDescription = _rpcDescriptions[name].Item2
};
});
return new ManifestDto
{
Options =
// translate `System.CommandLine` to c-lightning compatible style.
CommandLines.GetOptions().Select(CliOptionsToDto).ToArray(),
RpcMethods = methods.ToArray(),
Notifications = new NotificationsDTO[]{},
Subscriptions = new string[]{},
Hooks = new object[] {},
Dynamic = true,
FeatureBits = null
};
INITは以下の通りである.これはGetManifestよりも自動化するのがより困難です.また、プロセスはアプリケーションの要件によって異なります. [JsonRpcMethod("init")]
public async Task InitAsync(LnInitConfigurationDTO configuration, Dictionary<string, object> options)
{
using var releaser = await _semaphore.EnterAsync();
foreach (var op in options)
{
var maybeProp =
_opts.GetType().GetProperties()
.FirstOrDefault(p => string.Equals(p.Name, op.Key, StringComparison.OrdinalIgnoreCase));
maybeProp?.SetValue(_opts, op.Value);
}
_opts.IsInitiated = true;
}
initはメソッド引数をオブジェクトとして渡します.オブジェクトはフィールド名
configurations
and options
, したがって、Cの角側のメソッドハンドラは、自動的にバインドするのと同じ名前でなければなりません.この場合、私たちは、Cの稲妻側によってスタートアップオプションgivinを結合するだけです
_opts
.LnInitConfigurationDto
'Cライトニング側の情報(例えばtorを使用しているユーザーですか?どんな稲妻特徴ビットが支えられるか?)を使用して初期化処理を自由に実行できます.セット
IsInitiated
初期化処理終了.これは、initメッセージが受信されるまで、サーバーの起動プロセスを中断するために後で使用されます.
jsonrpclogger
Cライトニングとプラグインが標準入出力を介して通信するという事実は、ログを標準出力に送信することはできません.
既存のCライトニングプラグインはC -ライトニングにメッセージを送ります
log
RPC通知としてRPCメソッドと残りの処理をCライトニング側に残します.これにより、Cライトニング側でログを統一し、読みやすくする.
. NETにバンドルされます.
ConsoleLogger
, しかし、これはバックグラウンドタスクとして書き込みプロセスを実行するので、同じようにロギングロジックを実装するならば、上記のスレッドレースが起こるかもしれません.これらの問題を解決するにはI have created my own ILoggerProvider .
起動処理
Cライトニングは起動時にユーザが指定したプラグインを起動し、環境変数
LIGHTNINGD_PLUGIN=1
起動時.このように、この環境変数の有無は、アプリケーションがプラグインとして実行されるべきであるかどうかをプログラムで決定するために使用することができる.
私の場合、2つの場所でこの変数を参照します
一つ目はホストの設定です.
Action<IHostBuilder> configureHostBuilder = hostBuilder =>
{
var isPluginMode = Environment.GetEnvironmentVariable("LIGHTNINGD_PLUGIN") == "1";
if (isPluginMode)
{
hostBuilder
.ConfigureLogging(builder => { builder.AddJsonRpcNotificationLogger();})
.ConfigureServices(serviceCollection =>
serviceCollection
.AddSingleton<MyAwesomeRpcServerOptions>()
.AddSingleton<MyAwesomeRpcServer>()
);
}
else
{
// configuration for running as a normal webserver.
}
};
もう一つはホストの実行中です.の実行を遅らせる
IHost.RunAsync()
下記の通り. var host = hostBuilder.Build();
var isPluginMode = Environment.GetEnvironmentVariable("LIGHTNINGD_PLUGIN") == "1";
if (isPluginMode)
await host.StartJsonRpcServerForInitAsync(); /// initialize only rpc server and listen to `init` call.
await host.RunAsync();
これにより起動が遅れるBackgroundService
そして、initメッセージがlightningdから受信されるまで、出力されるログ.単一のバイナリとしてコンパイルする
最後に、プラグインを実行可能な単一のバイナリとしてコンパイルする必要があります.
したがって、我々はSingle file Executable Compilation 特徴.ネット
そして、それ!
あなたは
--plugin
or --plugin-dir
Cライトニングのプラグインを起動します.図書館として発行する?
最初は、今回私がしたことをライブラリに変換して公開していますが、それをするのが難しくないので、initプロセスが自動化するのが難しいので、ドキュメントと例をここに残しておきます.
ライブラリにしたい場合は、おそらく最良の方法はSource Generator
Reference
この問題について(ビルトインCライトニングプラグイン.ネット), 我々は、より多くの情報をここで見つけました https://dev.to/joemphilips/building-c-lightning-plugin-with-net-3162テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol