-
Notifications
You must be signed in to change notification settings - Fork 497
/
DaemonBase.cs
265 lines (232 loc) · 10.5 KB
/
DaemonBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
#region License
//
// CoiniumServ - Crypto Currency Mining Pool Server Software
// Copyright (C) 2013 - 2014, CoiniumServ Project - http://www.coinium.org
// http://www.coiniumserv.com - https://github.com/CoiniumServ/CoiniumServ
//
// This software is dual-licensed: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// For the terms of this license, see licenses/gpl_v3.txt.
//
// Alternatively, you can license this software under a commercial
// license or white-label it as set out in licenses/commercial.txt.
//
#endregion
using System;
using System.IO;
using System.Net;
using System.Text;
using CoiniumServ.Coin.Config;
using CoiniumServ.Daemon.Config;
using CoiniumServ.Daemon.Errors;
using CoiniumServ.Daemon.Exceptions;
using CoiniumServ.Logging;
using CoiniumServ.Utils.Extensions;
using Newtonsoft.Json;
using Serilog;
namespace CoiniumServ.Daemon
{
public class DaemonBase : IDaemonBase
{
public string RpcUrl { get; private set; }
public string RpcUser { get; private set; }
public string RpcPassword { get; private set; }
public Int32 RequestCounter { get; private set; }
private readonly Int32 _timeout;
private readonly IRpcExceptionFactory _rpcExceptionFactory;
private readonly ILogger _logger;
public DaemonBase(IDaemonConfig daemonConfig, ICoinConfig coinConfig, IRpcExceptionFactory rpcExceptionFactory)
{
_rpcExceptionFactory = rpcExceptionFactory;
_logger = LogManager.PacketLogger.ForContext<DaemonClient>().ForContext("Component", coinConfig.Name);
_timeout = daemonConfig.Timeout * 1000; // set the daemon timeout.
RpcUrl = string.Format("http://{0}:{1}", daemonConfig.Host, daemonConfig.Port);
RpcUser = daemonConfig.Username;
RpcPassword = daemonConfig.Password;
RequestCounter = 0;
}
/// <summary>
/// Make a a request. Invoke the given method with the given parameters.
/// </summary>
/// <typeparam name="T">
/// Type to return as the response.
/// We will try to convert the JSON response to this type.
/// </typeparam>
/// <param name="method">Method to invoke.</param>
/// <param name="parameters">Parameters to pass to the method.</param>
/// <returns>The JSON RPC response deserialized as the given type.</returns>
protected T MakeRequest<T>(string method, params object[] parameters)
{
var rpcResponse = MakeRpcRequest<T>(new DaemonRequest(RequestCounter++, method, parameters));
return rpcResponse.Result;
}
/// <summary>
/// Make an JSON RPC request, and return a JSON RPC response object with the result
/// deserialized as the given type.
/// </summary>
/// <typeparam name="T">The type to deserialize the result as.</typeparam>
/// <param name="walletRequest">The request object.</param>
/// <returns>A JSON RPC response with the result deserialized as the given type.</returns>
private DaemonResponse<T> MakeRpcRequest<T>(DaemonRequest walletRequest)
{
var httpWebRequest = MakeHttpRequest(walletRequest);
return GetRpcResponse<T>(httpWebRequest);
}
/// <summary>
/// Make a raw JSON RPC request with the given request object. Returns raw JSON.
/// </summary>
/// <param name="method"></param>
/// <param name="parameters"></param>
/// <returns></returns>
public string MakeRawRequest(string method, params object[] parameters)
{
var response = MakeRawRpcRequest(new DaemonRequest(RequestCounter++, method, parameters));
_logger.Verbose("rx: {0}", response.PrettifyJson());
return response;
}
/// <summary>
/// Make a raw JSON RPC request with the given request object. Returns raw JSON.
/// </summary>
/// <param name="walletRequest">The request object.</param>
/// <returns>The raw JSON string.</returns>
private string MakeRawRpcRequest(DaemonRequest walletRequest)
{
var httpWebRequest = MakeHttpRequest(walletRequest);
return GetJsonResponse(httpWebRequest);
}
/// <summary>
/// Make the actual HTTP request to the Bitcoin RPC interface.
/// </summary>
/// <param name="walletRequest">The request to make.</param>
/// <returns>The HTTP request object.</returns>
private HttpWebRequest MakeHttpRequest(DaemonRequest walletRequest)
{
var webRequest = (HttpWebRequest)WebRequest.Create(RpcUrl);
webRequest.Credentials = new NetworkCredential(RpcUser, RpcPassword);
// Important, otherwise the service can't deserialse your request properly
webRequest.ContentType = "application/json-rpc";
webRequest.Method = "POST";
webRequest.Timeout = _timeout;
_logger.Verbose("tx: {0}", Encoding.UTF8.GetString(walletRequest.GetBytes()).PrettifyJson());
byte[] byteArray = walletRequest.GetBytes();
webRequest.ContentLength = byteArray.Length;
try
{
using (Stream dataStream = webRequest.GetRequestStream())
{
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
}
}
catch (WebException webException)
{
throw _rpcExceptionFactory.GetRpcException(webException);
}
catch (Exception exception)
{
throw _rpcExceptionFactory.GetRpcException("An unknown exception occured while making json request.", exception);
}
return webRequest;
}
/// <summary>
/// Get the JSON RPC response for the given HTTP request.
/// </summary>
/// <typeparam name="T">The type to deserialize the response result to.</typeparam>
/// <param name="httpWebRequest">The web request.</param>
/// <returns>A JSON RPC response with the result deserialized as the given type.</returns>
private DaemonResponse<T> GetRpcResponse<T>(HttpWebRequest httpWebRequest)
{
string json = GetJsonResponse(httpWebRequest);
_logger.Verbose("rx: {0}", json.PrettifyJson());
try
{
return JsonConvert.DeserializeObject<DaemonResponse<T>>(json);
}
catch (JsonException jsonEx)
{
throw new Exception("There was a problem deserializing the response from the coin wallet.", jsonEx);
}
catch (Exception exception)
{
throw _rpcExceptionFactory.GetRpcException("An unknown exception occured while reading json response.", exception);
}
}
/// <summary>
/// Gets the JSON response for the given HTTP request.
/// </summary>
/// <param name="httpWebRequest">The HTTP request send to the Bitcoin RPC interface.</param>
/// <returns>The raw JSON string.</returns>
private string GetJsonResponse(HttpWebRequest httpWebRequest)
{
try
{
WebResponse webResponse = httpWebRequest.GetResponse();
// Deserialize the json response
using (var stream = webResponse.GetResponseStream())
{
if (stream == null)
return string.Empty;
using (var reader = new StreamReader(stream))
{
string result = reader.ReadToEnd();
reader.Close();
return result;
}
}
}
catch (ProtocolViolationException protocolException)
{
throw _rpcExceptionFactory.GetRpcException(protocolException);
}
catch (WebException webException)
{
var response = webException.Response as HttpWebResponse;
if (response == null)
throw _rpcExceptionFactory.GetRpcException(webException);
var error = ReadJsonError(response); // try to read the error response.
if(error != null)
throw _rpcExceptionFactory.GetRpcErrorException(error); //throw the error.
else
throw _rpcExceptionFactory.GetRpcException("An unknown exception occured while reading json response.", webException);
}
catch (Exception exception)
{
throw _rpcExceptionFactory.GetRpcException("An unknown exception occured while reading json response.", exception);
}
}
private RpcErrorResponse ReadJsonError(HttpWebResponse response)
{
using (var stream = response.GetResponseStream())
{
if (stream == null)
return null;
using (var reader = new StreamReader(stream))
{
string data = reader.ReadToEnd(); // read the error response.
// we actually expect a json error response here, but it seems some coins may return non-json responses.
try
{
var error = JsonConvert.DeserializeObject<RpcErrorResponse>(data); // so let's try parsing the error response as json.
return error;
}
catch (JsonException e) // if we can't parse the error response as json
{
throw _rpcExceptionFactory.GetRpcException(data, e); // then just use the error text.
}
catch (Exception exception)
{
throw _rpcExceptionFactory.GetRpcException("An unknown exception occured while reading json response.", exception);
}
}
}
}
}
}