Reputation: 1
I am trying to sign a transaction on the Tron network using C#. I am able to send the initial request, receive the response and respond to it, however the response suggests that the transaction was signed by a different account address.
To try and explain a bit further, I am trying to withdraw the voting rewards, I am doing this on the Shasta test net, the idea is to have a function sign data universally so I don't need to create a new function for each transaction type, so this function is just the sign function, data to sign goes in, signature comes out both as strings. The JSON and HTTP stuff is done outside of this.
Here is my code so far:
public TronSigner(string privateKeyHex)
{
privateKey = privateKeyHex;
byte[] privateKeyBytes = ByteArrary.HexToByteArray(privateKey);
privKey = new NBitcoin.Key(privateKeyBytes, -1, false);
}
public string Sign(string data)
{
byte[] dataBytes = ByteArrary.HexToByteArray(data);
byte[] hashedData = Keccak256(dataBytes);
NBitcoin.Crypto.ECDSASignature eCDSASignature = privKey.Sign(new uint256(hashedData));
byte[] signatureBytes = eCDSASignature.ToCompact();
signatureBytes = AddByteToByteArray(signatureBytes, 0x41, true);
string signatureString = ByteArrary.ToHex(signatureBytes);
Debug.WriteLine("Signature: " + signatureString);
return signatureString;
}
byte[] AddByteToByteArray(byte[] byteArray, byte value = 0x41, bool addToEnd = false)
{
byte[] newArray = new byte[byteArray.Length + 1];
if (addToEnd)
{
Array.Copy(byteArray, 0, newArray, 0, byteArray.Length);
newArray[newArray.Length - 1] = value;
}
else
{
newArray[0] = value;
Array.Copy(byteArray, 0, newArray, 1, byteArray.Length);
}
return newArray;
}
Note, the signer is initialized and then the command is ran from external script triggered by a button press(I am using VS2022 to create this)
I have spared the JSON and HTTP code as testing has confirmed this to be working due to responses, but if it makes any difference, my JSON string is built simply by creating a string and using variables where applicable, in the case here, the signature is just as string as shown in the code above.
Here is some data sent and the responses: Data sent to withdrawbalance
{"owner_address":"TPwyWCDK3bH6LuMLXa5n1p5tU9xDnRYgsq", "visible": true}
Response:
{"visible":true,"txID":"3fcb0c072920451c14a49b64aa216f3fb79d2e22c0f19d2a8e17017c0eb621d6","raw_data":{"contract":[{"parameter":{"value":{"owner_address":"TPwyWCDK3bH6LuMLXa5n1p5tU9xDnRYgsq"},"type_url":"type.googleapis.com/protocol.WithdrawBalanceContract"},"type":"WithdrawBalanceContract"}],"ref_block_bytes":"a4c7","ref_block_hash":"124cdadd5d6c5021","expiration":1734437376000,"timestamp":1734437316598},"raw_data_hex":"0a02a4c72208124cdadd5d6c50214080a098a4bd325a53080d124f0a34747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e576974686472617742616c616e6365436f6e747261637412170a1541995613e5afaf3c4df3c53c141f59c57dceb5d0e170f6cf94a4bd32"}
I then sign the transactionID(txID from the response) to generate the signature and then send off the following JSON response to broadcasttransaction:
{"raw_data":{"contract":[{"parameter":{"value":{"owner_address":"TPwyWCDK3bH6LuMLXa5n1p5tU9xDnRYgsq"},"type_url":"type.googleapis.com/protocol.WithdrawBalanceContract"},"type":"WithdrawBalanceContract"}],"ref_block_bytes":"a4c7","ref_block_hash":"124cdadd5d6c5021","expiration":1734437376000,"timestamp":1734437316598},"raw_data_hex":"0a02a4c72208124cdadd5d6c50214080a098a4bd325a53080d124f0a34747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e576974686472617742616c616e6365436f6e747261637412170a1541995613e5afaf3c4df3c53c141f59c57dceb5d0e170f6cf94a4bd32","signature":["304402202e877a87decd3a70ec0cabbf0a243baaf844462aedc30d9f543ea7033d5e62ab02207b3f614c01b8d1312605232174f41bd799c79c4224d244e399791c"],"txID":"3fcb0c072920451c14a49b64aa216f3fb79d2e22c0f19d2a8e17017c0eb621d6","visible":true}
Response from Tron:
{"code":"SIGERROR","txid":"3fcb0c072920451c14a49b64aa216f3fb79d2e22c0f19d2a8e17017c0eb621d6","message":"Validate signature error: java.lang.IllegalArgumentException: Invalid point compression"}
A different response with a similar message, only the transactionID and in turn signature changes in the request sent:
{"code":"SIGERROR","txid":"bb037464b014fcac4d44ba13c13a55a0cbdf15c9d3be089b6f16303680700e81","message":"Validate signature error: 3044022074b28bfd50813c6f33ec7ba729b0bc70d8226a4decc1b3094519db85723402c30220487301cff96c2363820157eef3d61792ec4e9c05546af18606801c is signed by TFqFg48KSVoTNv23Tdwa7wQ9qM4WJqfVSs but it is not contained of permission."}
As you can see, it appears to not be accepting my signature properly. But I am very unsure as to where I am going wrong with this. I am aware that there are various other libraries around that package all the sending, receiving, signing etc but none some to be for C# and I am unable to replicate their processes for the signing. It is also unclear in the API references and docs which part of the received response I am supposed to be signing here.
If you do have any solutions for this, please let me know, please note that I am using various libraries in an effort to resolve this, so any code examples may need to include full qualified names such "NBitcoin.Crypto.ECDSASignature" as seen in my code. Here is my current using list at the moment, not all are being used, but they are there:
using NBitcoin;
using NBitcoin.Secp256k1;
using Org.BouncyCastle.Crypto.Digests;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using TronDotNet.Node;
using TronDotNet.Objects;
using TronNet;
using TronNet.Crypto;
using TronNet.Protocol;
using static TronNet.Protocol.Transaction.Types;
Thanks
Upvotes: 0
Views: 131