Проблема с размещением CREATERAWTRANSACTION в Bitcoin Core через JSON-RPC

Я пытаюсь отправить сообщение на локальный полный узел биткойнов через 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}]"]}

Я по-прежнему получаю то же сообщение об ошибке, что и раньше (см. Выше).


person oshirowanen    schedule 03.12.2018    source источник
comment
Вместо того, чтобы пытаться связать JSON с помощью построителя строк, вам следует создать несколько разумных объектов DTO для заполнения данными, а затем использовать Newtonsoft JSON для сериализации этих объектов в действительный JSON.   -  person Robert Harvey    schedule 04.12.2018
comment
@RobertHarvey - Как и предполагалось, я пробовал использовать объекты, а затем использовал Newtonsoft.Json для сериализации его в действительный json. Но я все еще получаю то же сообщение об ошибке.   -  person oshirowanen    schedule 05.12.2018


Ответы (1)


Я думаю, что проблема в том, что ваш params массив получает двойную сериализацию, поэтому сервер не знает, как интерпретировать запрос. Я понимаю, что ваш JSON выглядит так же, как в примере, поэтому я вполне могу ошибиться здесь; Я определенно не эксперт по использованию API Bitcoin Core. Однако я просмотрел исходный код сторонней библиотеки, которая должна быть совместимой с Bitcoin Core, и, похоже, он не выполняет двойную сериализацию параметров для запроса createrawtransation. Это заставляет меня думать, что проблема заключается в параметрах с двойной сериализацией.

Чтобы исправить это, попробуйте следующее:

  1. Измените подпись вашего существующего RequestServer метода следующим образом:

    public static string RequestServer(string methodName, List<string> parameters)
    

    к этому:

    public static string RequestServer(string methodName, List<JToken> parameters)
    
  2. Создайте новую перегрузку метода RequestServer, используя старую подпись, которая вызывает существующую, которую вы только что изменили. Это позволит другим вашим методам, которые уже работают (например, sendtoaddress и listunspent), продолжать работать без изменений.

    public static string RequestServer(string methodName, List<string> parameters)
    {
        return RequestServer(methodName, parameters.Select(p => new JValue(p)).ToList<JToken>());
    }
    
  3. Наконец, измените код в вашем 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}]"]}
person Brian Rogers    schedule 05.12.2018
comment
Это работает, Брайан. Спасибо за помощь. Не уверен, использовал ли я неправильный пример структуры json, или если документация неверна, так как я не смог использовать curl для запуска данного примера. Но ваши изменения, похоже, работают отлично. Спасибо! - person oshirowanen; 06.12.2018