<p>The problem appears to be the lines where you encode the number of inputs and outputs:</p>
<pre><code>    let inputs_length: u32 = inputs.len() as u32;
    tx.extend_from_slice(&amp;inputs_length.to_le_bytes());
...
    let outputs_length: u32 = outputs.len() as u32;
    tx.extend_from_slice(&amp;outputs_length.to_le_bytes());
</code></pre>
<p>These are not just 32 bit uints. These lengths are Compact Size unsigned ints which have a special encoding. You probably already know how to encode them since it's the same encoding for the script length in output scripts and there you use the function <code>turn_to_varint</code>. Assuming that function was implemented correctly, using that for the input and output lengths should resolve your issue.</p>

CC BY-SA 4.0
2024-02-12T23:11:32.150Z
2024-02-12T23:11:32.150Z
Ava Chow
<p>RAW Signed transaction (signet)</p>
<pre><code>020000000001013ccb46c8366e39d7ff36df4f199813aa8b9dc4bed143a0eb9205cf698ca16298e400000000ffffffff0240420f00000000002200202d67ce38ba266a68f0f7c4668e9d0f5584c6da7c454bcc2368bf9fff10135157a086010000000000160014d765749aa66430b85765bdedee488f5fe3c82a8e02483045022100f71bd596f18d3caf1297a74f5619cd6c823d58acaf5f9172bea7d7d6ef82cfd502206c7c7960accd4409cccb1ca97d8268af12664de50b905d1c3579e6237c0fb823012102d3428014dd5ccf1927c6dc164e6324e2c0945a9f50b84769592e195ae6a40e8a00000000
</code></pre>
<p>Transaction id returned .</p>
<pre><code>    &quot;txid&quot;: &quot;1d81e1dae9d956fd61cf79efa6a64de11465d6a1ead46e24ba043e278360fb35&quot;,
    &quot;wtxid&quot;: &quot;d9e69e8d05c61c78796b2d73b731a705334e07c013e7e714e10eaf45885a144c&quot;
</code></pre>
<p>Transaction id calculated by following <a href="https://developer.bitcoin.org/reference/transactions.html#raw-transaction-format" rel="nofollow noreferrer">Raw_tx</a>.</p>
<pre><code>22d008e988b168165bd6c06dc4200ad57d456f1b152d25e540e75e38bb023c40
</code></pre>
<p>Transaction id calculated by hashing the Raw signed transaction</p>
<pre><code>82fbc313fec14a7cf05309544b24bc97dc785bb82e4a9a713bb6e8d5f3cf4aeb
</code></pre>
<p>I have followed the above transaction formats. The signed raw transaction follows the specification given in bicoin developers raw transaction format.</p>
<p>Given are the implementation codes in Rust</p>
<pre><code>fn get_txid(inputs: Vec&lt;Vec&lt;u8&gt;&gt;, outputs: Vec&lt;Vec&lt;u8&gt;&gt;) -&gt; [u8; 32] {
    let mut tx = Vec::new();

    //adding version
    let version: u32 = 0x00000002;
    tx.extend_from_slice(&amp;version.to_le_bytes());

    let inputs_length: u32 = inputs.len() as u32;
    tx.extend_from_slice(&amp;inputs_length.to_le_bytes());

    for input in inputs {
        tx.extend_from_slice(&amp;input);
    }

    let outputs_length: u32 = outputs.len() as u32;
    tx.extend_from_slice(&amp;outputs_length.to_le_bytes());

    for output in outputs {
        tx.extend_from_slice(&amp;output);
    }

    let locktime: u32 = 0x00000000;
    tx.extend_from_slice(&amp;locktime.to_le_bytes());
    let txid = sha256_hash(&amp;sha256_hash(&amp;tx));
    //output

    let tx_array: [u8; 32] = match txid.try_into() {
        Ok(arr) =&gt; arr,
        Err(_) =&gt; panic!(&quot;Expected a Vec of length 32, but it was {}&quot;, tx.len()),
    };

    // reverse txid
    let mut reversed_txid = [0; 32];
    for i in 0..32 {
        reversed_txid[i] = tx_array[31 - i];
    }

    reversed_txid
}
</code></pre>
<p>Output serialization</p>
<pre><code>fn output_from_options(script: &amp;[u8], value: u32) -&gt; Vec&lt;u8&gt; {
    
    let mut output = Vec::new();
    let valhue: u64 = value as u64;
    let value_in_8_bytes_in_little_endian = valhue.to_le_bytes();
    output.extend_from_slice(&amp;value_in_8_bytes_in_little_endian);

    let script_length = script.len() as u64; 
    let script_length_varint = turn_to_varint(script_length);
    
    output.extend_from_slice(&amp;script_length_varint);
    output.extend_from_slice(script); //scriptpubkey {p2wpkh and p2wsh}
    output

    //OK
}
</code></pre>
<p>Input Serialization</p>
<pre><code>fn input_from_utxo(txid: &amp;[u8], index: u32) -&gt; Vec&lt;u8&gt; {
    let mut input = Vec::new();

    //txid
    let reversed_txid: Vec&lt;u8&gt; = txid.iter().rev().cloned().collect();
    input.extend_from_slice(&amp;reversed_txid);

    //index
    let index_in_little_endian_bytes = index.to_le_bytes();  // fixed 4 bytes
    input.extend_from_slice(&amp;index_in_little_endian_bytes);
    
    //script length (default is zero for us)
    input.push(0x00);

    //sequence
    let sequence: u32 = 0xffffffff;
    input.extend_from_slice(&amp;sequence.to_le_bytes());

    input

    //OK
}
</code></pre>

<transactions><raw-transaction><p2wpkh>
Incorrect Transaction ID being calculated for a p2wpkh transaction
Prabhat Verma
# Incorrect Transaction ID being calculated for a p2wpkh transaction

(Asked by: username_0 on 2024-02-12)

RAW Signed transaction (signet)
020000000001013ccb46c8366e39d7ff36df4f199813aa8b9dc4bed143a0eb9205cf698ca16298e400000000ffffffff0240420f00000000002200202d67ce38ba266a68f0f7c4668e9d0f5584c6da7c454bcc2368bf9fff10135157a086010000000000160014d765749aa66430b85765bdedee488f5fe3c82a8e02483045022100f71bd596f18d3caf1297a74f5619cd6c823d58acaf5f9172bea7d7d6ef82cfd502206c7c7960accd4409cccb1ca97d8268af12664de50b905d1c3579e6237c0fb823012102d3428014dd5ccf1927c6dc164e6324e2c0945a9f50b84769592e195ae6a40e8a00000000

Transaction id returned .
    "txid": "1d81e1dae9d956fd61cf79efa6a64de11465d6a1ead46e24ba043e278360fb35",
    "wtxid": "d9e69e8d05c61c78796b2d73b731a705334e07c013e7e714e10eaf45885a144c"

Transaction id calculated by following Raw_tx.
22d008e988b168165bd6c06dc4200ad57d456f1b152d25e540e75e38bb023c40

Transaction id calculated by hashing the Raw signed transaction
82fbc313fec14a7cf05309544b24bc97dc785bb82e4a9a713bb6e8d5f3cf4aeb

I have followed the above transaction formats. The signed raw transaction follows the specification given in bicoin developers raw transaction format.
Given are the implementation codes in Rust
fn get_txid(inputs: Vec>, outputs: Vec>) -> [u8; 32] {
    let mut tx = Vec::new();

    //adding version
    let version: u32 = 0x00000002;
    tx.extend_from_slice(&version.to_le_bytes());

    let inputs_length: u32 = inputs.len() as u32;
    tx.extend_from_slice(&inputs_length.to_le_bytes());

    for input in inputs {
        tx.extend_from_slice(&input);
    }

    let outputs_length: u32 = outputs.len() as u32;
    tx.extend_from_slice(&outputs_length.to_le_bytes());

    for output in outputs {
        tx.extend_from_slice(&output);
    }

    let locktime: u32 = 0x00000000;
    tx.extend_from_slice(&locktime.to_le_bytes());
    let txid = sha256_hash(&sha256_hash(&tx));
    //output

    let tx_array: [u8; 32] = match txid.try_into() {
        Ok(arr) => arr,
        Err(_) => panic!("Expected a Vec of length 32, but it was {}", tx.len()),
    };

    // reverse txid
    let mut reversed_txid = [0; 32];
    for i in 0..32 {
        reversed_txid[i] = tx_array[31 - i];
    }

    reversed_txid
}

Output serialization
fn output_from_options(script: &[u8], value: u32) -> Vec {
    
    let mut output = Vec::new();
    let valhue: u64 = value as u64;
    let value_in_8_bytes_in_little_endian = valhue.to_le_bytes();
    output.extend_from_slice(&value_in_8_bytes_in_little_endian);

    let script_length = script.len() as u64; 
    let script_length_varint = turn_to_varint(script_length);
    
    output.extend_from_slice(&script_length_varint);
    output.extend_from_slice(script); //scriptpubkey {p2wpkh and p2wsh}
    output

    //OK
}

Input Serialization
fn input_from_utxo(txid: &[u8], index: u32) -> Vec {
    let mut input = Vec::new();

    //txid
    let reversed_txid: Vec = txid.iter().rev().cloned().collect();
    input.extend_from_slice(&reversed_txid);

    //index
    let index_in_little_endian_bytes = index.to_le_bytes();  // fixed 4 bytes
    input.extend_from_slice(&index_in_little_endian_bytes);
    
    //script length (default is zero for us)
    input.push(0x00);

    //sequence
    let sequence: u32 = 0xffffffff;
    input.extend_from_slice(&sequence.to_le_bytes());

    input

    //OK
}

**Tags:** transactions, raw-transaction, p2wpkh


---

## 1 answers

### Answer by username_1 on 2024-02-12. Score: 2 | Accepted answer

The problem appears to be the lines where you encode the number of inputs and outputs:
    let inputs_length: u32 = inputs.len() as u32;
    tx.extend_from_slice(&inputs_length.to_le_bytes());
...
    let outputs_length: u32 = outputs.len() as u32;
    tx.extend_from_slice(&outputs_length.to_le_bytes());

These are not just 32 bit uints. These lengths are Compact Size unsigned ints which have a special encoding. You probably already know how to encode them since it's the same encoding for the script length in output scripts and there you use the function turn_to_varint. Assuming that function was implemented correctly, using that for the input and output lengths should resolve your issue.


---

