Web3js
我可以使用 Nethereum 獲得原始交易嗎?
如何使用 web3/Nethereum 在區塊鏈上獲取原始交易?
可以在此處查看原始交易的範例: https ://etherscan.io/getRawTx?tx=0x3866bd52ab1273b9b014e81f981fb35e6b1ec3254514e09c604921ecc17a6ad6
Nethereum 不直接支持它,但所有版本的 Parity 錢包都支持它,最近版本的 geth 也支持它,因此我們可以直接發送 RPC 呼叫而無需使用任何 Nethereum RPC 呼叫。有問題的 RPC 方法是
eth_getRawTransactionByHash
.像這樣
/// <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; }
使用了以下幫助類的地方
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; } }
如果您不幸使用不支持的節點
eth_getRawTransactionByHash
(舊的 geth 版本),您仍然可以使用以下函式從可用數據構造原始十六進制來獲取原始十六進制。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); }