1 /**
2     Stratum protocol is based on JSON-RPC 2.0.
3     (although it doesn't include "jsonrpc" information in every message).
4     Each message has to end with a line end character (n).
5 
6     This module has 3 entry point:
7         $(UL
8 			$(LI `RawStratumRpcAutoClient`)
9 			$(LI `HttpStratumRpcAutoClient`)
10 			$(LI `TcpStratumRpcAutoClient`)
11 		)
12 */
13 module rpc.protocol.stratum;
14 
15 public import rpc.core;
16 import rpc.protocol.json;
17 import vibe.data.json;
18 import std.typecons: Nullable, nullable;
19 
20 
21 class StratumRpcRequest : JsonRpcRequest!int
22 {
23 public:
24     static StratumRpcRequest make(T)(int id, string method, T params)
25     {
26         import vibe.data.json : serializeToJson;
27 
28         auto request = new StratumRpcRequest();
29         request.id = id;
30         request.method = method;
31         request.params = serializeToJson!T(params);
32 
33         return request;
34     }
35 
36     static StratumRpcRequest make(int id, string method)
37     {
38         auto request = new StratumRpcRequest();
39         request.id = id;
40         request.method = method;
41 
42         return request;
43     }
44 
45     /** Strip the "jsonrpc" field.
46     */
47     override Json toJson() const @safe
48     {
49         Json json = Json.emptyObject;
50         json["method"] = method;
51         json["id"] = id;
52         if (!params.isNull)
53             json["params"] = params.get;
54         return json;
55     }
56 
57     /** Strip the "jsonrpc" field.
58     */
59     static StratumRpcRequest fromJson(Json src) @safe
60     {
61         StratumRpcRequest request = new StratumRpcRequest();
62         request.method = src["method"].to!string;
63         if (src["id"].type != Json.Type.undefined)
64             request.id = src["id"].to!int;
65         if (src["params"].type != Json.Type.undefined)
66             request.params = src["params"].nullable;
67         return request;
68     }
69 }
70 
71 class StratumRpcResponse : JsonRpcResponse!int
72 {
73 public:
74     /** Strip the "jsonrpc" field.
75     */
76     override Json toJson() const @safe
77     {
78         Json json = Json.emptyObject;
79         // the id must be 'null' in case of parse error
80         if (!id.isNull)
81             json["id"] = id.get;
82         else
83             json["id"] = null;
84         if (!result.isNull)
85             json["result"] = result.get;
86         if (!error.isNull)
87             json["error"] = serializeToJson!(const(JsonRpcError))(error.get);
88         return json;
89     }
90 
91     /** Strip the "jsonrpc" field.
92     */
93     static StratumRpcResponse fromJson(Json src) @safe
94     {
95         StratumRpcResponse request = new StratumRpcResponse();
96         if (src["id"].type != Json.Type.undefined)
97         {
98             if (src["id"].type == Json.Type.null_)
99                 request.id.nullify;
100             else
101                 request.id = src["id"].to!int;
102         }
103         if (src["result"].type != Json.Type.undefined)
104             request.result = src["result"].nullable;
105         if (src["error"].type != Json.Type.undefined)
106             request.error = deserializeJson!JsonRpcError(src["error"]).nullable;
107         return request;
108     }
109 }
110 
111 class RawStratumRPCServer : RawJsonRpcServer!(int, StratumRpcRequest, StratumRpcResponse)
112 {
113     import vibe.core.stream: InputStream, OutputStream;
114 
115 public:
116     this(OutputStream ostream, InputStream istream) @safe
117     {
118         super(ostream, istream);
119     }
120 }
121 
122 class HTTPStratumRPCServer : HttpJsonRpcServer!(int, StratumRpcRequest, StratumRpcResponse)
123 {
124     import vibe.http.router: URLRouter;
125 
126 public:
127     this(URLRouter router, string path)
128     {
129         super(router, path);
130     }
131 }
132 
133 class TCPStratumRPCServer : TcpJsonRpcServer!(int, StratumRpcRequest, StratumRpcResponse)
134 {
135 public:
136     this(ushort port, RpcInterfaceSettings settings = new RpcInterfaceSettings())
137     {
138         super(port, settings);
139     }
140 }
141 
142 class StratumRpcAutoClient(I) : I
143 {
144     import autointf;
145     import std.traits;
146 
147 protected:
148     alias TId = int; // always an int
149     alias TReq = StratumRpcRequest;
150     alias TResp = StratumRpcResponse;
151     alias AutoClient(I) = JsonRpcAutoClient!(I, TId, TReq, TResp);
152     alias RPCClient = IRpcClient!(TId, TReq, TResp);
153     AutoClient!I _autoClient;
154 
155 public:
156     this(RPCClient client, RpcInterfaceSettings settings) @safe
157     {
158         _autoClient = new AutoClient!I(client, settings);
159     }
160 
161     pragma(inline, true)
162     ReturnType!Func executeMethod(alias Func, ARGS...)(ARGS args) @safe
163     {
164         return _autoClient.executeMethod!(Func, ARGS)(args);
165     }
166 
167     pragma(inline, true)
168     @property auto client() @safe { return _autoClient.client; }
169 
170     mixin(autoImplementMethods!(I,executeMethod)());
171 }
172 
173 ///
174 class RawStratumRpcAutoClient(I) : StratumRpcAutoClient!I
175 {
176     import vibe.core.stream: InputStream, OutputStream;
177 
178 public:
179     this(OutputStream ostream, InputStream istream) @safe
180     {
181         super(new RawJsonRpcClient!(int, StratumRpcRequest, StratumRpcResponse)(ostream, istream),
182             new RpcInterfaceSettings());
183     }
184 }
185 
186 ///
187 class HttpStratumRpcAutoClient(I) : StratumRpcAutoClient!I
188 {
189 public:
190     this(string host) @safe
191     {
192         super(new HttpJsonRpcClient!(int, StratumRpcRequest, StratumRpcResponse)(host),
193             new RpcInterfaceSettings());
194     }
195 }
196 
197 ///
198 class TcpStratumRpcAutoClient(I) : StratumRpcAutoClient!I
199 {
200 public:
201     this(string host, ushort port, RpcInterfaceSettings settings = new RpcInterfaceSettings()) @safe
202     {
203         super(new TcpJsonRpcClient!(int, StratumRpcRequest, StratumRpcResponse)(host, port, settings),
204             settings);
205     }
206 }