Wie erhalte ich eine Rohtransaktion für eine Transaktion auf der Blockchain mit web3/Nethereum?
Ein Beispiel für eine Rohtransaktion ist hier zu sehen: https://etherscan.io/getRawTx?tx=0x3866bd52ab1273b9b014e81f981fb35e6b1ec3254514e09c604921ecc17a6ad6
Das Nethereum unterstützt es nicht direkt, aber alle Versionen von Parity Wallet unterstützen es und neuere Versionen von Geth unterstützen es auch, sodass wir einen RPC-Aufruf direkt senden können, ohne einen Nethereum-RPC-Aufruf zu verwenden. Die fragliche RPC-Methode ist eth_getRawTransactionByHash
.
Wie so
/// <summary>
/// Attempt to get the raw hex directly from the geth node
/// </summary>
/// <param name="txid"></param>
/// <returns></returns>
internal string GetRawTxHexByGetRawTransactionByHash(string txid)
{
// Extract the HttpClient object from the web3 object
FieldInfo field = typeof(Nethereum.JsonRpc.Client.RpcClient).GetField("_httpClient", BindingFlags.NonPublic | BindingFlags.Instance);
HttpClient httpClient = (HttpClient)field.GetValue(_web3.Client);
// Make the post request to our own Geth node and deserialize the result
RpcRequest rpcRequest = new RpcRequest
{
method = "eth_getRawTransactionByHash",
@params = new string[] { txid },
};
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri("/", UriKind.Relative));
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
HttpResponseMessage resp = httpClient.SendAsync(requestMessage).Result;
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning($"Failed to call to eth_getRawTransactionByHash for txid={txid}.");
return null;
}
EthGetRawTransactionByHashResponse txResp =
JsonConvert.DeserializeObject<EthGetRawTransactionByHashResponse>(resp.Content.ReadAsStringAsync().Result);
return txResp.result;
}
wobei die folgenden Hilfsklassen verwendet wurden
private abstract class Web3RpcBase
{
public int id { get; set; } = 67;
public string jsonrpc { get; set; } = "2.0";
}
private class RpcRequest : Web3RpcBase
{
public string method { get; set; }
public string[] @params { get; set; }
}
private class EthGetRawTransactionByHashResponse : Web3RpcBase
{
public string result { get; set; }
}
Wenn Sie so unglücklich sind, einen Knoten zu verwenden, der eth_getRawTransactionByHash
(alte Geth-Versionen) nicht unterstützt, können Sie immer noch das Roh-Hex erhalten, indem Sie die folgenden Funktionen verwenden, um das Roh-Hex aus verfügbaren Daten zu erstellen.
private abstract class Web3RpcBase
{
public int id { get; set; } = 67;
public string jsonrpc { get; set; } = "2.0";
}
private class RpcRequest : Web3RpcBase
{
public string method { get; set; }
public string[] @params { get; set; }
}
private class EthGetTransactionByHashResponseResult
{
public string blockHash { get; set; }
public string blockNumber { get; set; }
public string from { get; set; }
// This is what we usually call gas limit
public string gas { get; set; }
public string gasPrice { get; set; }
public string hash { get; set; }
// This is what we usually call data
public string input { get; set; }
public string nonce { get; set; }
public string to { get; set; }
public string transactionIndex { get; set; }
public string value { get; set; }
public string v { get; set; }
public string r { get; set; }
public string s { get; set; }
}
private class EthGetTransactionByHashResponse : Web3RpcBase
{
public EthGetTransactionByHashResponseResult result { get; set; }
}
private class EthGetRawTransactionByHashResponse : Web3RpcBase
{
public string result { get; set; }
}
/// <summary>
/// Should only be used for Ethereum, not BTC.
/// </summary>
/// <param name="hexString"></param>
/// <returns></returns>
private static byte[] HexToByteArrayWrapper(string hexString)
{
// Prevent "0" from becoming "00" represented by[0]. "0" should be[], and 1 should be [0x01]
if (string.IsNullOrEmpty(hexString) || hexString == "0" || hexString == "0x0")
{
return Array.Empty<byte>();
}
return hexString.HexToByteArray();
}
/// <summary>
/// Attempt to get the raw hex directly from the geth node
/// </summary>
/// <param name="txid"></param>
/// <returns></returns>
internal string GetRawTxHexByGetRawTransactionByHash(string txid)
{
// Extract the HttpClient object from the web3 object
FieldInfo field = typeof(Nethereum.JsonRpc.Client.RpcClient).GetField("_httpClient", BindingFlags.NonPublic | BindingFlags.Instance);
HttpClient httpClient = (HttpClient)field.GetValue(_web3.Client);
// Make the post request to our own Geth node and deserialize the result
RpcRequest rpcRequest = new RpcRequest
{
method = "eth_getRawTransactionByHash",
@params = new string[] { txid },
};
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri("/", UriKind.Relative));
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
HttpResponseMessage resp = httpClient.SendAsync(requestMessage).Result;
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning($"Failed to call to eth_getRawTransactionByHash for txid={txid}.");
return null;
}
EthGetRawTransactionByHashResponse txResp =
JsonConvert.DeserializeObject<EthGetRawTransactionByHashResponse>(resp.Content.ReadAsStringAsync().Result);
return txResp.result;
}
/// <summary>
/// Attempt to reconstruct the raw hex by getting the transaction info from the geth node
/// </summary>
/// <param name="txid"></param>
/// <returns></returns>
internal string GetRawTxHexByGetTransactionByHash(string txid)
{
// Extract the HttpClient object from the web3 object
FieldInfo field = typeof(Nethereum.JsonRpc.Client.RpcClient).GetField("_httpClient", BindingFlags.NonPublic | BindingFlags.Instance);
HttpClient httpClient = (HttpClient)field.GetValue(_web3.Client);
// Make the post request to our own Geth node and deserialize the result
RpcRequest rpcRequest = new RpcRequest
{
method = "eth_getTransactionByHash",
@params = new string[] { txid },
};
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri("/", UriKind.Relative));
requestMessage.Content = new StringContent(JsonConvert.SerializeObject(rpcRequest), Encoding.UTF8, "application/json");
HttpResponseMessage resp = httpClient.SendAsync(requestMessage).Result;
if (!resp.IsSuccessStatusCode)
{
_logger.LogWarning($"Failed to call to eth_getTransactionByHash for txid={txid}.");
return null;
}
EthGetTransactionByHashResponse txResp = JsonConvert.DeserializeObject<EthGetTransactionByHashResponse>(resp.Content.ReadAsStringAsync().Result);
// Create the signature object to insert in the transaction (needed to support chain IDs above 0xff)
Org.BouncyCastle.Math.BigInteger r = new Org.BouncyCastle.Math.BigInteger(txResp.result.r.Substring(2), 0x10);
Org.BouncyCastle.Math.BigInteger s = new Org.BouncyCastle.Math.BigInteger(txResp.result.s.Substring(2), 0x10);
byte[] v = new byte[] { byte.Parse(txResp.result.v.Substring(2), System.Globalization.NumberStyles.HexNumber, CultureInfo.GetCultureInfo("en-US")) };
EthECDSASignature signature = new Nethereum.Signer.EthECDSASignature(r, s, v);
// Create unsigned transaction object
Nethereum.Signer.Transaction transaction = new Nethereum.Signer.Transaction(
HexToByteArrayWrapper(txResp.result.nonce),
HexToByteArrayWrapper(txResp.result.gasPrice),
HexToByteArrayWrapper(txResp.result.gas),
HexToByteArrayWrapper(txResp.result.to),
HexToByteArrayWrapper(txResp.result.value),
HexToByteArrayWrapper(txResp.result.input),
Array.Empty<byte>(),
Array.Empty<byte>(),
0);
transaction.SetSignature(signature);
// Get the hex format of the RLP-encoded transaction, the rawtx
string rawTxHex = "0x" + transaction.GetRLPEncoded().Select(x => x.ToString("x2", NumberFormatInfo.InvariantInfo)).Aggregate((acc, elem) => acc + elem);
return rawTxHex;
}
/// <summary>
/// Given a txid return the RLP-encoded raw hex that represents the transcation on the blockchain.
/// </summary>
/// <param name="txid"></param>
/// <returns></returns>
internal string GetRawHex(string txid)
{
// First try to get the raw hex directly from the node
string res = GetRawTxHexByGetRawTransactionByHash(txid);
if (!String.IsNullOrWhiteSpace(res) && res != "0x")
{
return res;
}
// If that did not work, attempt to construct it from tx details
_logger.LogInformation("Failed to get raw hex from node, calculating it locally instead.");
return GetRawTxHexByGetTransactionByHash(txid);
}