From cadedbcbdf91062ce98cd8824cb008c49d7a62cf Mon Sep 17 00:00:00 2001 From: 10gic Date: Fri, 6 Dec 2024 22:55:56 +0800 Subject: [PATCH 1/7] Support compiling a transaction with partial signatures (#4143) Fix issue #4142 --- .../chains/tw_solana/src/modules/tx_signer.rs | 7 --- .../tests/chains/solana/solana_compile.rs | 53 +++++++++++++++++++ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/rust/chains/tw_solana/src/modules/tx_signer.rs b/rust/chains/tw_solana/src/modules/tx_signer.rs index 5fb421e6876..754fa372efe 100644 --- a/rust/chains/tw_solana/src/modules/tx_signer.rs +++ b/rust/chains/tw_solana/src/modules/tx_signer.rs @@ -44,13 +44,6 @@ impl TxSigner { ) -> SigningResult { let mut tx = versioned::VersionedTransaction::unsigned(unsigned_msg); - let actual_signatures = key_signs.len(); - let expected_signatures = tx.message.num_required_signatures(); - if actual_signatures != expected_signatures { - return SigningError::err(SigningErrorType::Error_signatures_count) - .with_context(|| format!("Expected '{expected_signatures}' signatures, provided '{actual_signatures}'")); - } - for (signing_pubkey, ed25519_signature) in key_signs { // Find an index of the corresponding account. let account_index = tx diff --git a/rust/tw_tests/tests/chains/solana/solana_compile.rs b/rust/tw_tests/tests/chains/solana/solana_compile.rs index 592d5bee322..d57c136f067 100644 --- a/rust/tw_tests/tests/chains/solana/solana_compile.rs +++ b/rust/tw_tests/tests/chains/solana/solana_compile.rs @@ -3,8 +3,10 @@ // Copyright © 2017 Trust Wallet. use tw_any_coin::test_utils::sign_utils::{CompilerHelper, PreImageHelper}; +use tw_any_coin::test_utils::transaction_decode_utils::TransactionDecoderHelper; use tw_coin_registry::coin_type::CoinType; use tw_encoding::base58::{self, Alphabet}; +use tw_encoding::base64::{self, STANDARD}; use tw_encoding::hex::{DecodeHex, ToHex}; use tw_proto::Common::Proto::SigningError; use tw_proto::Solana::Proto::{self, mod_SigningInput::OneOftransaction_type as TransactionType}; @@ -421,3 +423,54 @@ fn test_solana_compile_transfer_with_fake_signature() { let output = compiler.compile(CoinType::Solana, &input, vec![signature], vec![public_key]); assert_eq!(output.error, SigningError::Error_signing); } + +#[test] +fn test_solana_compile_with_partial_signature() { + // The following is an unsigned transaction generated by the Jito Staking DApp. + // This transaction requires two signatures; only the first one will be provided by the user. + // The DApp will add the second signature before submitting the transaction to Solana network. + let encoded_unsigned_tx = base64::decode("AgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAGDqMrdVJnGyKZfDMMU6yR6mjQRynQy91x5Ik0QW5S7HjblEDFOjfYkcjeiZdpl9opTtGz1XgRDjyGqc4Z5VKuBHI5lg1U5Wnl5tFnrGEI9Y2rG1BNC7tYK5PXpURkpupeap6naP7fZEyKrpuOIYit0GvFUPv3Fsgiuc5jx3g9lS4fsU4N5V6fuoY5br/VSM/4ySAR6se3W6qbLZxqhvWhcUEJ5qP+7PmQMuHB32uXItyzY057jjRAk2vDSwzByOtSH/zRQemDLK8QrZF0lcoPJxtbKTzUcCfqc3AH7UDrOaC9BIo+CMO0lb4X9FQn2JvsW4DH4mlcGGTXZ0PbOb7TRtYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTlniTAUaihSnsel5fw4Szfp0kCFmUxlaalEqbxZmArjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+FkDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAaBTtTK9ooXRnL9rIYDGmPoTqFe+h1EtyKT9tvbABZQBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKnLHFG8r27UznARMamsTdStWltOfL+TLpgyxe4Hfqm8xQYIAgABDAIAAACghgEAAAAAAAoGAAIABggNAQEMCgcJAwECBQIGCA0JDqCGAQAAAAAACwAFAkANAwALAAkDgDgBAAAAAAAIAgAEDAIAAAC0/gAAAAAAAA==", STANDARD).unwrap(); + + // Step 1: Decode the transaction, get the raw message, and prepare the signing input + let mut decoder = TransactionDecoderHelper::::default(); + let output = decoder.decode(CoinType::Solana, encoded_unsigned_tx); + assert_eq!(output.error, SigningError::OK); + + let input = Proto::SigningInput { + raw_message: output.transaction, + ..Proto::SigningInput::default() + }; + + // Step 2: Simulate signature (the first signature), normally obtained from signature server. + let signature1 = "daf56b31e3a97504bb5adb4c64709cdd8240700935d9e1cae9a05ab032de04b469cc1e02587f9c6d7e57192e4f9c77117897a85d15900730d90a7f55debf690a".decode_hex().unwrap(); + let public_key1 = "a32b7552671b22997c330c53ac91ea68d04729d0cbdd71e48934416e52ec78db" + .decode_hex() + .unwrap(); + + // Step 3: Compile the transaction with partial signature + let mut compiler = CompilerHelper::::default(); + let output = compiler.compile( + CoinType::Solana, + &input, + vec![signature1], + vec![public_key1], + ); + assert_eq!(output.error, SigningError::OK); + assert_eq!(output.encoded, "YziLZ5ChTunpGLAAufaXrSpPrFZsL9fsiyo2NshjFMUXbyRrFbpTZpLdsWWULCvKeg4oSdxDuaDqazKZjNgdFjh7bDzq7HPzafzGRHEWy3Rub9DK8uUJrZe6EHpZSm1EKLsbFXYHaoRPPpuT7Pywufjdsk79qWGMHba6KnSaXbRm1tY2Fz88Hz25GUKvvKg7aSUJ5CZ3E2EEcWuX1VBDXPdzcgFkWtrW7JmjfNfhcxKQ5rHet8h3Cr1DueXoz12Sso2gKMgEjAdeHsMFpACercWtWW8M6B8VKp4KqJDvDUCRcJU9U6EbfiTt25dZEuDg4S7ceJG8nHJrYVEKajkPr5HZeU5zuxKQix1xUKBTz7Dx3U4ddFMSSb4xYb7Hv5oacUb3FRP7HwFYkuRocME8fr4wCxGnXg4Tkq1Fe9sVK1nD4YEiD77m6KD44bbXubqQJqkiLuXRJB28Jc3MUPJHrWRJhS5ezL1y89sL91cqZ9fpnLvih4MDC3VcCeuV35LxbigwHvcPPVxsHo2DdKraYMEf5kpR8eqbftAvQbpcUbxeraPcvhPKqU1GtkAgYCL6XG3Y44io5eTQwqMvLwRW9SsFhmbGCk1i7vBPf4p6EmVs3zbfKKSBXK7tg8RD4ojJiGBqCr3yuZkNeFoFXS1JhkggoS8Ury9xLrAB2mmCHEnyr9x5jEspyCcFUtxSyvtCGiyQVG9HxMEzJKVLX27vYncy1URzGyUS9TF35mpXRo3kUNVjTNuMXmYyMSGunt84atiCWSUMPWRB6sjJD9YBv35pLok589m5WvYvB1XFH9sSfkDQ8RCMpu762ekSJSQev3aH1F61hucKSkXNrkCvW8J2wLrLvwSjoPZ28u74AcvpzEa8o3ks33EYdNWTdY1aWWFzqRbCyQPE2s6JmRDJgX57QYdMqXs1KDkhgb7ttQrrkfgb5ki5EofcmNw"); + + // Step 4: Return the compiled transaction with partial signature to the DApp, which will then complete the remaining signatures. + let mut tx_with_partial_signature = + base58::decode(output.encoded.as_ref(), Alphabet::Bitcoin).unwrap(); + // The following is the signature (the second signature) generated by the DApp. + let signature_generated_by_dapp = "c3207d277754a1a872bdc910136e917f9e902f025e493f0c1bf3d5b4512fffe03b250e86e4e68e20e96ab09beb24d7706ae338a2c5d11fbb23d779ae5b8c0701".decode_hex().unwrap(); + // The following is a simple code to simulate the process of the DApp filling in the second signature. + let second_signature_offset = 1 + 64; + let second_signature_length = 64; + tx_with_partial_signature + [second_signature_offset..(second_signature_offset + second_signature_length)] + .copy_from_slice(&signature_generated_by_dapp); + + let full_signed_tx = base64::encode(&tx_with_partial_signature, STANDARD); + // Successfully broadcasted: https://solscan.io/tx/5NuXtYpE58FbtCfEzgk2cTHZgEdNF69Z76bd8TQhBgmEN1RC98DNGNiWhvp1VSDMPudCgpE3z8jD7BNRuBztUbpM + assert_eq!(full_signed_tx, "Atr1azHjqXUEu1rbTGRwnN2CQHAJNdnhyumgWrAy3gS0acweAlh/nG1+VxkuT5x3EXiXqF0VkAcw2Qp/Vd6/aQrDIH0nd1ShqHK9yRATbpF/npAvAl5JPwwb89W0US//4DslDobk5o4g6Wqwm+sk13Bq4ziixdEfuyPXea5bjAcBAgAGDqMrdVJnGyKZfDMMU6yR6mjQRynQy91x5Ik0QW5S7HjblEDFOjfYkcjeiZdpl9opTtGz1XgRDjyGqc4Z5VKuBHI5lg1U5Wnl5tFnrGEI9Y2rG1BNC7tYK5PXpURkpupeap6naP7fZEyKrpuOIYit0GvFUPv3Fsgiuc5jx3g9lS4fsU4N5V6fuoY5br/VSM/4ySAR6se3W6qbLZxqhvWhcUEJ5qP+7PmQMuHB32uXItyzY057jjRAk2vDSwzByOtSH/zRQemDLK8QrZF0lcoPJxtbKTzUcCfqc3AH7UDrOaC9BIo+CMO0lb4X9FQn2JvsW4DH4mlcGGTXZ0PbOb7TRtYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFTlniTAUaihSnsel5fw4Szfp0kCFmUxlaalEqbxZmArjJclj04kifG7PRApFI4NgwtaE5na/xCEBI572Nvp+FkDBkZv5SEXMv/srbpyw5vnvIzlu8X3EmssQ5s6QAAAAAaBTtTK9ooXRnL9rIYDGmPoTqFe+h1EtyKT9tvbABZQBt324ddloZPZy+FGzut5rBy0he1fWzeROoz1hX7/AKnLHFG8r27UznARMamsTdStWltOfL+TLpgyxe4Hfqm8xQYIAgABDAIAAACghgEAAAAAAAoGAAIABggNAQEMCgcJAwECBQIGCA0JDqCGAQAAAAAACwAFAkANAwALAAkDgDgBAAAAAAAIAgAEDAIAAAC0/gAAAAAAAA==") +} From eb408ea706f4b64f6930af95f1ed2208f36da01d Mon Sep 17 00:00:00 2001 From: Sergei Boiko <127754187+satoshiotomakan@users.noreply.github.com> Date: Fri, 6 Dec 2024 21:56:35 +0700 Subject: [PATCH 2/7] [BRC20]: Fix bug when revealing BRC20 with extra P2TR (#4152) --- .../tw_utxo/src/modules/sighash_computer.rs | 14 ++-- .../chains/bitcoin/bitcoin_sign/brc20.rs | 74 +++++++++++++++++++ 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs b/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs index 0bcfb626960..f2ee8880590 100644 --- a/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs +++ b/rust/frameworks/tw_utxo/src/modules/sighash_computer.rs @@ -60,11 +60,11 @@ where .input_args() .iter() .enumerate() - .map(|(input_index, utxo)| { + .map(|(signing_input_index, utxo)| { let signing_method = utxo.signing_method; let utxo_args = UtxoPreimageArgs { - input_index, + input_index: signing_input_index, script_pubkey: utxo.script_pubkey.clone(), amount: utxo.amount, // TODO move `leaf_hash_code_separator` to `UtxoTaprootPreimageArgs`. @@ -90,12 +90,14 @@ where let tr_spent_script_pubkeys: Vec