323 lines
9.2 KiB
ReStructuredText
323 lines
9.2 KiB
ReStructuredText
This guide uses the roles functionality provided by ``basecli`` to
|
|
create a multi-sig wallet. It builds upon the basecoin basics and key
|
|
management guides. You should have ``basecoin`` started with blocks
|
|
streaming in, and three accounts: ``rich, poor, igor`` where ``rich``
|
|
was the account used on ``basecoin init``, *and* run ``basecli init``
|
|
with the appropriate flags. Review the intro guides for more
|
|
information.
|
|
|
|
In this example, ``rich`` will create the role and send it some coins
|
|
(i.e., fill the multi-sig wallet). Then, ``poor`` will prepare a
|
|
transaction to withdraw coins, which will be approved by ``igor``. Let's
|
|
look at our keys:
|
|
|
|
::
|
|
|
|
basecli keys list
|
|
|
|
::
|
|
|
|
All keys:
|
|
igor 5E4CB7A4E729BA0A8B18DE99E21409B6D706D0F1
|
|
poor 65D406E028319289A0706E294F3B764F44EBA3CF
|
|
rich CB76F4092D1B13475272B36585EBD15D22A2848D
|
|
|
|
Using the ``basecli query account`` command, you'll see that ``rich``
|
|
has plenty of coins:
|
|
|
|
::
|
|
|
|
{
|
|
"height": 81,
|
|
"data": {
|
|
"coins": [
|
|
{
|
|
"denom": "mycoin",
|
|
"amount": 9007199254740992
|
|
}
|
|
],
|
|
"credit": []
|
|
}
|
|
}
|
|
|
|
whereas ``poor`` and ``igor`` have no coins (in fact, the chain doesn't
|
|
know about them yet):
|
|
|
|
::
|
|
|
|
ERROR: Account bytes are empty for address 65D406E028319289A0706E294F3B764F44EBA3CF
|
|
|
|
Create Role
|
|
-----------
|
|
|
|
This first step defines the parameters of a new role, which will have
|
|
control of any coins sent to it, and only release them if correct
|
|
conditions are met. In this example, we are going to make a 2/3
|
|
multi-sig wallet. Let's look a the command and dissect it below:
|
|
|
|
::
|
|
|
|
basecli tx create-role --role=10CAFE4E --min-sigs=2 --members=5E4CB7A4E729BA0A8B18DE99E21409B6D706D0F1,65D406E028319289A0706E294F3B764F44EBA3CF,CB76F4092D1B13475272B36585EBD15D22A2848D --sequence=1 --name=rich
|
|
|
|
In the first part we are sending a transaction that creates a role,
|
|
rather than transfering coins. The ``--role`` flag is the name of the
|
|
role (in hex only) and must be in double quotes. The ``--min-sigs`` and
|
|
``--members`` define your multi-sig parameters. Here, we require a
|
|
minimum of 2 signatures out of 3 members but we could easily say 3 of 5
|
|
or 9 of 10, or whatever your application requires. The ``--members``
|
|
flag requires a comma-seperated list of addresses that will be
|
|
signatories on the role. Then we set the ``--sequence`` number for the
|
|
transaction, which will start at 1 and must be incremented by 1 for
|
|
every transaction from an account. Finally, we use the name of the
|
|
key/account that will be used to create the role, in this case the
|
|
account ``rich``.
|
|
|
|
Remember that ``rich``'s address was used on ``basecoin init`` and is
|
|
included in the ``--members`` list. The command above will prompt for a
|
|
password (which can also be piped into the command if desired) then - if
|
|
executed correctly - return some data:
|
|
|
|
::
|
|
|
|
{
|
|
"check_tx": {
|
|
"code": 0,
|
|
"data": "",
|
|
"log": ""
|
|
},
|
|
"deliver_tx": {
|
|
"code": 0,
|
|
"data": "",
|
|
"log": ""
|
|
},
|
|
"hash": "4849DA762E19CE599460B9882DD42C7F19655DC1",
|
|
"height": 321
|
|
}
|
|
|
|
showing the block height at which the transaction was committed and its
|
|
hash. A quick review of what we did: 1) created a role, essentially an
|
|
account, that requires a minimum of two (2) signatures from three (3)
|
|
accounts (members). And since it was the account named ``rich``'s first
|
|
transaction, the sequence was set to 1.
|
|
|
|
Let's look at the balance of the role that we've created:
|
|
|
|
::
|
|
|
|
basecli query account role:10CAFE4E
|
|
|
|
and it should be empty:
|
|
|
|
::
|
|
|
|
ERROR: Account bytes are empty for address role:10CAFE4E
|
|
|
|
Next, we want to send coins *to* that role. Notice that because this is
|
|
the second transaction being sent by rich, we need to increase
|
|
``--sequence`` to ``2``:
|
|
|
|
::
|
|
|
|
basecli tx send --fee=90mycoin --amount=10000mycoin --to=role:10CAFE4E --sequence=2 --name=rich
|
|
|
|
We need to pay a transaction fee to the validators, in this case 90
|
|
``mycoin`` to send 10000 ``mycoin`` Notice that for the ``--to`` flag,
|
|
to specify that we are sending to a role instead of an account, the
|
|
``role:`` prefix is added before the role. Because it's ``rich``'s
|
|
second transaction, we've incremented the sequence. The output will be
|
|
nearly identical to the output from ``create-role`` above.
|
|
|
|
Now the role has coins (think of it like a bank).
|
|
|
|
Double check with:
|
|
|
|
::
|
|
|
|
basecli query account role:10CAFE4E
|
|
|
|
and this time you'll see the coins in the role's account:
|
|
|
|
::
|
|
|
|
{
|
|
"height": 2453,
|
|
"data": {
|
|
"coins": [
|
|
{
|
|
"denom": "mycoin",
|
|
"amount": 10000
|
|
}
|
|
],
|
|
"credit": []
|
|
}
|
|
}
|
|
|
|
``Poor`` decides to initiate a multi-sig transaction to himself from the
|
|
role's account. First, it must be prepared like so:
|
|
|
|
::
|
|
|
|
basecli tx send --amount=6000mycoin --from=role:10CAFE4E --to=65D406E028319289A0706E294F3B764F44EBA3CF --sequence=1 --assume-role=10CAFE4E --name=poor --multi --prepare=tx.json
|
|
|
|
you'll be prompted for ``poor``'s password and there won't be any
|
|
``stdout`` to the terminal. Note that the address in the ``--to`` flag
|
|
matches the address of ``poor``'s account from the beginning of the
|
|
tutorial. The main output is the ``tx.json`` file that has just been
|
|
created. In the above command, the ``--assume-role`` flag is used to
|
|
evaluate account permissions on the transaction, while the ``--multi``
|
|
flag is used in combination with ``--prepare``, to specify the file that
|
|
is prepared for a multi-sig transaction.
|
|
|
|
The ``tx.json`` file will look like this:
|
|
|
|
::
|
|
|
|
{
|
|
"type": "sigs/multi",
|
|
"data": {
|
|
"tx": {
|
|
"type": "chain/tx",
|
|
"data": {
|
|
"chain_id": "test_chain_id",
|
|
"expires_at": 0,
|
|
"tx": {
|
|
"type": "nonce",
|
|
"data": {
|
|
"sequence": 1,
|
|
"signers": [
|
|
{
|
|
"chain": "",
|
|
"app": "sigs",
|
|
"addr": "65D406E028319289A0706E294F3B764F44EBA3CF"
|
|
}
|
|
],
|
|
"tx": {
|
|
"type": "role/assume",
|
|
"data": {
|
|
"role": "10CAFE4E",
|
|
"tx": {
|
|
"type": "coin/send",
|
|
"data": {
|
|
"inputs": [
|
|
{
|
|
"address": {
|
|
"chain": "",
|
|
"app": "role",
|
|
"addr": "10CAFE4E"
|
|
},
|
|
"coins": [
|
|
{
|
|
"denom": "mycoin",
|
|
"amount": 6000
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"outputs": [
|
|
{
|
|
"address": {
|
|
"chain": "",
|
|
"app": "sigs",
|
|
"addr": "65D406E028319289A0706E294F3B764F44EBA3CF"
|
|
},
|
|
"coins": [
|
|
{
|
|
"denom": "mycoin",
|
|
"amount": 6000
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"signatures": [
|
|
{
|
|
"Sig": {
|
|
"type": "ed25519",
|
|
"data": "A38F73BF2D109015E4B0B6782C84875292D5FAA75F0E3362C9BD29B16CB15D57FDF0553205E7A33C740319397A434B7C31CBB10BE7F8270C9984C5567D2DC002"
|
|
},
|
|
"Pubkey": {
|
|
"type": "ed25519",
|
|
"data": "6ED38C7453148DD90DFC41D9339CE45BEFA5EB505FD7E93D85E71DFFDAFD9B8F"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
}
|
|
|
|
and it is loaded by the next command.
|
|
|
|
With the transaction prepared, but not sent, we'll have ``igor`` sign
|
|
and send the prepared transaction:
|
|
|
|
::
|
|
|
|
basecli tx --in=tx.json --name=igor
|
|
|
|
which will give output similar to:
|
|
|
|
::
|
|
|
|
{
|
|
"check_tx": {
|
|
"code": 0,
|
|
"data": "",
|
|
"log": ""
|
|
},
|
|
"deliver_tx": {
|
|
"code": 0,
|
|
"data": "",
|
|
"log": ""
|
|
},
|
|
"hash": "E345BDDED9517EB2CAAF5E30AFF3AB38A1172833",
|
|
"height": 2673
|
|
}
|
|
|
|
and voila! That's the basics for creating roles and sending multi-sig
|
|
transactions. For 3 of 3, you'd add an intermediate transactions like:
|
|
|
|
::
|
|
|
|
basecli tx --in=tx.json --name=igor --prepare=tx2.json
|
|
|
|
before having rich sign and send the transaction. The ``--prepare`` flag
|
|
writes files to disk rather than sending the transaction and can be used
|
|
to chain together multiple transactions.
|
|
|
|
We can check the balance of the role:
|
|
|
|
::
|
|
|
|
basecli query account role:10CAFE4E
|
|
|
|
and get the result:
|
|
|
|
::
|
|
|
|
{
|
|
"height": 2683,
|
|
"data": {
|
|
"coins": [
|
|
{
|
|
"denom": "mycoin",
|
|
"amount": 4000
|
|
}
|
|
],
|
|
"credit": []
|
|
}
|
|
}
|
|
|
|
and see that ``poor`` now has 6000 ``mycoin``:
|
|
|
|
::
|
|
|
|
basecli query account 65D406E028319289A0706E294F3B764F44EBA3CF
|
|
|
|
to confirm that everything worked as expected.
|