Search code examples
c#jsonjson.netbitcoinjson-rpc

Unable to post GETBLOCKHASH to Bitcoin Core via JSON-RPC


The following code works fine for the most part:

public static string RequestServer(string methodName, List<string> parameters)
{
    // Use the values you specified in the bitcoin server command line
    string ServerIp = "http://localhost.:8332";
    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 responseValue = string.Empty;

    // Configure request type
    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 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();

    // deserialze the response
    StreamReader sReader = null;
    WebResponse webResponse = webRequest.GetResponse();
    sReader = new StreamReader(webResponse.GetResponseStream(), true);
    responseValue = sReader.ReadToEnd();

    var data = JsonConvert.DeserializeObject(responseValue).ToString();
    
    return data;
}

I can then use a methodName such as getnewaddress to get data back from the server:

static void Main(string[] args)
{
    Console.WriteLine(RequestServer("getnewaddress", new List<string>(){"","legacy"}));
}

That will return something like this:

{
  "result": "1EWJkGrirdhXpduoNdccxaCx7syqWHuDcK",
  "error": null,
  "id": "1"
}

The above methodName works fine when using the terminal too:

bitcoin@desktop:~/Downloads/bitcoin-0.20.0-x86_64-linux-gnu/bitcoin-0/bin$ ./bitcoin-cli getnewaddress "" "legacy"
1EWJkGrirdhXpduoNdccxaCx7syqWHuDcK
bitcoin@desktop:~/Downloads/bitcoin-0.20.0-x86_64-linux-gnu/bitcoin-0.20.0/bin$ 

I can use a few methodNames the same way and they work fine. However, when I use getblockhash:

static void Main(string[] args)
{
    Console.WriteLine(RequestServer("getblockhash", new List<string>(){"0"}));
}

It gives me the following error:

bitcoin@desktop:~/Code/blockchain-app$ dotnet run
Unhandled exception. System.Net.WebException: The remote server returned an error: (500) Internal Server Error.
   at System.Net.HttpWebRequest.GetResponse()
   at blockchain-app.Program.RequestServer(String methodName, List`1 parameters) in /home/bitcoin/Code/blockchain-app/Program.cs:line 72
   at blockchain-app.Program.Main(String[] args) in /home/bitcoin/Code/blockchain-app/Program.cs:line 29
bitcoin@desktop:~/Code/blockchain-app$

When debugging, the error happens on this line:

WebResponse webResponse = webRequest.GetResponse();

If I try to check the output manually using that methodName in the terminal such as the following, it works fine:

bitcoin@desktop:~/Downloads/bitcoin-0.20.0-x86_64-linux-gnu/bitcoin-0.20.0/bin$ ./bitcoin-cli getblockhash 0
000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
bitcoin@desktop:~/Downloads/bitcoin-0.20.0-x86_64-linux-gnu/bitcoin-0.20.0/bin$

The request example structures look the same to me other than the methodName and the parameters sent:

https://bitcoincore.org/en/doc/0.20.0/rpc/wallet/getnewaddress/ https://bitcoincore.org/en/doc/0.20.0/rpc/blockchain/getblockhash/

Anyone know why this is happening?


Solution

  • I think the problem is that you are passing the wrong parameter type for the height index when calling getblockhash. The documentation you linked to says that it should be a numeric (integer) parameter, but you are passing a string. In contrast, the getnewaddress method uses string parameters, so your existing code works for that.

    If you capture the JSON that is being generated by your RequestServer method, it looks like this:

    {"jsonrpc":"1.0","id":"1","method":"getblockhash","params":["0"]}
    

    But the sample in the documentation for getblockhash looks like this:

    {"jsonrpc":"1.0","id":"curltest","method":"getblockhash","params":[1000]}
    

    Notice that the value in the params array is not quoted in the sample, whereas it is quoted in yours.

    To fix, try the following:

    1. Change the method signature for your existing RequestServer method from this:

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

      to this:

      public static string RequestServer(string methodName, List<JToken> parameters)
      
    2. Create a new overload of the RequestServer method using the old signature which calls the existing one you just changed. This will allow your other method call(s) which already work (e.g. getnewaddress) to keep working with no changes.

      public static string RequestServer(string methodName, List<string> parameters)
      {
          return RequestServer(methodName, parameters.Select(p => new JValue(p)).ToList<JToken>());
      }
      
    3. Change the code which calls getblockhash from this:

      Console.WriteLine(RequestServer("getblockhash", new List<string>() { "0" }));
      

      to this:

      Console.WriteLine(RequestServer("getblockhash", new List<JToken>() { new JValue(0) }));
      

    Note that I answered a similar question for you back in 2018 in which I recommended the same steps 1 and 2 shown above, so you may have already done that part. If so, all you need to do is step 3.