Я пытаюсь отправить сообщение на локальный полный узел биткойнов через json-rpc, но получаю сообщение об ошибке с сервера.
Следуя документации здесь: https://bitcoincore.org/en/doc/0.17.0/rpc/rawtransactions/createrawtransaction/
Я вижу следующий пример структуры для запроса createrawtransaction:
{"jsonrpc": "1.0", "id":"curltest", "method": "createrawtransaction", "params": ["[{\"txid\":\"myid\",\"vout\":0}]", "[{\"address\":0.01}]"] }
Мой код создает следующую структуру, которая, кажется, соответствует структуре примера с bitcoincore.org:
{"jsonrpc":"1.0","id":"1","method":"createrawtransaction","params":["[{\"txid\":\"1a43a1f27c5837d5319a45217aa948a4d39c1d89faf497ce59de5bd570a64a26\",\"vout\":1}]","[{\"2NAZpRsvj9BstxxCDkKoe1FVjmPPxdmvqKj\":0.01}]"]}
Но выдает ошибку:
System.Net.WebException
HResult=0x80131509
Message=The remote server returned an error: (500) Internal Server Error.
Source=RawTransactions
StackTrace:
at RawTransactions.Form1.RequestServer(String methodName, List`1 parameters) in C:\Users\userthree\Documents\Visual Studio 2017\Projects\RawTransactions\RawTransactions\Form1.cs:line 132
at RawTransactions.Form1.button1_Click(Object sender, EventArgs e) in C:\Users\userthree\Documents\Visual Studio 2017\Projects\RawTransactions\RawTransactions\Form1.cs:line 77
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
at System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(MSG& msg)
at System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 reason, Int32 pvLoopData)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 reason, ApplicationContext context)
at System.Windows.Forms.Application.Run(Form mainForm)
at RawTransactions.Program.Main() in C:\Users\userthree\Documents\Visual Studio 2017\Projects\RawTransactions\RawTransactions\Program.cs:line 19
Ниже приведен метод, который я использую для выполнения запроса RPC, который я получил из справки по API здесь:
https://en.bitcoin.it/wiki/API_reference_(JSON-RPC)#.NET_.28C.23.29
public static string RequestServer(string methodName, List<string> parameters)
{
string ServerIp = "http://localhost:18332";
string UserName = "USERNAME";
string Password = "PASSWORD";
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(ServerIp);
webRequest.Credentials = new NetworkCredential(UserName, Password);
webRequest.ContentType = "application/json-rpc";
webRequest.Method = "POST";
string respVal = string.Empty;
JObject joe = new JObject();
joe.Add(new JProperty("jsonrpc", "1.0"));
joe.Add(new JProperty("id", "1"));
joe.Add(new JProperty("method", methodName));
JArray props = new JArray();
foreach (var parameter in parameters)
{
props.Add(parameter);
}
joe.Add(new JProperty("params", props));
// serialize json for the request
string s = JsonConvert.SerializeObject(joe);
byte[] byteArray = Encoding.UTF8.GetBytes(s);
webRequest.ContentLength = byteArray.Length;
Stream dataStream = webRequest.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
StreamReader streamReader = null;
try
{
WebResponse webResponse = webRequest.GetResponse();
streamReader = new StreamReader(webResponse.GetResponseStream(), true);
respVal = streamReader.ReadToEnd();
var data = JsonConvert.DeserializeObject(respVal).ToString();
return data;
}
catch (Exception exp)
{
throw (exp);
}
finally
{
if (streamReader != null)
{
streamReader.Close();
}
}
return string.Empty;
}
Вот моя попытка использовать вышеуказанный метод:
private void button1_Click(object sender, EventArgs e)
{
StringBuilder sb1 = new StringBuilder();
sb1.Append("[{\"");
sb1.Append("txid");
sb1.Append("\":\"");
sb1.Append(Convert.ToString(data["result"][Convert.ToInt32(txtFromJSON.Text)]["txid"]));
sb1.Append("\",\"");
sb1.Append("vout");
sb1.Append("\":");
sb1.Append(Convert.ToString(data["result"][Convert.ToInt32(txtFromJSON.Text)]["vout"]));
sb1.Append("}]");
StringBuilder sb2 = new StringBuilder();
sb2.Append("[{\"");
sb2.Append(Convert.ToString(data["result"][Convert.ToInt32(txtToJSON.Text)]["address"]));
sb2.Append("\":");
sb2.Append(txtAmountToSpend.Text);
sb2.Append("}]");
// {"jsonrpc":"1.0","id":"1","method":"createrawtransaction","params":["[{\"txid\":\"1a43a1f27c5837d5319a45217aa948a4d39c1d89faf497ce59de5bd570a64a26\",\"vout\":1}]","[{\"2NAZpRsvj9BstxxCDkKoe1FVjmPPxdmvqKj\":0.01}]"]}
data = JObject.Parse(RequestServer("createrawtransaction", new List<string>() { Convert.ToString(sb1), Convert.ToString(sb2) }));
MessageBox.Show(Convert.ToString(data));
}
Другие команды, такие как эти, работают:
// {"jsonrpc":"1.0","id":"1","method":"sendtoaddress","params":["2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF","0.1"]}
data = JObject.Parse(RequestServer("sendtoaddress", new List<string>() { "2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF", Convert.ToString(0.1) } ));
Это тоже работает:
// {"jsonrpc":"1.0","id":"1","method":"listunspent","params":[]}
data = JObject.Parse(RequestServer("listunspent", new List<String>() { }));
Мой вопрос:
Что я сделал не так с createrawtransaction?
Обновление 1:
Как было предложено в комментариях, я изменил StringBuilder и теперь использую объекты, а затем сериализую объекты с помощью Newtonsoft.Json.
Вот моя вторая попытка использовать справочный код API из https://en.bitcoin.it/wiki/API_reference_(JSON-RPC)#.NET_.28C.23.29:
private void button1_Click(object sender, EventArgs e)
{
JContainer jArray = new JArray();
JObject jFromTx = new JObject
{
{ "txid", data["result"][Convert.ToInt32(txtFromJSON.Text)]["txid"] },
{ "vout", data["result"][Convert.ToInt32(txtFromJSON.Text)]["vout"] }
};
jArray.Add(jFromTx);
JObject jToTx = new JObject
{
{ Convert.ToString(data["result"][Convert.ToInt32(txtToJSON.Text)]["address"]), Convert.ToDouble(txtAmountToSpend.Text) }
};
JContainer jArray2 = new JArray
{
jToTx
};
string strFrom = JsonConvert.SerializeObject(jArray);
string strTo = JsonConvert.SerializeObject(jArray2);
data = JObject.Parse(RequestServer("createrawtransaction", new List<string>() { strFrom, strTo }));
}
Вот новый сериализованный JSON:
{"jsonrpc":"1.0","id":"1","method":"createrawtransaction","params":["[{\"txid\":\"1a43a1f27c5837d5319a45217aa948a4d39c1d89faf497ce59de5bd570a64a26\",\"vout\":1}]","[{\"2NAZpRsvj9BstxxCDkKoe1FVjmPPxdmvqKj\":0.01}]"]}
По сравнению со старым StringBuilder JSON с моей первой попытки:
{"jsonrpc":"1.0","id":"1","method":"createrawtransaction","params":["[{\"txid\":\"1a43a1f27c5837d5319a45217aa948a4d39c1d89faf497ce59de5bd570a64a26\",\"vout\":1}]","[{\"2NAZpRsvj9BstxxCDkKoe1FVjmPPxdmvqKj\":0.01}]"]}
Я по-прежнему получаю то же сообщение об ошибке, что и раньше (см. Выше).
@RobertHarvey - Как и предполагалось, я пробовал использовать объекты, а затем использовал Newtonsoft.Json для сериализации их в действительный json. Но я все еще получаю то же сообщение об ошибке.





Я считать проблема в том, что ваш массив params получает двойную сериализацию, поэтому сервер не знает, как интерпретировать запрос. Я понимаю, что ваш JSON выглядит так же, как в примере, поэтому я вполне могу ошибиться здесь; Я определенно не эксперт по использованию API Bitcoin Core. Однако я просмотрел исходный код для сторонняя библиотека, который должен быть совместим с Bitcoin Core, и, похоже, он не выполняет двойную сериализацию параметров для запроса createrawtransation. Это заставляет меня думать, что проблема заключается в параметрах с двойной сериализацией.
Чтобы исправить это, попробуйте следующее:
Измените подпись метода для существующего метода RequestServer следующим образом:
public static string RequestServer(string methodName, List<string> parameters)
к этому:
public static string RequestServer(string methodName, List<JToken> parameters)
Создайте новую перегрузку метода RequestServer, используя старую подпись, которая вызывает существующую, которую вы только что изменили. Это позволит другим вашим методам, которые уже работают (например, sendtoaddress и listunspent), продолжать работать без изменений.
public static string RequestServer(string methodName, List<string> parameters)
{
return RequestServer(methodName, parameters.Select(p => new JValue(p)).ToList<JToken>());
}
Наконец, измените код в вашем методе button1_Click, чтобы он не сериализовал jArray и jArray2, а вместо этого передавал их в List<JToken> в RequestServer. Другими словами, измените этот код:
string strFrom = JsonConvert.SerializeObject(jArray);
string strTo = JsonConvert.SerializeObject(jArray2);
data = JObject.Parse(RequestServer("createrawtransaction", new List<string>() { strFrom, strTo }));
к этому:
data = JObject.Parse(RequestServer("createrawtransaction", new List<JToken>() { jArray, jArray2 }));
С этими изменениями RPC JSON для createrawtransaction должен выглядеть следующим образом:
{"jsonrpc":"1.0","id":"1","method":"createrawtransaction","params":[[{"txid":"1a43a1f27c5837d5319a45217aa948a4d39c1d89faf497ce59de5bd570a64a26","vout":1}],[{"2NAZpRsvj9BstxxCDkKoe1FVjmPPxdmvqKj":0.01}]]}
Обратите внимание, что лишние кавычки и обратная косая черта в массиве params исчезли. Сравните с тем, что у вас было раньше:
{"jsonrpc":"1.0","id":"1","method":"createrawtransaction","params":["[{\"txid\":\"1a43a1f27c5837d5319a45217aa948a4d39c1d89faf497ce59de5bd570a64a26\",\"vout\":1}]","[{\"2NAZpRsvj9BstxxCDkKoe1FVjmPPxdmvqKj\":0.01}]"]}
Это работает, Брайан. Спасибо за помощь. Не уверен, использовал ли я неправильный пример структуры json, или если документация неверна, так как мне не удалось использовать curl для запуска данного примера. Но ваши изменения, похоже, работают отлично. Спасибо!
Вместо того, чтобы пытаться связать JSON с помощью построителя строк, вам следует создать несколько разумных объектов DTO для заполнения данными, а затем использовать Newtonsoft JSON для сериализации этих объектов в действительный JSON.