Проблем с публикуването на 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 чрез stringbuilder, това, което трябва да направите вместо това, е да създадете някои разумни 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