Liquid Transaction Example

Liquid transaction example in Java

The example below shows you how to create and run an example in Java that builds a Liquid transaction and signs it.

Create file named liquid_tx_example.java in libwally-core/src/swig_java/src/com/blockstream/test/

The contents of the file are shown below. Copy them into the file and save it.

package com.blockstream.test;
 
import com.blockstream.libwally.Wally;
 
public class liquid_tx_example {  
 
    public liquid_tx_example() { }
 
    /**
     * Example of stages in creating a transaction, using dummy hex values. 
     * Adds two inputs and one output.
     * Signs the inputs.
     *
     * @System.out    the signed transaction hex
     */
    public void build_and_sign_transaction() {
        /*
         * Create a transaction 
         * --------------------
         */
        final String tx_in_hex = "0000000000000000000000000000000000000000000000000000000000000000"; 
        final byte[] tx_in_byte = Wally.hex_to_bytes(tx_in_hex);
        
        final String tx_out_hex = "0000000000000000000000000000000000000000000000000000000000000000"; 
        final byte[] tx_out_byte = Wally.hex_to_bytes(tx_out_hex);
        
        final String script_hex = "0000"; 
        final byte[] script_byte = Wally.hex_to_bytes(script_hex);
    
        final String witness_script_hex = "000000"; 
        final byte[] witness_script_byte = Wally.hex_to_bytes(witness_script_hex);
        
        final String nonce_hex = "0000000000000000000000000000000000000000000000000000000000000000"; 
        final byte[] nonce_byte = Wally.hex_to_bytes(nonce_hex);
        
        final String entropy_hex = "0000000000000000000000000000000000000000000000000000000000000000"; 
        final byte[] entropy_byte = Wally.hex_to_bytes(entropy_hex);
        
        final Object witness = Wally.tx_witness_stack_init(5L);
        Wally.tx_witness_stack_add(witness, witness_script_byte);
 
        // This is currently required by tx_elements_input_init - this is a bug that is being addressed.
        final Object pegin_witness = Wally.tx_witness_stack_init(5L);
        Wally.tx_witness_stack_add(pegin_witness, witness_script_byte);
        
        final long sequence = 0xffffffffL;
        
        // Version, locktime, inputs, outputs:
        final Object tx = Wally.tx_init(2, 0, 2, 1);
        
        final Object tx_input01 = Wally.tx_elements_input_init(tx_in_byte, 0L, sequence, script_byte, witness, nonce_byte, entropy_byte, null, null, null, null, pegin_witness);
        final Object tx_input02 = Wally.tx_elements_input_init(tx_in_byte, 2L, sequence, script_byte, witness, nonce_byte, entropy_byte, null, null, null, null, pegin_witness);
        
        
        final byte[] ct_value_byte = Wally.tx_confidential_value_from_satoshi(10000L, null);
        final Object tx_output = Wally.tx_elements_output_init(script_byte, null, ct_value_byte, null, null, null);
        
        Wally.tx_add_input(tx, tx_input01);
        Wally.tx_add_input(tx, tx_input02);
        Wally.tx_add_output(tx, tx_output);
        
        final byte[] input_witness01 = Wally.tx_input_get_witness(tx_input01, 0);
        final byte[] input_witness02 = Wally.tx_input_get_witness(tx_input02, 0);
                
        String tx_hex = "";
 
        tx_hex = Wally.tx_to_hex(tx, Wally.WALLY_TX_FLAG_USE_WITNESS);
        
        System.out.println("UNSIGNED TX HEX: " + tx_hex);
        
        
        /*
         * Sign the transaction 
         * --------------------
         */
        byte[] sig_hash01 = Wally.tx_get_elements_signature_hash(tx, 0L, script_byte, ct_value_byte, Wally.WALLY_SIGHASH_ALL, Wally.WALLY_TX_FLAG_USE_WITNESS, null);
        byte[] sig_hash02 = Wally.tx_get_elements_signature_hash(tx, 1L, script_byte, ct_value_byte, Wally.WALLY_SIGHASH_ALL, Wally.WALLY_TX_FLAG_USE_WITNESS, null);
        
        // Example bip32 key
        final byte[] seed = h("000102030405060708090a0b0c0d0e0f"); 
        final Object seedKey = Wally.bip32_key_from_seed(seed, Wally.BIP32_VER_MAIN_PRIVATE, 0);
        final Object derivedKey = Wally.bip32_key_from_parent(seedKey, 0, Wally.BIP32_FLAG_KEY_PRIVATE);
        
        final byte[] private_key_bytes = Wally.bip32_key_get_priv_key(derivedKey);
        final byte[] public_key_bytes = Wally.bip32_key_get_pub_key(derivedKey);
        
        final byte[] signature01 = Wally.ec_sig_from_bytes(private_key_bytes, sig_hash01, Wally.EC_FLAG_ECDSA);        
        final byte[] signature02 = Wally.ec_sig_from_bytes(private_key_bytes, sig_hash02, Wally.EC_FLAG_ECDSA);        
        
        Wally.ec_sig_verify(public_key_bytes, sig_hash01, Wally.EC_FLAG_ECDSA, signature01);
        Wally.ec_sig_verify(public_key_bytes, sig_hash02, Wally.EC_FLAG_ECDSA, signature02);
 
        Wally.tx_set_input_script(tx, 0L, signature01);
        Wally.tx_set_input_script(tx, 1L, signature02);
 
        tx_hex = Wally.tx_to_hex(tx, Wally.WALLY_TX_FLAG_USE_WITNESS);
 
        System.out.println("SIGNED TX HEX: " + tx_hex);
 
        // Cleanup
        Wally.bip32_key_free(derivedKey);
        Wally.bip32_key_free(seedKey);
        Wally.cleanup();
    }
 
    private String h(final byte[] bytes) { return Wally.hex_from_bytes(bytes); }
    private byte[] h(final String hex) { return Wally.hex_to_bytes(hex); }
 
    public static void main(final String[] args) {
        System.out.println("EXAMPLE");
        final liquid_tx_example example = new liquid_tx_example();
        example.build_and_sign_transaction();
    }
}

From the libwally-core/src directory, compile the code and then run it:

cd swig_java/src
javac com/blockstream/test/liquid_tx_example.java
cd ..
cd ..
LD_LIBRARY_PATH=.libs java -Djava.library.path=.libs -classpath swig_java/src com.blockstream.test.liquid_tx_example

For more details on signing see libwally-core/src/swig_java/src/com/blockstream/test/test_bip32.java.

Using other Libwally functions

You can find all the Libwally functions on https://wally.readthedocs.io.

To use one, look up your chosen function on https://wally.readthedocs.io

To see the arguments you need to provide to use the function in Java:

  • Refer to the similar function (e.g. wally_tx_init_alloc is wally_tx_init) in Wally.java
  • Note the arguments you must provide (some are not needed) and call the function from Java.

For example using tx_elements_input_init as an example:

Readthedocs lists 21 arguments whereas Wally.java lists 12:

jarg1, jarg3, jarg4, jarg5, jarg7, jarg8, jarg10, jarg12, jarg14, jarg16, jarg18, jarg20.

Meaning that you do not need to provide the following readthedocs arguments in order to call the function from Java:

2 (txhash_len), 6, 9, 11, 13, 15, 17, 19, 21


The Liquid Network is a Bitcoin layer-2 enabling the issuance of security tokens and other digital assets.

© 2023 Liquid Network
All rights reserved.

Feedback and Content Requests

We'd be happy to hear your suggestions on how we can improve this site.

BuildOnL2 Community

The official BuildOnL2 community lives
at community.liquid.net. Join us and build the future of Bitcoin on Liquid.

Telegram

Community-driven telegram group where
most of the Liquid developers hang out.
Go to t.me/liquid_devel to join.