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