Hyperledger Indy Technical Deep Dive - Handout
Workshop Prerequisites
Please complete the instructions found here in order to be prepared for the hands on portion of the workshop. If you have questions about these prerequisites, we'll do our best to provide assistance on RocketChat in the #indy channel.
Useful Links and Additional Reading
How to install the Indy CLI: See Appendix A of the Indicio Selfserve Instructions
Go to this link then click appropriate directories to see examples of Genesis files, TAA, AML, and auth_rules.
TAA Walkthrough - instructions for adding a TAA to a network.
BFT Algorithms in Hyperledger Fabric and Indy - An excellent discussion and comparison of Consensus Algorithms in Indy and Fabric.
Hands-On Guide
We will be extending the nym transaction in indy plenum to include an image field. We are working from the 'ubuntu-20.04-upgrade' branch.
To get started we will create a test that writes a nym with an image, then retrieves it from the ledger, and finally checks the returned image is correct.
Inindy_node/test/nym_txn/test_nym_additional.pyaround line 90 add this test.test_nym_img
def test_nym_img(looper, sdk_pool_handle, sdk_wallet_steward, endorser_did_verkey): # prepare txn did, verkey = endorser_did_verkey nym_request, _ = looper.loop.run_until_complete( prepare_nym_request(sdk_wallet_steward, None, None, ENDORSER_STRING, did, verkey, False if verkey else True)) # modify transaction to include image hyperledger_logo = 'https://raw.githubusercontent.com/hyperledger/indy-node/master/collateral/logos/indy-logo.png' txn = json.loads(nym_request) txn['operation']['img'] = hyperledger_logo # don't do this, avoid private data on ledgers request_couple = sdk_sign_and_send_prepared_request(looper, sdk_wallet_steward, sdk_pool_handle, json.dumps(txn)) sdk_get_and_check_replies(looper, [request_couple]) # check image is in the nym rep = get_nym(looper, sdk_pool_handle, sdk_wallet_steward, did) assert rep[0][1]['result']['data'] assert json.loads(rep[0][1]['result']['data'])['img'] == hyperledger_logoInside
indy_node/test/nym_txndirectory,pytest -k test_nym_imgshould result in a failing test.
Now that we have a failing test we can start updating nym write transaction types. In
indy_common/types.pythere is a class calledClientOperationFieldaround line 470. This class extends operations. Looks like Indy Node does not change the basic nym operations, we will borrow some code from Indy Plenum that defines a new operation. We will define in the next steps a new ClientNYMOperation. Since we are here let's add the instantiation inside _specific_operations.nym operation
... class ClientOperationField(PClientOperationField): _specific_operations = { NYM: ClientNYMOperation(), # <----- new operation SCHEMA: ClientSchemaOperation(), ATTRIB: ClientAttribOperation(), ...Around line 72 we will copy in the
ClientNYMOperationclass from Indy Plenum with our extraimgfield.nym operation
class ClientNYMOperation(MessageValidator): schema = ( (TXN_TYPE, ConstantField(NYM)), (ALIAS, LimitedLengthStringField(max_length=ALIAS_FIELD_LIMIT, optional=True)), (VERKEY, VerkeyField(optional=True, nullable=True)), (TARGET_NYM, DestNymField()), (ROLE, RoleField(optional=True)), (IMG, LimitedLengthStringField(max_length=RAW_FIELD_LIMIT, optional=True)) # <----- new image field )We also need to update some imports. Some of which we will define in the next steps.
nym operation
... # include ALIAS, VERKEY from plenum.common.constants from plenum.common.constants import TARGET_NYM, NONCE, RAW, ENC, HASH, NAME, \ VERSION, FORCE, ORIGIN, OPERATION_SCHEMA_IS_STRICT, OP_VER, ALIAS, VERKEY ... # include VerkeyField, DestNymField from plenum.common.messages.fields from plenum.common.messages.fields import ConstantField, IdentifierField, \ ... AnyMapField, NonEmptyStringField, DatetimeStringField, RoleField, AnyField, FieldBase, VerkeyField, DestNymField ... from plenum.config import JSON_FIELD_LIMIT, NAME_FIELD_LIMIT, DATA_FIELD_LIMIT, \ NONCE_FIELD_LIMIT, \ ENC_FIELD_LIMIT, RAW_FIELD_LIMIT, SIGNATURE_TYPE_FIELD_LIMIT, ALIAS_FIELD_LIMIT ... # include NYM, IMG from indy_common.constants from indy_common.constants import TXN_TYPE, ATTRIB, GET_ATTR, \ ... RICH_SCHEMA_PRES_DEF, RS_PRES_DEF_TYPE_VALUE, NYM, IMG
Now that we have a test, updated operator field we need to define the '
NYM' string constant. Insideindy_common/constants.pydefine a newNYMconstant around line 14.nym constant
# NYM IMG = "img"
With the client operation for nym updated we will have transactions with our new "img" field. We now can update the nym handler.
inside
indy_node/server/request_handlers/domain_req_handlers/nym_handler.pywe will add a check for theimgfield, if present, store it. On line 78, add the check inside ofupdate_statewith supportingNYMimport.nym handler
... from indy_common.constants import IMG, NYM <--- include NYM ... def update_state(self, txn, prev_result, request, is_committed=False): ... if IMG in txn_data: # check for IMG field new_data[IMG] = txn_data[IMG] # store IMG ...
Inside
indy_node/test/nym_txndirectory,pytest -k test_nym_imgshould result in a passing test.Celebrate!
You can find this coding example here, with a diff highlighting changes.