Solnet: [Bug] Serialization/Deserialization concepts of Transaction

Describe the bug I think I have problem understanding the serialization/deserialization concepts of Transaction I try to run the code below but the result is : Transaction signature verification failure

var tbuilder = new TransactionBuilder()
                .SetRecentBlockHash((await rpcClient.GetRecentBlockHashAsync()).Result.Value.Blockhash)
                .SetFeePayer(buyer.Account)
                .AddInstruction(MemoProgram.NewMemo(owner.PublicKey, "this is a test"));
            var signlaterwithmessage = tbuilder.CompileMessage();
            //this happens out of csharp code on the browser , but even doing it in csharp doesn't help as it doesn't work
            var clienttransacton = Transaction.Populate(Message.Deserialize(signlaterwithmessage)); 
            clienttransacton.Sign(buyer.Account);      
            var clientdto = clienttransacton.Serialize();
            //this happens in csharp code side
            var newtx = Transaction.Deserialize(clientdto);
            newtx.Sign(owner);
            RequestResult<string> tx2 = await rpcClient.SendTransactionAsync(newtx.Serialize());
            Console.WriteLine(tx2.Result);
            var subscription2 = await streamingRpcClient.SubscribeSignatureAsync(tx2.Result,
                (subscriptionState, response) => {
                }, Commitment.Finalized);
            while (true)
            {
                Console.WriteLine(tx2.Reason);  //Transaction signature verification failure 
                Console.WriteLine(subscription2.State);
                System.Threading.Thread.Sleep(1000);
            }

Expected behavior I think it should work

Desktop (please complete the following information):

  • OS: Windows 10
  • Solnet version 4.2 [as 4.17 doesn’t work with Solana.Metaplex that we use]

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Comments: 17 (14 by maintainers)

Most upvoted comments

I still don’t understand why this example does not work

  var tbuilder1 = new TransactionBuilder()
                .SetRecentBlockHash((await rpcClient.GetRecentBlockHashAsync()).Result.Value.Blockhash)
                .SetFeePayer(buyer.Account.PublicKey)
                .AddInstruction(MemoProgram.NewMemo(creater.Account, "hello world"));
            var binmessge2  = tbuilder1.CompileMessage();
            var gg = Transaction.Populate(Message.Deserialize(binmessge2));
            gg.Sign(buyer.Account);
            gg.Sign(creater.Account);
 RequestResult<string> tx = await rpcClient.SendTransactionAsync(gg.Serialize());    //Transaction signature verification failure 

As you said ‘A transaction is a message along with the compact array of it’s signatures.’. when I populate a transaction from a message with no signature , it means i am creating a transaction with empty signature array. so when i do gg.Sign(buyer.Account);, I am adding the signature of buyer to the array of it's signatures. and the same with gg.Sign(creater.Account);. but transaction still fails with Transaction signature verification

In this example what does gg.Sign(buyer.Account) return? Internally that method also calls VerifySignatures() which verifies that all of the signatures present are valid, this is just to make sure what’s going on. I’m going to attempt to replicate this.

This is the full example 0-100 :

            IRpcClient rpcClient2 = ClientFactory.GetClient(Cluster.TestNet);
            var wallet1 = new Wallet(WordCount.Fifteen, WordList.English);
            var wallet2 =  new Wallet(WordCount.Fifteen, WordList.English);
            var tbuilder1 = new TransactionBuilder()
                .SetRecentBlockHash((await rpcClient2.GetRecentBlockHashAsync()).Result.Value.Blockhash)
                .SetFeePayer(wallet1.Account.PublicKey)
                .AddInstruction(MemoProgram.NewMemo(wallet2.Account, "hello world"));
            var binmessge2  = tbuilder1.CompileMessage();
            var gg = Transaction.Populate(Message.Deserialize(binmessge2));
            var result1 =     gg.Sign(wallet1.Account);
            var result2 =   gg.Sign(wallet2.Account);
            RequestResult<string> txxx = await rpcClient2.SendTransactionAsync(gg.Serialize());    //Transaction signature verification failure         

result1 = true result2=true

We’ve replicated this and narrowed it down to an edge case in the Transaction.Populate method. A fix for this will be merged shortly and v0.5.1 will be released. Thanks for the help narrowing this down.

@nort3x @Neo-vortex Any updates on this since my last message? Need to figure out if something actually needs to be fixed.