A simple example how to use libwally - from generation of the recovery phrase to signing of the psbt transaction
main.cpp
- Committer:
- diybitcoinhardware
- Date:
- 2019-09-19
- Revision:
- 3:3d6e031ab07b
- Parent:
- 0:3729443d0bf8
File content as of revision 3:3d6e031ab07b:
#include "mbed.h" #include "wally_core.h" #include "wally_bip32.h" #include "wally_bip39.h" #include "wally_address.h" #include "wally_psbt.h" #include "wally_script.h" RawSerial pc(SERIAL_TX, SERIAL_RX, 115200); // small helper functions that prints // data in hex to the serial port void print_hex(const uint8_t * data, size_t data_len){ for(int i=0; i<data_len; i++){ printf("%02x", data[i]); } } // just adds a new line to the end of the data void println_hex(const uint8_t * data, size_t data_len){ print_hex(data, data_len); printf("\r\n"); } // prints error and hangs forever void err(const char * message, void * data = NULL){ printf("ERROR: %s\r\n", message); while(1){ wait(1); } } void print_psbt(const wally_psbt * psbt); void libwally_example(){ size_t len; // to store length of serialization etc int res; // to store wally's results // we need to initialize libwally first res = wally_init(0); /**************** BIP-39 recovery phrase ******************/ // random buffer should be generated by TRNG or somehow else // but we will use predefined one for demo purposes // 16 bytes will generate a 12-word recovery phrase uint8_t rnd[] = { 0xbd, 0xb5, 0x1a, 0x16, 0xeb, 0x64, 0x60, 0xec, 0x16, 0xf8, 0x4d, 0x7b, 0x6f, 0x19, 0xe2, 0x0d }; // creating a recovery phrase char *phrase = NULL; res = bip39_mnemonic_from_bytes(NULL, rnd, sizeof(rnd), &phrase); printf("Recovery phrase: %s\r\n", phrase); // converting recovery phrase to seed uint8_t seed[BIP39_SEED_LEN_512]; res = bip39_mnemonic_to_seed(phrase, "my password", seed, sizeof(seed), &len); // don't forget to securely clean up the string when done wally_free_string(phrase); printf("Seed: "); println_hex(seed, sizeof(seed)); /**************** BIP-32 HD keys ******************/ // root HD key ext_key * root = NULL; res = bip32_key_from_seed_alloc(seed, sizeof(seed), BIP32_VER_TEST_PRIVATE, 0, &root); // get base58 xprv string char *xprv = NULL; res = bip32_key_to_base58(root, BIP32_FLAG_KEY_PRIVATE, &xprv); printf("Root key: %s\r\n", xprv); // don't forget to securely clean up the string when done wally_free_string(xprv); // deriving account key for native segwit, testnet: m/84h/1h/0h ext_key * account = NULL; uint32_t path[] = { BIP32_INITIAL_HARDENED_CHILD+84, // 84h BIP32_INITIAL_HARDENED_CHILD+1, // 1h BIP32_INITIAL_HARDENED_CHILD // 0h }; res = bip32_key_from_parent_path_alloc(root, path, 3, BIP32_FLAG_KEY_PRIVATE, &account); res = bip32_key_to_base58(account, BIP32_FLAG_KEY_PRIVATE, &xprv); printf("Account private key: %s\r\n", xprv); // don't forget to securely clean up the string when done wally_free_string(xprv); char *xpub = NULL; res = bip32_key_to_base58(account, BIP32_FLAG_KEY_PUBLIC, &xpub); printf("Account public key: %s\r\n", xpub); // fingerprint printf("Derivation information: ["); print_hex(root->hash160, 4); printf("/84h/1h/0h]%s\r\n", xpub); // don't forget to securely clean up the string when done wally_free_string(xpub); /**************** Addresses ******************/ // key for the first address ext_key * first_recv = NULL; uint32_t recv_path[] = {0, 0}; // we only need public key here, no need in private key res = bip32_key_from_parent_path_alloc(account, recv_path, 2, BIP32_FLAG_KEY_PUBLIC, &first_recv); char * addr = NULL; // native segwit address res = wally_bip32_key_to_addr_segwit(first_recv, "tb", 0, &addr); printf("Segwit address: %s\r\n", addr); wally_free_string(addr); // nested segwit address res = wally_bip32_key_to_address(first_recv, WALLY_ADDRESS_TYPE_P2SH_P2WPKH, WALLY_ADDRESS_VERSION_P2SH_TESTNET, &addr); printf("Nested segwit address: %s\r\n", addr); wally_free_string(addr); // legacy address res = wally_bip32_key_to_address(first_recv, WALLY_ADDRESS_TYPE_P2PKH, WALLY_ADDRESS_VERSION_P2PKH_TESTNET, &addr); printf("Legacy address: %s\r\n", addr); wally_free_string(addr); /**************** PSBT ******************/ char b64_psbt[] = "cHNidP8BAHICAAAAAX7U8W8nyYJcYzFjoHa5UH5j0Ug43ej/q2bf" "IPq8XnjeAAAAAAD/////AihATAAAAAAAFgAUPE9Ix5e2qWWcDW78" "DM+uqX09ANNAS0wAAAAAABepFPPNvp1TAyho+Kys4wwejY8iakgQ" "hwAAAAAAAQEfgJaYAAAAAAAWABRy2Nt/+F2j9EYd2bcI5CfSqeey" "MSIGAmijc35Kh8Nfa9oC0jGb2I1UfzkS0MqAfHw0BKA6JRwuGCbd" "2XhUAACAAQAAgAAAAIAAAAAAAAAAAAAiAgOTSWH+F+K8DsY9lzWM" "QZJptjBUmMVgdE+1814LZMwVFRgm3dl4VAAAgAEAAIAAAACAAQAAAAAAAAAAAA=="; wally_psbt * psbt = NULL; res = wally_psbt_from_base64(b64_psbt, &psbt); print_psbt(psbt); for(int i = 0; i < psbt->num_inputs; i++){ if(!psbt->inputs[i].witness_utxo){ printf("Too lazy for legacy"); return; } uint8_t hash[32]; uint8_t script[25]; printf("scriptpubkey: "); println_hex(psbt->inputs[i].witness_utxo->script, psbt->inputs[i].witness_utxo->script_len); wally_scriptpubkey_p2pkh_from_bytes( psbt->inputs[i].witness_utxo->script+2, 20, 0, script, 25, &len); printf("scriptpubkey: "); println_hex(script, len); wally_tx_get_btc_signature_hash(psbt->tx, i, script, len, psbt->inputs[i].witness_utxo->satoshi, WALLY_SIGHASH_ALL, WALLY_TX_FLAG_USE_WITNESS, hash, 32 ); printf("Input %d. Hash to sign: ", i); println_hex(hash, 32); ext_key * pk = NULL; bip32_key_from_parent_path_alloc(root, psbt->inputs[i].keypaths->items[0].origin.path, psbt->inputs[i].keypaths->items[0].origin.path_len, BIP32_FLAG_KEY_PRIVATE, &pk); uint8_t sig[EC_SIGNATURE_LEN]; wally_ec_sig_from_bytes( pk->priv_key+1, 32, // first byte of ext_key.priv_key is 0x00 hash, 32, EC_FLAG_ECDSA, sig, EC_SIGNATURE_LEN ); bip32_key_free(pk); uint8_t der[EC_SIGNATURE_DER_MAX_LEN+1]; wally_ec_sig_to_der( sig, EC_SIGNATURE_LEN, der, EC_SIGNATURE_DER_MAX_LEN, &len ); der[len] = WALLY_SIGHASH_ALL; if(!psbt->inputs[i].partial_sigs){ partial_sigs_map_init_alloc(1, &psbt->inputs[i].partial_sigs); } add_new_partial_sig(psbt->inputs[i].partial_sigs, pk->pub_key, der, len+1 ); } char * output; wally_psbt_to_base64(psbt, &output); wally_psbt_free(psbt); printf("PSBT: %s\r\n", output); wally_free_string(output); bip32_key_free(root); bip32_key_free(account); wally_cleanup(0); } void print_psbt(const wally_psbt * psbt){ printf("PSBT:\r\n" "Inputs: %d\r\n", psbt->num_inputs); uint64_t in_amount = 0; for(int i = 0; i < psbt->num_inputs; i++){ if(!psbt->inputs[i].witness_utxo){ printf("Non-segwit input or prev output is missing! It's bad..."); return; } in_amount += psbt->inputs[i].witness_utxo->satoshi; } printf("Spending %llu satoshi\r\n", in_amount); printf("Outputs: %d\r\n", psbt->tx->num_outputs); uint64_t out_amount = 0; for(int i=0; i < psbt->tx->num_outputs; i++){ size_t script_type; wally_scriptpubkey_get_type(psbt->tx->outputs[i].script, psbt->tx->outputs[i].script_len, &script_type); char * addr = NULL; uint8_t bytes[21]; // should deal with all script types, only P2WPKH for now switch(script_type){ case WALLY_SCRIPT_TYPE_P2WPKH: case WALLY_SCRIPT_TYPE_P2WSH: wally_addr_segwit_from_bytes(psbt->tx->outputs[i].script, psbt->tx->outputs[i].script_len, "tb", 0, &addr); break; case WALLY_SCRIPT_TYPE_P2SH: bytes[0] = WALLY_ADDRESS_VERSION_P2SH_TESTNET; memcpy(bytes+1, psbt->tx->outputs[i].script+2, 20); wally_base58_from_bytes(bytes, 21, BASE58_FLAG_CHECKSUM, &addr); break; case WALLY_SCRIPT_TYPE_P2PKH: bytes[0] = WALLY_ADDRESS_VERSION_P2PKH_TESTNET; memcpy(bytes+1, psbt->tx->outputs[i].script+3, 20); wally_base58_from_bytes(bytes, 21, BASE58_FLAG_CHECKSUM, &addr); break; } // TODO: also verify if it is a change address if(addr){ printf("- Out %d: %llu sat to %s\r\n", i+1, psbt->tx->outputs[i].satoshi, addr); wally_free_string(addr); }else{ printf("- Out %d: %llu sat to %s\r\n", i+1, psbt->tx->outputs[i].satoshi, "...custom script..."); } out_amount += psbt->tx->outputs[i].satoshi; } printf("Fee: %llu sat\r\n", in_amount-out_amount); } int main(){ printf("Press any key to continue\r\n"); pc.getc(); printf("Ready to go!\r\n"); printf("=== Running example for libwally ===\r\n"); libwally_example(); printf("\r\n=== Done ===\r\n"); }