Update ERC-20 readme (#377)

Update ERC-20 readme to indicate the new contract name
and add TransferFrom tutorial for Go chaincode.

Signed-off-by: David Enyeart <enyeart@us.ibm.com>
This commit is contained in:
denyeart 2020-11-22 01:54:25 -05:00 committed by GitHub
parent 1829666e3a
commit 75f491f2e4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,6 +1,6 @@
# Account-based token scenario
# ERC-20 token scenario
The account-based token smart contract demonstrates how to create and transfer fungible tokens using an account-based model. In an account-based model, there is an account for each participant that holds a balance of tokens.
The ERC-20 token smart contract demonstrates how to create and transfer fungible tokens using an account-based model. In an ERC-20 account-based model, there is an account for each participant that holds a balance of tokens.
A mint transaction creates tokens in an account, while a transfer transaction debits the caller's account and credits another account.
In this sample it is assumed that only one organization (played by Org1) is in a central banker role and can mint new tokens into their account, while any organization can transfer tokens from their account to a recipient's account.
@ -9,12 +9,12 @@ The client ID is simply a base64-encoded concatenation of the issuer and subject
In this tutorial, you will mint and transfer tokens as follows:
- A member of Org1 uses the `Mint` function to create new tokens into their account. The `Mint` function reads the certificate information of the client identity that submitted the transaction using the `GetClientIdentity.GetID()` API and credits the account associated with the client ID with the requested number of tokens.
- A member of Org1 uses the `Mint` function to create new tokens into their account. The `Mint` smart contract function reads the certificate information of the client identity that submitted the transaction using the `GetClientIdentity.GetID()` API and credits the account associated with the client ID with the requested number of tokens.
- The same minter client will then use the `Transfer` function to transfer the requested number of tokens to the recipient's account. It is assumed that the recipient has provided their account ID to the transfer caller out of band. The recipient can then transfer tokens to other registered users in the same fashion.
## Bring up the test network
You can run the account-based token transfer scenario using the Fabric test network. Open a command terminal and navigate to the test network directory in your local clone of the `fabric-samples`. We will operate from the `test-network` directory for the remainder of the tutorial.
You can run the ERC-20 token transfer scenario using the Fabric test network. Open a command terminal and navigate to the test network directory in your local clone of the `fabric-samples`. We will operate from the `test-network` directory for the remainder of the tutorial.
```
cd fabric-samples/test-network
```
@ -29,19 +29,19 @@ The -ca flag is used to deploy the network using certificate authorities. This a
## Deploy the smart contract to the channel
You can use the test network script to deploy the account-based token contract to the channel that was just created. Deploy the smart contract to `mychannel` using the following command:
You can use the test network script to deploy the ERC-20 token contract to the channel that was just created. Deploy the smart contract to `mychannel` using the following command:
**For a Go Contract:**
```
./network.sh deployCC -ccn token_account -ccp ../token-erc-20/chaincode-go/
./network.sh deployCC -ccn token_erc20 -ccp ../token-erc-20/chaincode-go/
```
**For a JavaScript Contract:**
```
./network.sh deployCC -ccn token_account -ccp ../token-erc-20/chaincode-javascript/ -ccl javascript
./network.sh deployCC -ccn token_erc20 -ccp ../token-erc-20/chaincode-javascript/ -ccl javascript
```
The above command deploys the go chaincode with short name `token_account`. The smart contract will use the default endorsement policy of majority of channel members.
The above command deploys the go chaincode with short name `token_erc20`. The smart contract will use the default endorsement policy of majority of channel members.
Since the channel has two members, this implies that we'll need to get peer endorsements from 2 out of the 2 channel members.
Now you are ready to call the deployed smart contract via peer CLI calls. But let's first create the client identities for our scenario.
@ -50,7 +50,7 @@ Now you are ready to call the deployed smart contract via peer CLI calls. But le
The smart contract supports accounts owned by individual client identities from organizations that are members of the channel. In our scenario, the minter of the tokens will be a member of Org1, while the recipient will belong to Org2. To highlight the connection between the `GetClientIdentity().GetID()` API and the information within a user's certificate, we will register two new identities using the Org1 and Org2 Certificate Authorities (CA's), and then use the CA's to generate each identity's certificate and private key.
First, we need to set the following environment variables to use the the Fabric CA client (and subsequent commands).
First, we need to set the following environment variables to use the Fabric CA client (and subsequent commands).
```
export PATH=${PWD}/../bin:${PWD}:$PATH
export FABRIC_CFG_PATH=$PWD/../config/
@ -66,7 +66,7 @@ You can register a new minter client identity using the `fabric-ca-client` tool:
fabric-ca-client register --caname ca-org1 --id.name minter --id.secret minterpw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
```
You can now generate the identity certificates and MSP folder by providing the enroll name and secret to the enroll command:
You can now generate the identity certificates and MSP folder by providing the minter's enroll name and secret to the enroll command:
```
fabric-ca-client enroll -u https://minter:minterpw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/minter@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
```
@ -88,7 +88,7 @@ You can register a recipient client identity using the `fabric-ca-client` tool:
fabric-ca-client register --caname ca-org2 --id.name recipient --id.secret recipientpw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem
```
We can now enroll to generate the identity certificates and MSP folder:
We can now enroll to generate the recipient's identity certificates and MSP folder:
```
fabric-ca-client enroll -u https://recipient:recipientpw@localhost:8054 --caname ca-org2 -M ${PWD}/organizations/peerOrganizations/org2.example.com/users/recipient@org2.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org2/tls-cert.pem
```
@ -115,12 +115,12 @@ The last environment variable above will be utilized within the CLI invoke comma
We can then invoke the smart contract to mint 5000 tokens:
```
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Mint","Args":["5000"]}'
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"Mint","Args":["5000"]}'
```
The mint function validated that the client is a member of the minter organization, and then credited the minter client's account with 5000 tokens. We can check the minter client's account balance by calling the `ClientAccountBalance` function.
```
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountBalance","Args":[]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"ClientAccountBalance","Args":[]}'
```
The function queries the balance of the account associated with the minter client ID and returns:
@ -144,7 +144,7 @@ export CORE_PEER_ADDRESS=localhost:9051
Using the Org2 terminal, the Org2 recipient user can retrieve their own account ID:
```
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountID","Args":[]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"ClientAccountID","Args":[]}'
```
**For a Go Contract:**
@ -177,13 +177,14 @@ Back in the Org1 terminal, request the transfer of 100 tokens to the recipient a
**For a Go Contract:**
```
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Transfer","Args":[ "eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw==","100"]}'
export RECIPIENT="eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw=="
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"Transfer","Args":[ "'"$RECIPIENT"'","100"]}'
```
**For a JavaScript Contract:**
```
export RECIPIENT="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=recipient::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com"
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Transfer","Args":[ "'"$RECIPIENT"'","100"]}'
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"Transfer","Args":[ "'"$RECIPIENT"'","100"]}'
```
The `Transfer` function validates that the account associated with the calling client ID has sufficient funds for the transfer.
@ -191,7 +192,7 @@ It will then debit the caller's account and credit the recipient's account. Note
While still in the Org1 terminal, let's request the minter's account balance again:
```
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountBalance","Args":[]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"ClientAccountBalance","Args":[]}'
```
The function queries the balance of the account associated with the minter client ID and returns:
@ -201,7 +202,7 @@ The function queries the balance of the account associated with the minter clien
And then using the Org2 terminal, let's request the recipient's balance:
```
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountBalance","Args":[]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"ClientAccountBalance","Args":[]}'
```
The function queries the balance of the account associated with the recipient client ID and returns:
@ -211,17 +212,17 @@ The function queries the balance of the account associated with the recipient cl
Congratulations, you've transferred 100 tokens! The Org2 recipient can now transfer tokens to other registered users in the same manner.
## Another scenario (for JavaScript contract only)
## 3rd party transfers (TransferFrom)
This sample has another transfer method called `transferFrom`, which allows an approved spender to transfer fungible tokens on behalf of the account owner. The second scenario demonstrates how to approve the spender and transfer fungible tokens.
This sample has another ERC-20 transfer method called `TransferFrom`, which allows an approved 3rd party spender to transfer fungible tokens on behalf of the account owner. This scenario demonstrates how to approve the spender and transfer fungible tokens.
In this tutorial, you will approve the spender and transfer tokens as follows:
In this scenario, you will approve the spender and transfer tokens as follows:
- A minter has already created tokens according to the scenario above.
- The same minter client uses the `approve` function to set the allowance of tokens a spender client can transfer on behalf of the minter. It is assumed that the spender has provided their client ID to the `approve` caller out of band.
- The spender client will then use the `transferFrom` function to transfer the requested number of tokens to the recipient's account on behalf of the minter. It is assumed that the recipient has provided their client ID to the `transferFrom` caller out of band.
- The same minter client uses the `Approve` function to set the allowance of tokens a spender client can transfer on behalf of the minter. It is assumed that the spender has provided their client ID to the `Approve` caller out of band.
- The spender client will then use the `TransferFrom` function to transfer the requested number of tokens to the recipient's account on behalf of the minter. It is assumed that the recipient has provided their client ID to the `TransferFrom` caller out of band.
## Register identities
## Register identity for 3rd party spender
You have already brought up the network and deployed the smart contract to the channel. We will use the same network and smart contract.
@ -231,7 +232,7 @@ Back in the Org1 terminal, you can register a new spender client identity using
fabric-ca-client register --caname ca-org1 --id.name spender --id.secret spenderpw --id.type client --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
```
You can now generate the identity certificates and MSP folder by providing the enroll name and secret to the enroll command:
You can now generate the identity certificates and MSP folder by providing the spender's enroll name and secret to the enroll command:
```
fabric-ca-client enroll -u https://spender:spenderpw@localhost:7054 --caname ca-org1 -M ${PWD}/organizations/peerOrganizations/org1.example.com/users/spender@org1.example.com/msp --tls.certfiles ${PWD}/organizations/fabric-ca/org1/tls-cert.pem
```
@ -260,9 +261,18 @@ export TARGET_TLS_OPTIONS="-o localhost:7050 --ordererTLSHostnameOverride ordere
Now the Org1 spender can retrieve their own client ID:
```
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountID","Args":[]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"ClientAccountID","Args":[]}'
```
**For a Go Contract:**
The function returns of spender's account ID:
```
eDUwOTo6Q049c3BlbmRlcixPVT1jbGllbnQsTz1IeXBlcmxlZGdlcixTVD1Ob3J0aCBDYXJvbGluYSxDPVVTOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT
```
**For a JavaScript Contract:**
The function returns of spender's client ID.
The result shows that the subject and issuer is indeed the recipient user from Org2:
```
@ -270,18 +280,38 @@ x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=spender::/C=US/ST=North
```
After the Org1 spender provides their client ID to the minter, the minter can approve a spender.
Back in the Org1 terminal, request the approval of 100 tokens to be withdrew by the spender.:
Back in the Org1 minter terminal, request the approval of 500 tokens to be withdrawn by the spender.
**For a Go Contract:**
```
export SPENDER="eDUwOTo6Q049c3BlbmRlcixPVT1jbGllbnQsTz1IeXBlcmxlZGdlcixTVD1Ob3J0aCBDYXJvbGluYSxDPVVTOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT"
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"Approve","Args":[ "'"$SPENDER"'","500"]}'
```
**For a JavaScript Contract:**
```
export SPENDER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=spender::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"Approve","Args":["'"$SPENDER"'", "500"]}'
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"Approve","Args":["'"$SPENDER"'", "500"]}'
```
The approve function added that the spender client can consume 500 tokens on behalf of the minter. We can check the spender client's allowance from the minter by calling the `allowance` function.
The approve function specified that the spender client can transfer 500 tokens on behalf of the minter. We can check the spender client's allowance from the minter by calling the `allowance` function.
Let's request the spender's allowance from the Org1 minter terminal.
**For a Go Contract:**
```
export MINTER="eDUwOTo6Q049bWludGVyLE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzEuZXhhbXBsZS5jb20sTz1vcmcxLmV4YW1wbGUuY29tLEw9RHVyaGFtLFNUPU5vcnRoIENhcm9saW5hLEM9VVM="
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
```
**For a JavaScript Contract:**
Let's request the spender's allowance from the minter:
```
export MINTER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=minter::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
peer chaincode query -C mychannel -n token_account -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
```
The function queries the allowance associated with the spender client ID and returns:
@ -293,19 +323,30 @@ The function queries the allowance associated with the spender client ID and ret
The spender intends to transfer 100 tokens to the Org2 recipient on behalf of the minter. The spender has already got the minter client Id and the recipient client ID.
Back in the 3rd terminal, request the transfer of 100 tokens to the recipient account:
Back in the 3rd terminal, request the transfer of 100 tokens to the recipient account.
**For a Go Contract:**
```
export MINTER="eDUwOTo6Q049bWludGVyLE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzEuZXhhbXBsZS5jb20sTz1vcmcxLmV4YW1wbGUuY29tLEw9RHVyaGFtLFNUPU5vcnRoIENhcm9saW5hLEM9VVM="
export RECIPIENT="eDUwOTo6Q049cmVjaXBpZW50LE9VPWNsaWVudCxPPUh5cGVybGVkZ2VyLFNUPU5vcnRoIENhcm9saW5hLEM9VVM6OkNOPWNhLm9yZzIuZXhhbXBsZS5jb20sTz1vcmcyLmV4YW1wbGUuY29tLEw9SHVyc2xleSxTVD1IYW1wc2hpcmUsQz1VSw=="
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"TransferFrom","Args":[ "'"$MINTER"'", "'"$RECIPIENT"'", "100"]}'
```
**For a JavaScript Contract:**
```
export MINTER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=minter::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
export RECIPIENT="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=recipient::/C=UK/ST=Hampshire/L=Hursley/O=org2.example.com/CN=ca.org2.example.com"
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_account -c '{"function":"TransferFrom","Args":[ "'"$MINTER"'", "'"$RECIPIENT"'", "100"]}'
peer chaincode invoke $TARGET_TLS_OPTIONS -C mychannel -n token_erc20 -c '{"function":"TransferFrom","Args":[ "'"$MINTER"'", "'"$RECIPIENT"'", "100"]}'
```
The `TransferFrom` function has three args: sender, recipient, amount. The function validates that the account associated with the sender has sufficient funds for the transfer. The function also validates if the allowance associated with the calling client ID exceeds funds to be transferred.
It will then debit the sender's account and credit the recipient's account. It will also decrease the spender's allowance approved by the minter. Note that the sample contract will automatically create an account with zero balance for the recipient, if one does not yet exist.
While still in the 3rd terminal, let's request the minter's account balance again:
While still in the 3rd terminal for the spender, let's request the minter's account balance again:
```
peer chaincode query -C mychannel -n token_account -c '{"function":"BalanceOf","Args":["'"$MINTER"'"]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"BalanceOf","Args":["'"$MINTER"'"]}'
```
The function queries the balance of the account associated with the minter client ID and returns:
@ -313,10 +354,20 @@ The function queries the balance of the account associated with the minter clien
4800
```
While still in the 3rd terminal, let's request the spender's allowance from the minter again:
While still in the 3rd terminal for the spender, let's request the spender's allowance from the minter again.
**For a Go Contract:**
```
export SPENDER="eDUwOTo6Q049c3BlbmRlcixPVT1jbGllbnQsTz1IeXBlcmxlZGdlcixTVD1Ob3J0aCBDYXJvbGluYSxDPVVTOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT"
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
```
**For a JavaScript Contract:**
```
export SPENDER="x509::/C=US/ST=North Carolina/O=Hyperledger/OU=client/CN=spender::/C=US/ST=North Carolina/L=Durham/O=org1.example.com/CN=ca.org1.example.com"
peer chaincode query -C mychannel -n token_account -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"Allowance","Args":["'"$MINTER"'", "'"$SPENDER"'"]}'
```
The function queries the allowance associated with the spender client ID and returns:
@ -326,7 +377,7 @@ The function queries the allowance associated with the spender client ID and ret
And then using the Org2 terminal, let's request the recipient's balance:
```
peer chaincode query -C mychannel -n token_account -c '{"function":"ClientAccountBalance","Args":[]}'
peer chaincode query -C mychannel -n token_erc20 -c '{"function":"ClientAccountBalance","Args":[]}'
```
The function queries the balance of the account associated with the recipient client ID and returns:
@ -345,8 +396,8 @@ When you are finished, you can bring down the test network. The command will rem
## Contract extension ideas
You can extend the basic account-based token sample to meet other requirements. For example:
You can extend the basic ERC-20 account-based token sample to meet other requirements. For example:
* Rather than using the default 'majority' endorsement policy, you could set the endorsement policy to a subset of organizations that represent trust anchors for the contract execution.
* You could also require that accounts get setup before use, and apply state-based endorsement for each account key that has been created. For example on an OrgA account, set state-based endorsement policy to be OrgA and the central banker (or some other trust anchor). And on an OrgB account, set state-based endorsement policy to be OrgB and the central banker (or some other trust anchor). Then to transfer tokens from an OrgA account to an OrgB account, you would require endorsements from OrgA, OrgB, and the central banker (or some other trust anchor).
* You could also require that accounts get setup before use, and apply state-based endorsement for each account key that has been created. For example on an Org1 account, set state-based endorsement policy to be Org1 and the central banker (or some other trust anchor). And on an Org2 account, set state-based endorsement policy to be Org2 and the central banker (or some other trust anchor). Then to transfer tokens from an Org1 account to an Org2 account, you would require endorsements from Org1, Org2, and the central banker (or some other trust anchor).
* You could utilize anonymous addresses for accounts based on private-public key pairs, instead of accounts keyed by the client ID. In order to spend the tokens, the client would have to sign the transfer input as proof that they own the address private key, which the contract would then validate, similar to the Ethereum model in the permissionless blockchain space. However, in a permissioned blockchain such as Fabric, only registered clients are authorized to participate. Furthermore, if you don't want to leak the registered client identity associated with each account, the clients could be registered using an Identity Mixer MSP, so that the client itself is also anonymous in each of the token transactions.