3

I am writing full custom wallet software for Tezos in C# and got the following raw transaction:

UnsignedHex: 008f1d96e2783258ff663f03dacfe946c026a5d194c73d1987b3da73fadea7d46b01549a0cde570a49b9200680341a6c78e87635480f940adc85a001f44eac02010238244743822921db1661fc7306c3044faac1e373906fdb5582d9454352b3b31c6c01549a0cde570a49b9200680341a6c78e87635480ff00bdd85a0018452ac02e0a712000153957451d3cc83a71e26b65ea2391a1b16713d2d00

HashPreimage: 03008f1d96e2783258ff663f03dacfe946c026a5d194c73d1987b3da73fadea7d46b01549a0cde570a49b9200680341a6c78e87635480f940adc85a001f44eac02010238244743822921db1661fc7306c3044faac1e373906fdb5582d9454352b3b31c6c01549a0cde570a49b9200680341a6c78e87635480ff00bdd85a0018452ac02e0a712000153957451d3cc83a71e26b65ea2391a1b16713d2d00

Hash: 8541434de6c77aaa3e6a4d4e341717a5c96b14e990183fd33e8157ef53f38579

Signed: 008f1d96e2783258ff663f03dacfe946c026a5d194c73d1987b3da73fadea7d46b01549a0cde570a49b9200680341a6c78e87635480f940adc85a001f44eac02010238244743822921db1661fc7306c3044faac1e373906fdb5582d9454352b3b31c6c01549a0cde570a49b9200680341a6c78e87635480ff00bdd85a0018452ac02e0a712000153957451d3cc83a71e26b65ea2391a1b16713d2d0049ff4a47599e0c65447182eca4c900893a74de3ca24c26708fc9d4bf79891ca384b0d5f98618bc3acec4f0c6bffd002de6fd839a418f47e83404aed285fc0235

When I try to inject the raw signed transaction ('Signed') with the node rpc I get the response:
[
{
"kind": "temporary",
"id": "failure",
"msg": "Error while applying operation oozXCEKTUFcw8PHcrskvuWphiH1kNz12eohX1mp8AKinJU9fPYS:\nrefused (Error:\n The operation signature is invalid\n)"
}
]

I can verify the signature (secp256k1) of the transaction against third party tools and it verifies (that is purely that the 'Hash' value is signed correctly). I used the node rpc 'Forge' endpoint to verify the correct serialization with following JSON:
{ "branch":"BKiXcfN1ZTXnNNbTWSRArSWzVFc6om7radWq5mTqGX6rY4P2Uhe",
"contents":[{
"kind":"reveal",
"fee":"1300",
"gas_limit":"10100",
"storage_limit": "300",
"public_key":"sppk7ZjMvKgUwnjYZKoQEYaNhorr1NJxmqgHLjExsP3uq98XKChFius",
"counter":"2622172",
"source":"tz2G2Zy3sgYCuh1EQqrx9Q62CcpSkgcLPPDt"},{
"kind":"transaction",
"fee":"1520",
"gas_limit":"10500",
"storage_limit": "300",
"amount":"300000",
"counter":"2622173",
"destination":"tz2FwBnXhuXvPAUcr1aF3uX84Z6JELxrdYxD",
"source":"tz2G2Zy3sgYCuh1EQqrx9Q62CcpSkgcLPPDt"}]
}

And also was able to verify UnsignedRaw => Hash with python code:

from pyblake2 import blake2b
import binascii
operation=binascii.unhexlify('008f1d96e2783258ff663f03dacfe946c026a5d194c73d1987b3da73fadea7d46b01549a0cde570a49b9200680341a6c78e87635480f940adc85a001f44eac02010238244743822921db1661fc7306c3044faac1e373906fdb5582d9454352b3b31c6c01549a0cde570a49b9200680341a6c78e87635480ff00bdd85a0018452ac02e0a712000153957451d3cc83a71e26b65ea2391a1b16713d2d00')
h = blake2b(digest_size=32)
h.update(b'\x03' + operation)
digest = h.digest()
print(binascii.hexlify(digest))
=> 8541434de6c77aaa3e6a4d4e341717a5c96b14e990183fd33e8157ef53f38579

It almost seems like a bad error message. Can anyone help me figuring out what the problem with my transaction is?

P.s. in a second run with the same software I was able to generate a fully valid transaction: 007fc1ecd6a56046aaaeab78bbfa1c889eb8f502b66c720bf1ac3b40ee9ecf6d6b01549a0cde570a49b9200680341a6c78e87635480f940adc85a001f44eac02010238244743822921db1661fc7306c3044faac1e373906fdb5582d9454352b3b31c6c01549a0cde570a49b9200680341a6c78e87635480ff00bdd85a0018452ac02c09a0c000153957451d3cc83a71e26b65ea2391a1b16713d2d00231176ec0cf55e3f04d5de633c33b38a6370ad2c37fd31c4c45e12ec9f1466753c8137f5dee8105bd5cc3d4ce780f51ec01d5adb21893e75eef4b960189818a3

A third run was again deemed invalid.

6

Make sure you revealed public key just once and make sure you properly incement counter.

Also, take a look at Netezos, C# Tezos SDK. Here is an example of how to forge, sign and send a transaction:

var key = Key.FromBase58("edskS9DjUKo8ogBBU8LeU...");

var rpc = new TezosRpc("https://rpc.tzkt.io/carthagenet/");
var head = await rpc.Blocks.Head.Hash.GetAsync<string>();
var counter = await rpc.Blocks.Head.Context.Contracts[key.PubKey.Address].Counter.GetAsync<int>();

var forge = new LocalForge();
var bytes = await forge.ForgeOperationAsync(head, new TransactionContent
{
    Source = key.PubKey.Address,
    Counter = ++counter,
    Fee = 10_000,
    GasLimit = 20_000,
    StorageLimit = 20_000,
    Amount = 1_000_000,
    Destination = "tz1KhnTgw...",
});

var sig = key.Sign(new byte[] { 3 }.Concat(bytes).ToArray());
var op = bytes.Concat(sig.ToBytes()).ToArray();

var tx = await rpc.Inject.Operation.PostAsync(Hex.Convert(op));

If it doesn't work even with Netezos, check if your RPC node is synced and up to date.

| improve this answer | |
  • Based on the JSON in the original question and the status of the sender address link, does counter & reveal look ok? – Felix Laufenberg Apr 20 at 6:38
  • 1
    I suggest you use TzKT explorer, because it shows more useful info, including operations failed in mempool tzkt.io/tz2G2Zy3sgYCuh1EQqrx9Q62CcpSkgcLPPDt/operations. So, what I see is two failed in mempool operations with counters ..72, ..73 from 12:01:49 UTC. In your question I see JSON with the same counters BUT with the branch tzkt.io/BKiXcfN1ZTXnNNbTWSRArSWzVFc6om7radWq5mTqGX6rY4P2Uhe - from 12:55:12 UTC. So, I assume that you tried to send the operations with the counters ..72, ..73 which had already been in mempool. – Groxan Apr 20 at 10:07
  • Generally speaking, your JSON looks fine, but the fact that there were failed "clones" in the mempool may cause the error you described. – Groxan Apr 20 at 10:13
  • Thanks for the quick response. The transaction in the mempool is the successfull transaction that I was referring to, however it is now invalid because the branch is outdated, if that were to block future transactions it would complete block the account and should be rendered a critical bug in the Tezos node, so I don't think that it should be the cause of the error. – Felix Laufenberg Apr 20 at 10:27
  • Unfortunately I won't be able to test signing with Netezos since I retrieve the signature from an HSM and can not get access to the private key. – Felix Laufenberg Apr 21 at 5:14
3

https://github.com/bitcoin-core/secp256k1/blob/d644dda5c9dbdecee52d1aa259235510fdc2d4ee/include/secp256k1.h#L483-L513

There are two valid EC points that correspond to the same signature, but some blockchains restrict the usage to one of those two EC points to avoid malleability (= it is easy to convert the signature to the other signature value, and thus create a second valid transaction which would have a different transaction id without haveing access to the private key).

Edit: The corresponding valid transaction is:
008f1d96e2783258ff663f03dacfe946c026a5d194c73d1987b3da73fadea7d46b01549a0cde570a49b9200680341a6c78e87635480f940adc85a001f44eac02010238244743822921db1661fc7306c3044faac1e373906fdb5582d9454352b3b31c6c01549a0cde570a49b9200680341a6c78e87635480ff00bdd85a0018452ac02e0a712000153957451d3cc83a71e26b65ea2391a1b16713d2d0049ff4a47599e0c65447182eca4c900893a74de3ca24c26708fc9d4bf79891ca384b0d5f98618bc3acec4f0c6bffd002de6fd839a418f47e83404aed285fc0235
This was created without re-signing and without access to the private key.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.