Skip to content

Commit dde6101

Browse files
author
Vyacheslav
authored
Merge branch 'master' into feature/cli-payments-design
2 parents 3aa0d0d + ae9d722 commit dde6101

File tree

9 files changed

+250
-1
lines changed

9 files changed

+250
-1
lines changed

doc/how-tos/rotate-key/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
This shows how to change the verkey that the ledger associates with
44
a DID. It builds on ["Write DID and Query Its Verkey"](../write-did-and-query-verkey/README.md).
55

6-
[ [Python](../not-yet-written.md) | [Java](java/README.md) | [.NET](../not-yet-written.md) | [Node.js](../not-yet-written.md) | [Objective C](../not-yet-written.md) ]
6+
[ [Python](python/README.md) | [Java](java/README.md) | [.NET](../not-yet-written.md) | [Node.js](../not-yet-written.md) | [Objective C](../not-yet-written.md) ]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Rotate a Key
2+
3+
Indy-SDK Developer Walkthrough #2, Python Edition
4+
5+
[ [Java](../java/README.md) | [.NET](../../not-yet-written.md) | [Node.js](../../not-yet-written.md) | [Objective C](../../not-yet-written.md) ]
6+
7+
8+
## Prerequisites
9+
10+
Setup your workstation with an indy development virtual machine (VM). See [prerequisites](../../prerequisites.md).
11+
12+
## Steps
13+
14+
### Step 1
15+
16+
In your normal workstation operating system (not the VM), open a python editor of your
17+
choice and paste the code from [template.py](template.py)
18+
into a new doc. We will be modifying this code in later steps.
19+
20+
Save the doc as `rotate_key.py`.
21+
22+
### Step 2
23+
24+
This how-to builds on the work in
25+
["Write DID and Query Verkey"](../write-did-and-query-verkey/python/README.md).
26+
Rather than duplicate our explanation of those steps here, we will simply
27+
copy that code as our starting point.
28+
29+
Copy the contents of [step2.py](step2.py) into
30+
`rotate_key.py` on top of the `Step 2 code goes here` placeholder comment.
31+
32+
Save the updated version of `rotate_key.py`.
33+
34+
### Step 3
35+
36+
Once we have an identity on the ledger, we can rotate its key pair.
37+
38+
Copy the contents of [step3.py](step3.py) into
39+
`rotate_key.py` on top of the `Step 3 code goes here` placeholder comment.
40+
41+
Save the updated version of `rotate_key.py`.
42+
43+
Most of the logic here should be self-explanatory. However, it's worth
44+
explaining the paired functions `replace_keys_start` and `replace_keys_apply`.
45+
When we submit the update transaction to the ledger, we have to sign it
46+
using the current signing key; the ledger will verify this using the
47+
verkey that it recognizes. Yet we have to specify the new verkey value
48+
in the transaction itself. The `replace_keys_start` method tells the wallet
49+
that an update is pending, and that it should track both the new and old keypairs
50+
for the identity. The `replace_keys_apply` resolves the pending status
51+
so the new value becomes canonical in the local wallet (after it has
52+
already become canonical on the ledger).
53+
54+
### Step 4
55+
56+
Now we can query the ledger to see which verkey it has on record for the
57+
identity.
58+
59+
Copy the contents of [step4.py](step4.py) into
60+
`rotate_key.py` on top of the `Step 4 code goes here` placeholder comment.
61+
62+
Save the updated version of `rotate_key.py`.
63+
64+
Only a handful of lines of code matter to our goal here; the rest of this
65+
block is comments and boilerplate cleanup **(which you should not omit!)**.
66+
67+
### Step 5
68+
69+
Run the completed demo and observe the whole sequence.
70+
71+
## More experiments
72+
73+
TBD

doc/how-tos/rotate-key/python/__init__.py

Whitespace-only changes.

doc/how-tos/rotate-key/python/old/__init__.py

Whitespace-only changes.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Tell SDK which pool you are going to use. You should have already started
2+
# this pool using docker compose or similar. Here, we are dumping the config
3+
# just for demonstration purposes.
4+
print_log('1. Creates a new local pool ledger configuration that is used '
5+
'later when connecting to ledger.\n')
6+
pool_config = json.dumps({'genesis_txn': genesis_file_path})
7+
await pool.create_pool_ledger_config(config_name=pool_name, config=pool_config)
8+
9+
print_log('\n2. Open pool ledger and get handle from libindy\n')
10+
pool_handle = await pool.open_pool_ledger(config_name=pool_name, config=None)
11+
12+
print_log('\n3. Creating new secure wallet with the given unique name\n')
13+
await wallet.create_wallet(pool_name, wallet_name, None, None, None)
14+
15+
print_log('\n4. Open wallet and get handle from libindy to use in methods that require wallet access\n')
16+
wallet_handle = await wallet.open_wallet(wallet_name, None, None)
17+
18+
# First, put a steward DID and its keypair in the wallet. This doesn't write anything to the ledger,
19+
# but it gives us a key that we can use to sign a ledger transaction that we're going to submit later.
20+
print_log('\n5. Generating and storing steward DID and verkey\n')
21+
22+
23+
# The DID and public verkey for this steward key are already in the ledger; they were part of the genesis
24+
# transactions we told the SDK to start with in the previous step. But we have to also put the DID, verkey,
25+
# and private signing key into our wallet, so we can use the signing key to submit an acceptably signed
26+
# transaction to the ledger, creating our *next* DID (which is truly new). This is why we use a hard-coded seed
27+
# when creating this DID--it guarantees that the same DID and key material are created that the genesis txns
28+
# expect.
29+
steward_seed = "000000000000000000000000Steward1"
30+
31+
did_json = json.dumps({'seed': steward_seed})
32+
33+
steward_did, steward_verkey = await signus.create_and_store_my_did(wallet_handle, did_json)
34+
print_log('Steward DID: ', steward_did)
35+
36+
37+
# Now, create a new DID and verkey for a trust anchor, and store it in our wallet as well. Don't use a seed;
38+
# this DID and its keyas are secure and random. Again, we're not writing to the ledger yet.
39+
print_log('\n6. Generating and storing trust anchor DID and verkey\n')
40+
trust_anchor_did, trust_anchor_verkey = await signus.create_and_store_my_did(wallet_handle, "{}")
41+
print_log('Trust Anchor DID: ', trust_anchor_did)
42+
print_log('Trust Anchor Verkey: ', trust_anchor_verkey)
43+
44+
# Here, we are building the transaction payload that we'll send to write the Trust Anchor identity to the ledger.
45+
# We submit this transaction under the authority of the steward DID that the ledger already recognizes.
46+
# This call will look up the private key of the steward DID in our wallet, and use it to sign the transaction.
47+
print_log('\n7. Building NYM request to add Trust Anchor to the ledger\n')
48+
nym_transaction_request = await ledger.build_nym_request(submitter_did=steward_did,
49+
target_did=trust_anchor_did,
50+
ver_key=trust_anchor_verkey,
51+
alias=None,
52+
role='TRUST_ANCHOR')
53+
print_log('NYM request: ')
54+
pprint.pprint(json.loads(nym_transaction_request))
55+
56+
# Now that we have the transaction ready, send it. The building and the sending are separate steps because some
57+
# clients may want to prepare transactions in one piece of code (e.g., that has access to privileged backend systems),
58+
# and communicate with the ledger in a different piece of code (e.g., that lives outside the safe internal
59+
# network).
60+
print_log('\n8. Sending NYM request to the ledger\n')
61+
nym_transaction_response = await ledger.sign_and_submit_request(pool_handle=pool_handle,
62+
wallet_handle=wallet_handle,
63+
submitter_did=steward_did,
64+
request_json=nym_transaction_request)
65+
print_log('NYM response: ')
66+
pprint.pprint(json.loads(nym_transaction_response))
67+
68+
# At this point, we have successfully written a new identity to the ledger. Our next step will be to query it.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
print_log('\n9. Generating new verkey of trust anchor in wallet\n')
2+
new_verkey = await signus.replace_keys_start(wallet_handle, trust_anchor_did, "{}")
3+
print_log('New Trust Anchor Verkey: ', new_verkey)
4+
5+
print_log('\n10. Building NYM request to update new verkey to ledger\n')
6+
nym_request = await ledger.build_nym_request(trust_anchor_did, trust_anchor_did, new_verkey, None, 'TRUST_ANCHOR')
7+
print_log('NYM request:')
8+
pprint.pprint(json.loads(nym_request))
9+
10+
print_log('\n11. Sending NYM request to the ledger\n')
11+
nym_response = await ledger.sign_and_submit_request(pool_handle, wallet_handle, trust_anchor_did, nym_request)
12+
print_log('NYM response:')
13+
pprint.pprint(json.loads(nym_response))
14+
15+
print_log('\n12. Apply new verkey in wallet\n')
16+
await signus.replace_keys_apply(wallet_handle, trust_anchor_did)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
print_log('\n13. Reading new verkey from wallet\n')
2+
verkey_in_wallet = await signus.key_for_local_did(wallet_handle, trust_anchor_did)
3+
print_log('Trust Anchor Verkey in wallet: ', verkey_in_wallet)
4+
5+
print_log('\n14. Building GET_NYM request to get Trust Anchor verkey\n')
6+
get_nym_request = await ledger.build_get_nym_request(trust_anchor_did, trust_anchor_did)
7+
print_log('Get NYM request:')
8+
pprint.pprint(json.loads(get_nym_request))
9+
10+
print_log('\n15. Sending GET_NYM request to ledger\n')
11+
get_nym_response_json = await ledger.submit_request(pool_handle, get_nym_request)
12+
get_nym_response = json.loads(get_nym_response_json)
13+
print_log('GET NYM response:')
14+
pprint.pprint(get_nym_response)
15+
16+
print_log('\n16. Comparing Trust Anchor verkeys: written by Steward (original), '
17+
'current in wallet and current from ledger\n')
18+
print_log('Written by Steward: ', trust_anchor_verkey)
19+
print_log('Current in wallet: ', verkey_in_wallet)
20+
verkey_from_ledger = json.loads(get_nym_response['result']['data'])['verkey']
21+
print_log('Current from ledger: ', verkey_from_ledger)
22+
print_log('Matching: ', verkey_from_ledger == verkey_in_wallet != trust_anchor_verkey)
23+
24+
# Do some cleanup.
25+
print_log('\n17. Closing wallet and pool\n')
26+
await wallet.close_wallet(wallet_handle)
27+
await pool.close_pool_ledger(pool_handle)
28+
29+
print_log('\n18. Deleting created wallet\n')
30+
await wallet.delete_wallet(wallet_name, None)
31+
32+
print_log('\n19. Deleting pool ledger config')
33+
await pool.delete_pool_ledger_config(pool_name)
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Example demonstrating how to do the key rotation on the ledger.
3+
4+
Steward already exists on the ledger and its DID/Verkey are obtained using seed.
5+
Trust Anchor's DID/Verkey pair is generated and stored into wallet.
6+
Stewards builds NYM request in order to add Trust Anchor to the ledger.
7+
Once NYM transaction is done, Trust Anchor wants to change its Verkey.
8+
First, temporary key is created in the wallet.
9+
Second, Trust Anchor builds NYM request to replace the Verkey on the ledger.
10+
Third, when NYM transaction succeeds, Trust Anchor makes new Verkey permanent in wallet
11+
(it was only temporary before).
12+
13+
To assert the changes, Trust Anchor reads both the Verkey from the wallet and the Verkey from the ledger
14+
using GET_NYM request, to make sure they are equal to the new Verkey, not the original one
15+
added by Steward
16+
"""
17+
18+
19+
import asyncio
20+
import json
21+
import pprint
22+
23+
from indy import pool, ledger, wallet, signus
24+
from indy.error import IndyError
25+
26+
27+
pool_name = 'pool'
28+
wallet_name = 'wallet'
29+
genesis_file_path = '/home/vagrant/code/evernym/indy-sdk/cli/docker_pool_transactions_genesis'
30+
31+
32+
def print_log(value_color="", value_noncolor=""):
33+
"""set the colors for text."""
34+
HEADER = '\033[92m'
35+
ENDC = '\033[0m'
36+
print(HEADER + value_color + ENDC + str(value_noncolor))
37+
38+
39+
async def rotate_key_on_the_ledger():
40+
try:
41+
# Step 2 code goes here.
42+
43+
# Step 3 code goes here.
44+
45+
# Step 4 code goes here.
46+
47+
except IndyError as e:
48+
print('Error occurred: %s' % e)
49+
50+
51+
def main():
52+
loop = asyncio.get_event_loop()
53+
loop.run_until_complete(rotate_key_on_the_ledger())
54+
loop.close()
55+
56+
57+
if __name__ == '__main__':
58+
main()
59+

0 commit comments

Comments
 (0)