BitcoinSV Livebook
Mix.install(
[
# to create BitcoinSV transactions, including keys and scripts
{:bsv, "~> 2.1"}
],
config: [
bsv: [network: :test]
]
)
# ran `mix local.rebar` to resolve:
# Could not find "rebar3" to compile dependency :unicode_util_compat, please ensure "rebar3" is available
Part 1 - Generate a Master Key and additional addresses
Generate a Mneumonic
Wallets are software that manage transactions, much like a browser is software that manages HTML.
Wallet keys are created from a generated keypair.
Importantly, the keypair is reproduceable using the seed words. Thus, there is a lot of emphasis on securing seed phrases and/or keys, and rightly so.
A seed phrase is a string of characters (often 12 words – sometimes 24 words).
Here’s how to generate a seed phrase using the BSV library:
mnemonic = BSV.Mnemonic.new()
Here are 2 examples of what a 12-word seed phrase looks like:
jacket ceiling drum praise auction rude travel interest undo occur shoulder stone
super scene nice carry deer float grant twice exit nerve december glance
> Note: Hopefully obvious, but worth repeating: don’t use these phrases for things outside of these exercises
Creating a seed
The randomly generated string can be passed to the Mnemonic.to_seed
function to generate a binary string.
seed = BSV.Mnemonic.to_seed(mnemonic)
Create a seed phrase with a Passphrase
Similar to the example above, except,
an additional passphrase
is included when generating the seed.
passphrase = "your-additional-passphrase-goes-here"
seed = BSV.Mnemonic.to_seed(mnemonic, passphrase: passphrase)
Generate an Extended key from a Seed
An extended key can be used to derive additional keys in an Hierarchial Deterministic Wallet.
extkey = BSV.ExtKey.from_seed!(seed)
Derive a child key and address
Derive a child keypair from the extended key
using the first derivation path m/0/1
, for demonstration purposes.
In practice, other derivation paths are often used instead.
child = BSV.ExtKey.derive(extkey, "m/0/1")
Get m/0/1’s public key as a string
address = BSV.Address.from_pubkey(child.pubkey)
BSV.Address.to_string(address)
privkey = child.privkey |> BSV.PrivKey.to_wif()
So far, we have:
- generated a random Mnemonic string
- used that string to generate a binary Seed (optinally, with a passphrase)
- used the Seed to generate an Extended Key
- generate at least one derived keypair from the Extended Key
- display the 1. private key, 2. public key, and 3. address from the generated keypairs
Get m/0/2’s public key as a string
derivation_path = "m/0/2"
child = BSV.ExtKey.derive(extkey, derivation_path)
address = BSV.Address.from_pubkey(child.pubkey)
BSV.Address.to_string(address)
Get m/0/44/0’s public key as a string
BTC uses 0/44/0
as its derivation path.
> Note: Exodus describes how they use derivation paths.
Below, we generate an Extended Key using BTC’S derivation path. This derivation path has ticks after the numbers 44’ and 0’, which indicate “hardened keys.” You can find others resources online regarding HD Wallets and how hardened keys work.
derivation_path = "m/0/44'/0'"
child = BSV.ExtKey.derive(extkey, derivation_path)
Again, generate a (public) address from the public key.
address = BSV.Address.from_pubkey(child.pubkey)
BSV.Address.to_string(address)
for fun: display 10 keys in a loop
Wallets have to manage UTXO’s and will scan common derivation paths, looking for active addresses.
IO.puts("path address_string privkey")
Enum.each(0..9, fn i ->
derivation_path = "m/0/#{i}"
child = BSV.ExtKey.derive(extkey, derivation_path)
address = BSV.Address.from_pubkey(child.pubkey)
privkey = child.privkey |> BSV.PrivKey.to_wif()
address_string = BSV.Address.to_string(address)
IO.puts("#{derivation_path} #{address_string} #{privkey}")
end)
To demonstrate how we can regenerate public addresses from the private keys:
Derive a public address from a private key.
After displaying the 10 keys, grab one of the privkey
values and paste it below for the privkey variable, then run the code block.
You should see the associated public address.
You’ve now generated a Master Key from scratch, generated additional addresses that are derived from that key.
privkey = "cMuVDNaxLkwSm5bxPhMc6tPCX45GVLyNtzW75sS3UfgVgFyJ7riH"
{:ok, priv_key} = BSV.PrivKey.from_wif(privkey)
keypair = priv_key |> BSV.KeyPair.from_privkey()
%BSV.KeyPair{
privkey: privkey,
pubkey: pubkey
} = keypair
address = BSV.Address.from_pubkey(pubkey)
address_string = BSV.Address.to_string(address)
Next steps, we’ll use a key to create a transaction. And we’ll find a faucet to get a bit of bitcoin to put to use.