Skip to content
This repository has been archived by the owner on Jan 23, 2024. It is now read-only.

Commit

Permalink
Merge pull request #6 from MCLDG/feature/part5
Browse files Browse the repository at this point in the history
Feature/part5: Adding a new member to a Fabric network on Amazon Managed Blockchain
  • Loading branch information
MCLDG authored Mar 25, 2019
2 parents 0d73609 + 45494fd commit fcdfb44
Show file tree
Hide file tree
Showing 13 changed files with 1,138 additions and 13 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ README instructions in parts 1-4, in this order:
* [Part 2:](ngo-chaincode/README.md) Deploy the non-profit chaincode.
* [Part 3:](ngo-rest-api/README.md) Run the RESTful API server.
* [Part 4:](ngo-ui/README.md) Run the application.
* [Part 5:](new-member/README.md) Add a new member to the network.

## Cleanup

Expand Down
Binary file added images/MultimemberNetwork.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
898 changes: 898 additions & 0 deletions new-member/README.md

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions new-member/create-config-update.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#!/bin/bash

# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.

function createConfigUpdate {
echo "Creating config update payload for the new member '$MEMBERID'"
cd /opt/home

# Start the configtxlator
configtxlator start &
configtxlator_pid=$!
echo "configtxlator_pid:$configtxlator_pid"
echo "Sleeping 5 seconds for configtxlator to start..."
sleep 5

pushd /tmp
# Remove any previously generated config or protobuf files
rm /tmp/${MEMBERID}_config*.*
rm /tmp/${MEMBERID}_updated*.*

CTLURL=http://127.0.0.1:7059
# Convert the config block protobuf to JSON
curl -X POST --data-binary @$BLOCKDIR/$CHANNEL.config.block $CTLURL/protolator/decode/common.Block > ${MEMBERID}_config_block.json
# Extract the config from the config block
jq .data.data[0].payload.data.config ${MEMBERID}_config_block.json > ${MEMBERID}_config.json

isMemberInChannelConfig ${MEMBERID}_config.json
if [ $? -eq 0 ]; then
echo "Member '$MEMBERID' already exists in the channel config. Config will not be updated. Exiting createConfigUpdate"
return 1
fi

# Append the new org configuration information
jq -s '.[0] * {"channel_group":{"groups":{"Application":{"groups": {"'$MEMBERID'":.[1]}}}}}' ${MEMBERID}_config.json ${MEMBERID}.json > ${MEMBERID}_updated_config.json

# Create the config diff protobuf
curl -X POST --data-binary @${MEMBERID}_config.json $CTLURL/protolator/encode/common.Config > ${MEMBERID}_config.pb
curl -X POST --data-binary @${MEMBERID}_updated_config.json $CTLURL/protolator/encode/common.Config > ${MEMBERID}_updated_config.pb
curl -X POST -F original=@${MEMBERID}_config.pb -F updated=@${MEMBERID}_updated_config.pb $CTLURL/configtxlator/compute/update-from-configs -F channel=$CHANNEL > ${MEMBERID}_config_update.pb

# Convert the config diff protobuf to JSON
curl -X POST --data-binary @${MEMBERID}_config_update.pb $CTLURL/protolator/decode/common.ConfigUpdate > ${MEMBERID}_config_update.json

# Create envelope protobuf container config diff to be used in the "peer channel update" command to update the channel configuration block
echo '{"payload":{"header":{"channel_header":{"channel_id":"'"${CHANNEL}"'", "type":2}},"data":{"config_update":'$(cat ${MEMBERID}_config_update.json)'}}}' > ${MEMBERID}_config_update_as_envelope.json
curl -X POST --data-binary @${MEMBERID}_config_update_as_envelope.json $CTLURL/protolator/encode/common.Envelope > /tmp/${MEMBERID}_config_update_as_envelope.pb
# copy to the /data directory so the file can be signed by other admins
cp /tmp/${MEMBERID}_config_update_as_envelope.pb $BLOCKDIR

# Stop configtxlator
kill $configtxlator_pid
echo "Created config update payload for the new organization '$MEMBERID', in file ${BLOCKDIR}/${MEMBERID}_config_update_as_envelope.pb"

popd
return 0
}

# Checks whether the new member already exists in the channel config. This would be true if the member has already been added
# to the channel config
function isMemberInChannelConfig {
if [ $# -ne 1 ]; then
echo "Usage: isMemberInChannelConfig <Config JSON file>"
exit 1
fi
echo "Checking whether member '$MEMBERID' already exists in the channel config"
local JSONFILE=$1

# check if the member exists in the channel config
echo "About to execute jq '.channel_group.groups.Application.groups | contains({$MEMBERID})'"
if cat ${JSONFILE} | jq -e ".channel_group.groups.Application.groups | contains({\"$MEMBERID\"})" > /dev/null; then
echo "Member '$MEMBERID' already exists in the channel config"
return 0
else
echo "Member '$MEMBERID' does not exist in the channel config. This is expected as we are about to add the member"
return 1
fi
}

createConfigUpdate
121 changes: 121 additions & 0 deletions new-member/s3-handler.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
#!/bin/bash

# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License").
# You may not use this file except in compliance with the License.
# A copy of the License is located at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# or in the "license" file accompanying this file. This file is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
# express or implied. See the License for the specific language governing
# permissions and limitations under the License.

set +e

region=us-east-1
memberID=<your member ID, from the AWS Console>

# convert memberID to lowercase. S3 buckets must be lower case
memberID=$(echo "$memberID" | tr '[:upper:]' '[:lower:]')
S3BucketNameCreator=${memberID}-creator
S3BucketNameNewMember=${memberID}-newmember

# copy the certificates for the new Fabric member to S3
function copyCertsToS3 {
echo "Copying the certs for the new org to S3"
if [[ $(aws configure list) && $? -eq 0 ]]; then
aws s3api put-object --bucket $S3BucketNameNewMember --key ${memberID}/admincerts --body /home/ec2-user/admin-msp/admincerts/cert.pem
aws s3api put-object --bucket $S3BucketNameNewMember --key ${memberID}/cacerts --body /home/ec2-user/admin-msp/cacerts/*.pem
aws s3api put-object-acl --bucket $S3BucketNameNewMember --key ${memberID}/admincerts --acl public-read
aws s3api put-object-acl --bucket $S3BucketNameNewMember --key ${memberID}/cacerts --acl public-read
else
echo "AWS CLI is not configured on this node. To run this script install and configure the AWS CLI"
fi
echo "Copying the certs for the new org to S3 complete"
}

# copy the certificates for the new Fabric member from S3 to the Fabric creator network
function copyCertsFromS3 {
echo "Copying the certs from S3"
if [[ $(aws configure list) && $? -eq 0 ]]; then
mkdir -p /home/ec2-user/${memberID}-msp/admincerts
mkdir -p /home/ec2-user/${memberID}-msp/cacerts
aws s3api get-object --bucket $S3BucketNameNewMember --key ${memberID}/admincerts /home/ec2-user/${memberID}-msp/admincerts/cert.pem
aws s3api get-object --bucket $S3BucketNameNewMember --key ${memberID}/cacerts /home/ec2-user/${memberID}-msp/cacerts/cacert.pem
ls -lR /home/ec2-user/${memberID}-msp/
else
echo "AWS CLI is not configured on this node. To run this script install and configure the AWS CLI"
fi
echo "Copying the certs from S3 complete"
}

# copy the Channel Genesis block from the Fabric creator network to S3
function copyChannelGenesisToS3 {
echo "Copying the Channel Genesis block to S3"
if [[ $(aws configure list) && $? -eq 0 ]]; then
aws s3api put-object --bucket $S3BucketNameCreator --key org0/mychannel.block --body /home/ec2-user/fabric-samples/chaincode/hyperledger/fabric/peer/mychannel.block
aws s3api put-object-acl --bucket $S3BucketNameCreator --key org0/mychannel.block --grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers
aws s3api put-object-acl --bucket $S3BucketNameCreator --key org0/mychannel.block --acl public-read
else
echo "AWS CLI is not configured on this node. To run this script install and configure the AWS CLI"
fi
echo "Copying the Channel Genesis block to S3 complete"
}

# copy the Channel Genesis block from S3 to the new Fabric member
function copyChannelGenesisFromS3 {
echo "Copying the Channel Genesis block from S3"
if [[ $(aws configure list) && $? -eq 0 ]]; then
sudo chown -R ec2-user /home/ec2-user/fabric-samples/chaincode/hyperledger/fabric/peer
aws s3api get-object --bucket $S3BucketNameCreator --key org0/mychannel.block /home/ec2-user/fabric-samples/chaincode/hyperledger/fabric/peer/mychannel.block
else
echo "AWS CLI is not configured on this node. To run this script install and configure the AWS CLI"
fi
echo "Copying the Channel Genesis block from S3 complete"
}

# create S3 bucket to copy files from the Fabric network creator. Bucket will be read-only to other members
function createS3BucketForCreator {
#create the s3 bucket
echo -e "creating s3 bucket for network creator: $S3BucketNameCreator"
#quick way of determining whether the AWS CLI is installed and a default profile exists
if [[ $(aws configure list) && $? -eq 0 ]]; then
if [[ "$region" == "us-east-1" ]]; then
aws s3api create-bucket --bucket $S3BucketNameCreator --region $region
else
aws s3api create-bucket --bucket $S3BucketNameCreator --region $region --create-bucket-configuration LocationConstraint=$region
fi
aws s3api put-bucket-acl --bucket $S3BucketNameCreator --grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers
aws s3api put-bucket-acl --bucket $S3BucketNameCreator --acl public-read
else
echo "AWS CLI is not configured on this node. To run this script install and configure the AWS CLI"
fi
echo "Creating the S3 bucket complete"
}

# create S3 bucket to copy files from the new member. Bucket will be read-only to other members
function createS3BucketForNewMember {
#create the s3 bucket
echo -e "creating s3 bucket for new member $NEW_ORG: $S3BucketNameNewMember"
#quick way of determining whether the AWS CLI is installed and a default profile exists
if [[ $(aws configure list) && $? -eq 0 ]]; then
if [[ "$region" == "us-east-1" ]]; then
aws s3api create-bucket --bucket $S3BucketNameNewMember --region $region
else
aws s3api create-bucket --bucket $S3BucketNameNewMember --region $region --create-bucket-configuration LocationConstraint=$region
fi
aws s3api put-bucket-acl --bucket $S3BucketNameNewMember --grant-read uri=http://acs.amazonaws.com/groups/global/AllUsers
aws s3api put-bucket-acl --bucket $S3BucketNameNewMember --acl public-read
else
echo "AWS CLI is not configured on this node. To run this script install and configure the AWS CLI"
fi
echo "Creating the S3 bucket complete"
}

# This is a little hack I found here: https://stackoverflow.com/questions/8818119/how-can-i-run-a-function-from-a-script-in-command-line
# that allows me to call this bash script and invoke a specific function from the command line
"$@"

3 changes: 2 additions & 1 deletion ngo-chaincode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ Expected response:

## Step 3 - Instantiate the chaincode on the channel

Instantiation initlizes the chaincode on the channel, i.e. it binds the chaincode to a specific channel.
Instantiation initializes the chaincode on the channel, i.e. it binds the chaincode to a specific channel.
Instantiation is treated as a Fabric transaction. In fact, when chaincode is instantiated, the Init function
on the chaincode is called. Instantiation also sets the endorsement policy for this version of the chaincode
on this channel. In the example below we are not explictly passing an endorsement policy, so the default
Expand Down Expand Up @@ -169,4 +169,5 @@ The workshop instructions can be found in the README files in parts 1-4:
* [Part 2:](../ngo-chaincode/README.md) Deploy the non-profit chaincode.
* [Part 3:](../ngo-rest-api/README.md) Run the RESTful API server.
* [Part 4:](../ngo-ui/README.md) Run the application.
* [Part 5:](../new-member/README.md) Add a new member to the network.

8 changes: 4 additions & 4 deletions ngo-fabric/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ export VPCENDPOINTSERVICENAME=$(aws managedblockchain get-network --region $REGI
echo $VPCENDPOINTSERVICENAME
```

If the VPC endpoint is populated with a value, go ahead and run the script below. This will create the
CloudFormation stack. You will see an error saying `Keypair not found`. This is expected as the script
If the VPC endpoint is populated with a value, go ahead and run this script. This will create the
CloudFormation stack. You will see an error saying `key pair does not exist`. This is expected as the script
will check whether the keypair exists before creating it. I don't want to overwrite any existing
keypairs you have, so just ignore this error and let the script continue:

Expand Down Expand Up @@ -205,7 +205,7 @@ cp ~/non-profit-blockchain/ngo-fabric/configtx.yaml ~
vi ~/configtx.yaml
```

Generate the configtx channel configuration by executing the following script:
Generate the configtx channel configuration by executing the following script. When the channel is created, this channel configuration will become the genesis block (i.e. block 0) on the channel:

```
docker exec cli configtxgen -outputCreateChannelTx /opt/home/$CHANNEL.pb -profile OneOrgChannel -channelID $CHANNEL --configPath /opt/home/
Expand Down Expand Up @@ -417,4 +417,4 @@ The workshop instructions can be found in the README files in parts 1-4:
* [Part 2:](../ngo-chaincode/README.md) Deploy the non-profit chaincode.
* [Part 3:](../ngo-rest-api/README.md) Run the RESTful API server.
* [Part 4:](../ngo-ui/README.md) Run the application.

* [Part 5:](../new-member/README.md) Add a new member to the network.
1 change: 0 additions & 1 deletion ngo-fabric/templates/exports-template.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ export PEEREVENTENDPOINT=$eventEndPoint

echo Useful information used in Cloud9
echo REGION: $REGION
echo ENDPOINT: $ENDPOINT
echo NETWORKNAME: $NETWORKNAME
echo NETWORKVERSION: $NETWORKVERSION
echo ADMINUSER: $ADMINUSER
Expand Down
2 changes: 1 addition & 1 deletion ngo-rest-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,4 +227,4 @@ The workshop instructions can be found in the README files in parts 1-4:
* [Part 2:](../ngo-chaincode/README.md) Deploy the non-profit chaincode.
* [Part 3:](../ngo-rest-api/README.md) Run the RESTful API server.
* [Part 4:](../ngo-ui/README.md) Run the application.

* [Part 5:](../new-member/README.md) Add a new member to the network.
17 changes: 16 additions & 1 deletion ngo-rest-api/Troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,19 @@ error is caused by using the wrong certificate - probably an old one from the ce

[2018-11-16T10:25:40.240] [ERROR] Connection - ##### getRegisteredUser - Failed to get registered user: 5742cbbe-03b6-449d-ab65-3c885b6bfee1 with error: Error: Enrollment failed with errors [[{"code":19,"message":"CA 'ca.esxh3vewtnhsrldv5du3p52zpq' does not exist"}]]

We need to the name of the Fabric CA, as set in the CA, in FABRIC_CA_SERVER_CA_NAME
We need to set the name of the Fabric CA, as set in the CA, in FABRIC_CA_SERVER_CA_NAME



##### invokeChaincode - Invoke transaction request to Fabric {"targets":["peer1"],"chaincodeId":"ngo","fcn":"createSpend","args":["{\"spendId\":\"43a4d8be-c9f7-4d45-9f25-6074d312ee47\",\"spendDescription\":\"Peter Pipers Poulty Portions for Pets\",\"spendDate\":\"2018-09-20T12:41:59.582Z\",\"spendAmount\":22}"],"chainId":"mychannel","txId":{"_nonce":{"type":"Buffer","data":[78,51,96,123,70,26,73,101,108,20,68,49,246,213,77,198,106,37,113,217,60,230,97,118]},"_transaction_id":"c600ae42fc8ef3dffb50a2b27710d2b6488cf7bc2a90032299be78220ae0a113","_admin":false}}
[2019-03-15T05:41:41.344] [ERROR] Invoke - ##### invokeChaincode - received unsuccessful proposal response
[2019-03-15T05:41:41.344] [INFO] Invoke - ##### invokeChaincode - Failed to send Proposal and receive all good ProposalResponse. Status code: undefined, 2 UNKNOWN: access denied: channel [mychannel] creator org [org1MSP]
Error: 2 UNKNOWN: access denied: channel [mychannel] creator org [org1MSP]


The identities used by the REST API are being cached. Remove the cache directories:

```
rm -rf fabric-client-kv-org1/
rm -rf /tmp/fabric-client-kv-org1/
```
2 changes: 1 addition & 1 deletion ngo-rest-api/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ var channelName = hfc.getConfigSetting('channelName');
var chaincodeName = hfc.getConfigSetting('chaincodeName');
var peers = hfc.getConfigSetting('peers');
///////////////////////////////////////////////////////////////////////////////
//////////////////////////////// SET CONFIGURATONS ////////////////////////////
//////////////////////////////// SET CONFIGURATIONS ///////////////////////////
///////////////////////////////////////////////////////////////////////////////
app.options('*', cors());
app.use(cors());
Expand Down
2 changes: 1 addition & 1 deletion ngo-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,4 +172,4 @@ The workshop instructions can be found in the README files in parts 1-4:
* [Part 2:](../ngo-chaincode/README.md) Deploy the non-profit chaincode.
* [Part 3:](../ngo-rest-api/README.md) Run the RESTful API server.
* [Part 4:](../ngo-ui/README.md) Run the application.

* [Part 5:](../new-member/README.md) Add a new member to the network.
6 changes: 3 additions & 3 deletions ngo-ui/src/app/ui/signup/signup.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,17 +72,17 @@ export class SignupComponent implements OnInit {
},
err => {
this.loading = false;
this.error = 'Username or email already in used!';
this.error = 'Username or email already in use!';
}
);
} else {
this.loading = false;
this.error = 'Username or email already in used!';
this.error = 'Username or email already in use!';
}
},
err => {
this.loading = false;
this.error = 'Username or email already in used!';
this.error = 'Username or email already in use!';
}
);
}
Expand Down

0 comments on commit fcdfb44

Please sign in to comment.