A cli wallet for the Tuxedo Node Template
This wallet works with the Tuxedo Node Template and Tuxedo Template Runtime which is also contained in this repository.
Like many UTXO wallets, this one synchronizes a local-to-the-wallet database of UTXOs that exist on the current best chain. The wallet does not sync the entire blockchain state. Rather, it syncs a subset of the state that it considers "relevant". Currently, the wallet syncs any UTXOs that contain tokens owned by a key in the wallet's keystore. However, the wallet is designed so that this notion of "relevance" is generalizeable. This design allows developers building chains with Tuxedo to extend the wallet for their own needs. However, because this is a text- based wallet, it is likely not well-suited for end users of popular dapps.
The node application has a thorough help page that you can access on the CLI. It also has help pages for all subcommands. Please explore and read these docs thoroughly.
# Show the wallet's main help page
$ tuxedo-template-wallet --help
A simple example / template wallet built for the tuxedo template runtime
Usage: tuxedo-template-wallet [OPTIONS] <COMMAND>
Commands:
...
# Show the help for a subcommand
$ tuxedo-template-wallet verify-coin --help
Verify that a particular coin exists.
Show its value and owner from both chain storage and the local database.
Usage: tuxedo-template-wallet verify-coin <OUTPUT_REF>
Arguments:
<OUTPUT_REF>
A hex-encoded output reference
Options:
-h, --help
Print help (see a summary with '-h')
This guided tour shows off some of the most common and important wallet features. It can serve as a quickstart, but is not a substitute for reading the help pages mentioned above. (Seriously, please rtfm).
To follow this walkthrough, you should already have a fresh tuxedo template dev node running as described in the main readme. For example, node-template --dev
.
The wallet is not a long-running process. The wallet starts up, syncs with the latest chain state, performs the action invoked, and exits.
Let's begin by just starting a new wallet and letting it sync.
$ tuxedo-template-wallet
[2023-04-11T17:44:40Z INFO tuxedo_template_wallet::sync] Initializing fresh sync from genesis 0x12aba3510dc0918aec178a32927f145d22d62afe63392713cb65b85570206327
[2023-04-11T17:44:40Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2023-04-11T17:44:40Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 20
[2023-04-11T17:44:40Z INFO tuxedo_template_wallet] No Wallet Command invoked. Exiting.
The logs indicate that a fresh database was created and had no blocks in it. Then, by communicating with the node, the wallet was able to sync 20 blocks. Finally it tells us that we didn't ask the wallet to tell us any specific information or send any transactions, so it just exits.
Let's run the same command again and see that the wallet persists state.
$ tuxedo-template-wallet
[2023-04-11T17:46:17Z INFO tuxedo_template_wallet] Number of blocks in the db: 20
[2023-04-11T17:46:17Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 52
[2023-04-11T17:46:17Z INFO tuxedo_template_wallet] No Wallet Command invoked. Exiting.
This time, it is not a fresh database. In fact it starts from block 20, where it left off previously, and syncs up to block 52. Again, we didn't tell the wallet any specific action to take, so it just exits.
We can also tell the wallet to skip the initial sync if we want to for any reason.
$ tuxedo-template-wallet --no-sync
[2023-04-11T17:47:48Z INFO tuxedo_template_wallet] Number of blocks in the db: 52
[2023-04-11T17:47:48Z WARN tuxedo_template_wallet] Skipping sync with node. Using previously synced information.
[2023-04-11T17:47:48Z INFO tuxedo_template_wallet] No Wallet Command invoked. Exiting.
Now that we understand that the wallet syncs up with the node each time it starts, let's explore our first wallet command. Like most wallets, it will tell you how many tokens you own.
$ tuxedo-template-wallet show-balance
[2023-04-11T18:07:52Z INFO tuxedo_template_wallet] Number of blocks in the db: 52
[2023-04-11T18:07:52Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 55
Balance Summary
--------------------
total : 0
The wallet begins by syncing the blockchain state, as usual. Then it tells us that we don't have any addresses or coins yet.
To start, let's create and address. The wallet supports generating our own keys, or inserting pre-existing keys. To follow this guide as closely as possible, you should insert the same key we generated.
# Generate a new key
$ tuxedo-template-wallet generate-key
Generated public key is f41a866782d45a4d2d8a623a097c62aee6955a9e580985e3910ba49eded9e06b (5HamRMAa...)
Generated Phrase is decide city tattoo arrest jeans split main sad slam blame crack farm
# Or, to continue on with demo, insert the same generated key
$ tuxedo-template-wallet insert-key "decide city tattoo arrest jeans split main sad slam blame crack farm"
The generated public key is f41a866782d45a4d2d8a623a097c62aee6955a9e580985e3910ba49eded9e06b (5HamRMAa...)
$ tuxedo-template-wallet show-balance
[2023-04-11T18:54:42Z INFO tuxedo_template_wallet] Number of blocks in the db: 99
[2023-04-11T18:54:42Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 101
Balance Summary
0xf41a…e06b: 0
--------------------
total : 0
So now we have an address, but we still don't have any coins.
The chain begins with a single coin worth 100 units owned by a test account.
The wallet software knows about the test account and is able to access it using the --dev
flag.
Let's confirm this by showing the balance again.
$ tuxedo-template-wallet --dev show-balance
[2024-05-16T14:25:32Z INFO tuxedo_template_wallet::sync] Initializing fresh sync from genesis 0xe709c08c291e68de1126c7eba54465a599318b1ea4030dfa5b552d3f6c42b5d3
[2024-05-16T14:25:32Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2024-05-16T14:25:32Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 144
Balance Summary
0xd2bf…df67: 100
--------------------
total : 100
The first thing to notice is that the wallet synced all the way from block 0.
A --dev
wallet will always perform a complete resync into a new temporary directory when it is started up.
This facilitates easy testing during development without having to manually purge wallet state, and also explains why our previously-generated address no longer shows up.
This flag mirror's the node's own --dev flag
.
The second thing to notice is that the wallet already knows about the test account. And the final thing is that we do indeed have some coins worth a total of 100 units.
Let's see the details about which coins we own.
$ tuxedo-template-wallet --dev show-all-outputs
[2024-05-16T14:26:22Z INFO tuxedo_template_wallet::sync] Initializing fresh sync from genesis 0xe709c08c291e68de1126c7eba54465a599318b1ea4030dfa5b552d3f6c42b5d3
[2024-05-16T14:26:22Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2024-05-16T14:26:22Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 152
###### Unspent outputs ###########
50cd896f64b101540e68c57fe644c8c9ef5dbf0aa6ece24839812e16014abe9700000000: owner 0xd2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67, amount 100
We can confirm that the node and the wallet are familiar with the genesis coin using the verify-coin
subcommand.
$ tuxedo-template-wallet --dev verify-coin 50cd896f64b101540e68c57fe644c8c9ef5dbf0aa6ece24839812e16014abe9700000000
[2024-05-16T14:27:43Z INFO tuxedo_template_wallet::sync] Initializing fresh sync from genesis 0xe709c08c291e68de1126c7eba54465a599318b1ea4030dfa5b552d3f6c42b5d3
[2024-05-16T14:27:43Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2024-05-16T14:27:43Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 166
Details of coin 50cd896f64b101540e68c57fe644c8c9ef5dbf0aa6ece24839812e16014abe9700000000:
Found in storage. Value: 100, owned by 0xd2bf…df67
Found in local db. Value: 100, owned by 0xd2bf…df67
After syncing, it tells us the status of the coin that we are asking about.
That number ending with the 0
s is called an OutputRef
and it is a unique way to refer to a utxo.
The wallet tells us that the coin is found in the chain's storage and in the wallet's own local db.
Both sources agree that the coin exists, is worth 100, and is owned by the test account.
Let's "split" this coin by creating a transaction that spends it and creates two new coins worth 40 and 50, burning the remaining 10.
$ tuxedo-template-wallet --dev spend-coins \
--output-amount 40 \
--output-amount 50
[2023-04-11T17:58:00Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2023-04-11T17:58:00Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 167
[2023-04-11T17:58:00Z INFO tuxedo_template_wallet] Node's response to spend transaction: Ok("0xad0de5922a27fab1a3ce116868ada789677c80a0e70018bd32464b2e737d3546")
Created "9b3b0d17ad5f7784e840c40089d4d0aa0de990c5c620d49a0729c3a45afa35bf00000000" worth 40. owned by 0xd2bf…df67
Created "9b3b0d17ad5f7784e840c40089d4d0aa0de990c5c620d49a0729c3a45afa35bf01000000" worth 50. owned by 0xd2bf…df67
Our command told the wallet to create a transaction that spends some coins (in this case the genesis coin) and creates two new coins with the given amounts, burning the remaining 10.
It also tells us the OutputRef
s of the new coins created.
A balance check reveals that our balance has decreased by the 10 burnt tokes as expected.
$ tuxedo-template-wallet --dev show-balance
[2023-04-11T18:52:26Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2023-04-11T18:52:26Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 170
Balance Summary
0xd2bf…df67: 90
--------------------
total : 90
In this case we didn't specify a recipient of the new outputs, so the same default address was used. Now let's explore sending to our own address from the previous step.
$ tuxedo-template-wallet --dev spend-coins \
--recipient f41a866782d45a4d2d8a623a097c62aee6955a9e580985e3910ba49eded9e06b \
--output-amount 20 \
--output-amount 10
[2023-04-11T18:53:46Z INFO tuxedo_template_wallet] Number of blocks in the db: 0
[2023-04-11T18:53:46Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 171
[2023-04-11T18:53:46Z INFO tuxedo_template_wallet::money] Node's response to spend transaction: Ok("0x7b8466f6c418637958f8090304dbdd7f115c27abf787b8f034a41d522bdf2baf")
Created "90695702dabcca93d2c5f84a45b07bf59626ddb49a9b5255e202777127a3323d00000000" worth 20. owned by 0xf41a…e06b
Created "90695702dabcca93d2c5f84a45b07bf59626ddb49a9b5255e202777127a3323d01000000" worth 10. owned by 0xf41a…e06b
This command will consume one of the existing coins, and create two new ones owned by our key. Our new coins will be worth 20 and 10 tokens.
As an exercise, check the test account's balance, and then return to your wallet, and check your own balance. From now on we will use our own wallet, but you may find the dev wallet very useful when developing.
Another option for getting some coins would have been minting them. Although minting will usually not be available in production chains, it is useful when developing and the wallet supports it. By default we mint a coin of value 100 to the test account, but we can optionally pass the amount and public key of the owner as arguments.
$ tuxedo-template-wallet mint-coins \
--owner f41a866782d45a4d2d8a623a097c62aee6955a9e580985e3910ba49eded9e06b \
--amount 200
[2024-01-18T14:22:19Z INFO tuxedo_template_wallet] Number of blocks in the db: 6
[2024-01-18T14:22:19Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 14
[2024-01-18T14:22:19Z INFO tuxedo_template_wallet::money] Node's response to mint-coin transaction: Ok("0xaff830b7755fee67c288afe18dfa6eabffe06286005b0fd6cb8e57b246c08df6")
Created "f76373909591d85f796c36ed4b265e46efabdf5b5c493b94246d590823cc42a500000000" worth 200. owned by 0xdeba…3341
It is possible to verify a newly minted coin exists in both chain storage and the local database using verify-coin
command.
So far, we have let the wallet select which inputs to spend on our behalf. This is typically fine, but some users like to select specific inputs for their transactions. The wallet supports this. But before we can spend specific inputs, let's learn how to print the complete list of unspent outputs.
$ tuxedo-template-wallet show-all-outputs
[2023-04-11T18:55:23Z INFO tuxedo_template_wallet] Number of blocks in the db: 101
[2023-04-11T18:55:23Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 104
###### Unspent outputs ###########
90695702dabcca93d2c5f84a45b07bf59626ddb49a9b5255e202777127a3323d00000000: owner 0xf41a866782d45a4d2d8a623a097c62aee6955a9e580985e3910ba49eded9e06b, amount 20
90695702dabcca93d2c5f84a45b07bf59626ddb49a9b5255e202777127a3323d01000000: owner 0xf41a866782d45a4d2d8a623a097c62aee6955a9e580985e3910ba49eded9e06b, amount 10
9b3b0d17ad5f7784e840c40089d4d0aa0de990c5c620d49a0729c3a45afa35bf01000000: owner 0xd2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67, amount 50
Now that we know precisely which outputs exist in our chain, we can choose to spend a specific one.
Let's consume our 20 token input and send 15 of its coins to Shawn, burning the remaining 5.
Because we are sending to Shawn, and Shawn is the default recipient, we could leave off the --recipient
flag, but I'll choose to include it anyway.
# The input value has to be copied from your own `show-all-outputs` results
$ tuxedo-template-wallet spend-coins \
--input 90695702dabcca93d2c5f84a45b07bf59626ddb49a9b5255e202777127a3323d00000000 \
--recipient 0xd2bf4b844dfefd6772a8843e669f943408966a977e3ae2af1dd78e0f55f4df67 \
--output-amount 15
[2023-04-11T18:57:20Z INFO tuxedo_template_wallet] Number of blocks in the db: 94
[2023-04-11T18:57:20Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 133
[2023-04-11T18:57:20Z INFO tuxedo_template_wallet::money] Node's response to spend transaction: Ok("0x80018b868d1e29be5cb758e15618091da8185cd7256ae3338df4605732fcfe9f")
Created "4788fd9d517af94c2cfac80cb23fa6a63c41784b6fab01efd5d33b907af2550500000000" worth 15. owned by 0xd2bf…df67
You should confirm for yourself that both the balance summary and the complete list of UTXOs look as you expect.
The final wallet feature that we will demonstrate is its ability to construct transactions with inputs coming from multiple different owners.
Here we will create a transaction with a single output worth 70 units owned by some address that we'll call Jose, and we'll let the wallet select the inputs itself. This will require inputs from both Shawn and us, and the wallet is able to handle this.
$ tuxedo-template-wallet spend-coins \
--recipient 0x066ae8f6f5c3f04e7fc163555d6ef62f6f8878435a931ba7eaf02424a16afe62 \
--output-amount 70
[2023-04-11T18:59:18Z INFO tuxedo_template_wallet] Number of blocks in the db: 146
[2023-04-11T18:59:18Z INFO tuxedo_template_wallet] Wallet database synchronized with node to height 173
[2023-04-11T18:59:19Z INFO tuxedo_template_wallet::money] Node's response to spend transaction: Ok("0x04efb1c55f4efacbe41d00d3c5fe554470328a37150df6053bd48088e73a023c")
Created "d0f722019e05863769e64ac6d33ad3ebeb359ce0469e93a9856bfcc236c4bad700000000" worth 70. owned by 0x066a…fe62
Now we check the balance summary and find it is empty. That is because Jose's keys are not in the keystore, so the wallet does not track his tokens.