org.hyperledger.fabric.sdk.Member.java Source code

Java tutorial

Introduction

Here is the source code for org.hyperledger.fabric.sdk.Member.java

Source

/*
 *  Copyright 2016 DTCC, Fujitsu Australia Software Technology - 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.
 *  You may obtain a copy of the License at
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License 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.
 */

package org.hyperledger.fabric.sdk;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.bouncycastle.util.encoders.Hex;
import org.hyperledger.fabric.sdk.exception.CryptoException;
import org.hyperledger.fabric.sdk.exception.EnrollmentException;
import org.hyperledger.fabric.sdk.exception.ChainCodeException;
import org.hyperledger.fabric.sdk.exception.NoAvailableTCertException;
import org.hyperledger.fabric.sdk.exception.NoValidPeerException;
import org.hyperledger.fabric.sdk.exception.RegistrationException;
import org.hyperledger.fabric.sdk.transaction.TransactionContext;

import io.netty.util.internal.StringUtil;

public class Member implements Serializable {
    private static final long serialVersionUID = 8077132186383604355L;

    private static final Log logger = LogFactory.getLog(Member.class);

    private transient Chain chain;
    private String name;
    private ArrayList<String> roles;
    private String account;
    private String affiliation;
    private String enrollmentSecret;
    private Enrollment enrollment = null;
    private transient MemberServices memberServices;
    private transient KeyValStore keyValStore;
    private String keyValStoreName;
    private Map<String, TCertGetter> tcertGetterMap;
    private int tcertBatchSize;

    /**
     * Constructor for a member.
     * @param name The member name
     * @param chain
     */

    public Member(String name, Chain chain) {
        if (chain == null) {
            throw new IllegalArgumentException("A valid chain must be provided");
        }

        this.name = name;
        this.chain = chain;
        this.memberServices = chain.getMemberServices();
        this.keyValStore = chain.getKeyValStore();
        this.keyValStoreName = toKeyValStoreName(this.name);
        this.tcertBatchSize = chain.getTCertBatchSize();
        this.tcertGetterMap = new HashMap<String, TCertGetter>();
    }

    /**
     * Get the member name.
     * @return {string} The member name.
     */
    public String getName() {
        return this.name;
    }

    /**
     * Get the chain.
     * @return {Chain} The chain.
     */
    public Chain getChain() {
        return this.chain;
    }

    /**
     * Get the member services.
     * @return {MemberServices} The member services.
     */

    public MemberServices getMemberServices() {
        return this.memberServices;
    }

    /**
     * Get the roles.
     * @return {string[]} The roles.
     */
    public ArrayList<String> getRoles() {
        return this.roles;
    }

    /**
     * Set the roles.
     * @param roles {string[]} The roles.
     */
    public void setRoles(ArrayList<String> roles) {
        this.roles = roles;
    }

    /**
     * Get the account.
     * @return {string} The account.
     */
    public String getAccount() {
        return this.account;
    }

    /**
     * Set the account.
     * @param account The account.
     */
    public void setAccount(String account) {
        this.account = account;
    }

    /**
     * Get the affiliation.
     * @return {string} The affiliation.
     */
    public String getAffiliation() {
        return this.affiliation;
    }

    /**
     * Set the affiliation.
     * @param affiliation The affiliation.
     */
    public void setAffiliation(String affiliation) {
        this.affiliation = affiliation;
    }

    /**
     * Get the transaction certificate (tcert) batch size, which is the number of tcerts retrieved
     * from member services each time (i.e. in a single batch).
     * @return The tcert batch size.
     */
    public int getTCertBatchSize() {
        if (this.tcertBatchSize <= 0) {
            return this.chain.getTCertBatchSize();
        } else {
            return this.tcertBatchSize;
        }
    }

    /**
     * Set the transaction certificate (tcert) batch size.
     * @param batchSize
     */
    public void setTCertBatchSize(int batchSize) {
        this.tcertBatchSize = batchSize;
    }

    /**
     * Get the enrollment logger.info.
     * @return {Enrollment} The enrollment.
     */
    public Enrollment getEnrollment() {
        return this.enrollment;
    };

    /**
     * Determine if this name has been registered.
     * @return {boolean} True if registered; otherwise, false.
     */
    public boolean isRegistered() {
        return this.isEnrolled() || !StringUtil.isNullOrEmpty(enrollmentSecret);
    }

    /**
     * Determine if this name has been enrolled.
     * @return {boolean} True if enrolled; otherwise, false.
     */
    public boolean isEnrolled() {
        return this.enrollment != null;
    }

    /**
     * Register the member.
     * @param registrationRequest the registration request
     * @throws RegistrationException
     */
    public void register(RegistrationRequest registrationRequest) throws RegistrationException {
        if (!registrationRequest.getEnrollmentID().equals(getName())) {
            throw new RuntimeException("registration enrollment ID and member name are not equal");
        }

        this.enrollmentSecret = memberServices.register(registrationRequest, chain.getRegistrar());
        this.saveState();
    }

    /**
     * Enroll the member and return the enrollment results.
     * @param enrollmentSecret The password or enrollment secret as returned by register.
     * @return enrollment details
     * @throws EnrollmentException
     */
    public Enrollment enroll(String enrollmentSecret) throws EnrollmentException {
        EnrollmentRequest req = new EnrollmentRequest();
        req.setEnrollmentID(getName());
        req.setEnrollmentSecret(enrollmentSecret);
        logger.debug(String.format("Enrolling [req=%s]", req));

        this.enrollment = memberServices.enroll(req);
        this.saveState();
        return this.enrollment;
    }

    /**
     * Perform both registration and enrollment.
     * @throws RegistrationException
     * @throws EnrollmentException
     */
    public void registerAndEnroll(RegistrationRequest registrationRequest)
            throws RegistrationException, EnrollmentException {
        register(registrationRequest);
        enroll(this.enrollmentSecret);
    }

    /**
     * Issue a deploy request on behalf of this member
     * @param deployRequest {@link DeployRequest}
     * @return {@link ChainCodeResponse} response to chain code deploy transaction
     * @throws ChainCodeException if the deployment fails.
     */
    public ChainCodeResponse deploy(DeployRequest deployRequest)
            throws ChainCodeException, NoAvailableTCertException, CryptoException, IOException {
        logger.debug("Member.deploy");

        if (getChain().getPeers().isEmpty()) {
            throw new NoValidPeerException(String.format("chain %s has no peers", getChain().getName()));
        }

        TransactionContext tcxt = this.newTransactionContext(null);
        return tcxt.deploy(deployRequest);
    }

    /**
     * Issue a invoke request on behalf of this member
     * @param invokeRequest {@link InvokeRequest}
     * @throws ChainCodeException if the chain code invocation fails
     */
    public ChainCodeResponse invoke(InvokeRequest invokeRequest)
            throws ChainCodeException, NoAvailableTCertException, CryptoException, IOException {
        logger.debug("Member.invoke");

        if (getChain().getPeers().isEmpty()) {
            throw new NoValidPeerException(String.format("chain %s has no peers", getChain().getName()));
        }

        TransactionContext tcxt = this.newTransactionContext(null);
        return tcxt.invoke(invokeRequest);
    }

    /**
     * Issue a query request on behalf of this member.
     * @param queryRequest {@link QueryRequest}
     * @throws ChainCodeException if the query transaction fails
     */
    public ChainCodeResponse query(QueryRequest queryRequest)
            throws ChainCodeException, NoAvailableTCertException, CryptoException, IOException {
        logger.debug("Member.query");

        if (getChain().getPeers().isEmpty()) {
            throw new NoValidPeerException(String.format("chain %s has no peers", getChain().getName()));
        }

        TransactionContext tcxt = this.newTransactionContext(null);
        return tcxt.query(queryRequest);
    }

    /**
     * Create a transaction context with which to issue build, deploy, invoke, or query transactions.
     * Only call this if you want to use the same tcert for multiple transactions.
     * @param tcert A transaction certificate from member services.  This is optional.
     * @return A transaction context.
     */
    public TransactionContext newTransactionContext(TCert tcert) {
        return new TransactionContext(this, tcert);
    }

    /**
     * Get a user certificate.
     * @param attrs The names of attributes to include in the user certificate.
     */
    public void getUserCert(List<String> attrs) {
        this.getNextTCert(attrs);
    }

    /**
     * Get the next available transaction certificate with the appropriate attributes.
     */
    public TCert getNextTCert(List<String> attrs) {
        if (!isEnrolled()) {
            throw new RuntimeException(String.format("user '%s' is not enrolled", this.getName()));
        }
        String key = getAttrsKey(attrs);

        logger.debug(String.format("Member.getNextTCert: key=%s", key));
        TCertGetter tcertGetter = this.tcertGetterMap.get(key);
        if (tcertGetter == null) {
            logger.debug(String.format("Member.getNextTCert: key=%s, creating new getter", key));
            tcertGetter = new TCertGetter(this, attrs, key);
            this.tcertGetterMap.put(key, tcertGetter);
        }
        return tcertGetter.getNextTCert();
    }

    private String getAttrsKey(List<String> attrs) {
        if (attrs == null || attrs.isEmpty())
            return null;
        return String.join(",", attrs);
    }

    /**
     * Save the state of this member to the key value store.
     */
    public void saveState() {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            oos.flush();
            keyValStore.setValue(keyValStoreName, Hex.toHexString(bos.toByteArray()));
            bos.close();
        } catch (IOException e) {
            logger.debug(String.format("Could not save state of member %s", this.name), e);
        }
    }

    /**
     * Restore the state of this member from the key value store (if found).  If not found, do nothing.
     */
    public void restoreState() {
        String memberStr = keyValStore.getValue(keyValStoreName);
        if (null != memberStr) {
            // The member was found in the key value store, so restore the
            // state.
            byte[] serialized = Hex.decode(memberStr);
            ByteArrayInputStream bis = new ByteArrayInputStream(serialized);
            try {
                ObjectInputStream ois = new ObjectInputStream(bis);
                Member state = (Member) ois.readObject();
                if (state != null) {
                    this.name = state.name;
                    this.roles = state.roles;
                    this.account = state.account;
                    this.affiliation = state.affiliation;
                    this.enrollmentSecret = state.enrollmentSecret;
                    this.enrollment = state.enrollment;
                } else {
                    logger.debug(String.format("Could not find member %s from keyvalue store", this.name));
                }
            } catch (IOException | ClassNotFoundException e) {
                logger.debug(String.format("Could not restore state of member %s", this.name), e);
            }
        }
    }

    public String getEnrollmentSecret() {
        return enrollmentSecret;
    }

    public void setEnrollmentSecret(String enrollmentSecret) {
        this.enrollmentSecret = enrollmentSecret;
    }

    public void setEnrollment(Enrollment enrollment) {
        this.enrollment = enrollment;
    }

    private String toKeyValStoreName(String name) {
        return "member." + name;
    }
}