github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/examples/chaincode/go/asset_management02/depository_handler.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  		 http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"errors"
    21  
    22  	"encoding/json"
    23  	"fmt"
    24  
    25  	"github.com/hyperledger/fabric/core/chaincode/shim"
    26  )
    27  
    28  //DepositoryHandler provides APIs used to perform operations on CC's KV store
    29  type depositoryHandler struct {
    30  }
    31  
    32  // NewDepositoryHandler create a new reference to CertHandler
    33  func NewDepositoryHandler() *depositoryHandler {
    34  	return &depositoryHandler{}
    35  }
    36  
    37  type depositoryAccount struct {
    38  	AccountID   string `json:"account_id"`
    39  	ContactInfo string `json:"contact_info"`
    40  	Amount      uint64 `json:"amount"`
    41  }
    42  
    43  // assign allocates assets to account IDs in the chaincode state for each of the
    44  // account ID passed in.
    45  // accountID: account ID to be allocated with requested amount
    46  // contactInfo: contact information of the owner of the account ID passed in
    47  // amount: amount to be allocated to this account ID
    48  func (t *depositoryHandler) assign(stub shim.ChaincodeStubInterface,
    49  	accountID string,
    50  	contactInfo string,
    51  	amount uint64) error {
    52  
    53  	myLogger.Debugf("insert accountID= %v", accountID)
    54  
    55  	// you can only assign balances to new account IDs
    56  	accountBytes, err := stub.GetState(accountID)
    57  	if err == nil && len(accountBytes) > 0 {
    58  		myLogger.Errorf("system error %v", err)
    59  		return errors.New("Asset was already assigned.")
    60  	}
    61  
    62  	account := depositoryAccount{
    63  		AccountID:   accountID,
    64  		ContactInfo: contactInfo,
    65  		Amount:      amount,
    66  	}
    67  	accountBytes, err = json.Marshal(account)
    68  	if err != nil {
    69  		myLogger.Errorf("account marshaling error %v", err)
    70  		return errors.New("Failed to serialize account info." + err.Error())
    71  	}
    72  
    73  	//update this account that includes contact information and balance
    74  	err = stub.PutState(accountID, accountBytes)
    75  	return err
    76  }
    77  
    78  // updateAccountBalance updates the balance amount of an account ID
    79  // stub: chaincodestub
    80  // accountID: account will be updated with the new balance
    81  // contactInfo: contact information associated with the account owner (chaincode table does not allow me to perform updates on specific columns)
    82  // amount: new amount to be udpated with
    83  func (t *depositoryHandler) updateAccountBalance(stub shim.ChaincodeStubInterface,
    84  	accountID string,
    85  	contactInfo string,
    86  	amount uint64) error {
    87  
    88  	myLogger.Debugf("insert accountID= %v", accountID)
    89  
    90  	//replace the old record row associated with the account ID with the new record row
    91  	account := depositoryAccount{
    92  		AccountID:   accountID,
    93  		ContactInfo: contactInfo,
    94  		Amount:      amount,
    95  	}
    96  	accountBytes, err := json.Marshal(account)
    97  	if err != nil {
    98  		myLogger.Errorf("account marshaling error %v", err)
    99  		return errors.New("Failed to serialize account info." + err.Error())
   100  	}
   101  
   102  	//update this account that includes contact information and balance
   103  	err = stub.PutState(accountID, accountBytes)
   104  	return err
   105  }
   106  
   107  // deleteAccountRecord deletes the record row associated with an account ID on the chaincode state table
   108  // stub: chaincodestub
   109  // accountID: account ID (record matching this account ID will be deleted after calling this method)
   110  func (t *depositoryHandler) deleteAccountRecord(stub shim.ChaincodeStubInterface, accountID string) error {
   111  
   112  	myLogger.Debugf("insert accountID= %v", accountID)
   113  
   114  	//delete record matching account ID passed in
   115  	err := stub.DelState(accountID)
   116  
   117  	if err != nil {
   118  		myLogger.Errorf("system error %v", err)
   119  		return errors.New("error in deleting account record")
   120  	}
   121  	return nil
   122  }
   123  
   124  // transfer transfers X amount of assets from "from account IDs" to a new account ID
   125  // stub: chaincodestub
   126  // fromAccounts: from account IDs with assets to be transferred
   127  // toAccount: a new account ID on the table that will get assets transfered to
   128  // toContact: contact information of the owner of "to account ID"
   129  func (t *depositoryHandler) transfer(stub shim.ChaincodeStubInterface, fromAccounts []string, toAccount string, toContact string, amount uint64) error {
   130  
   131  	myLogger.Debugf("insert params= %v , %v , %v , %v ", fromAccounts, toAccount, toContact, amount)
   132  
   133  	//collecting assets need to be transfered
   134  	remaining := amount
   135  	for i := range fromAccounts {
   136  		contactInfo, acctBalance, err := t.queryAccount(stub, fromAccounts[i])
   137  		if err != nil {
   138  			myLogger.Errorf("system error %v", err)
   139  			return errors.New("error in deleting account record")
   140  		}
   141  
   142  		if remaining > 0 {
   143  			//check if this account need to be spent entirely; if so, delete the
   144  			//account record row, otherwise just take out what' needed
   145  			if remaining >= acctBalance {
   146  				remaining -= acctBalance
   147  				//delete accounts with 0 balance, this step is optional
   148  				t.deleteAccountRecord(stub, fromAccounts[i])
   149  			} else {
   150  				acctBalance -= remaining
   151  				remaining = 0
   152  				t.updateAccountBalance(stub, fromAccounts[i], contactInfo, acctBalance)
   153  				break
   154  			}
   155  		}
   156  	}
   157  
   158  	//check if toAccount already exist
   159  	acctBalance, err := t.queryBalance(stub, toAccount)
   160  	if err == nil || acctBalance > 0 {
   161  		myLogger.Errorf("system error %v", err)
   162  		return errors.New("error in deleting account record")
   163  	}
   164  
   165  	//create new toAccount in the Chaincode state table, and assign the total amount
   166  	//to its balance
   167  	return t.assign(stub, toAccount, toContact, amount)
   168  
   169  }
   170  
   171  // queryContactInfo queries the contact information matching a correponding account ID on the chaincode state table
   172  // stub: chaincodestub
   173  // accountID: account ID
   174  func (t *depositoryHandler) queryContactInfo(stub shim.ChaincodeStubInterface, accountID string) (string, error) {
   175  	account, err := t.queryTable(stub, accountID)
   176  	if err != nil {
   177  		return "", err
   178  	}
   179  
   180  	return account.ContactInfo, nil
   181  }
   182  
   183  // queryBalance queries the balance information matching a correponding account ID on the chaincode state table
   184  // stub: chaincodestub
   185  // accountID: account ID
   186  func (t *depositoryHandler) queryBalance(stub shim.ChaincodeStubInterface, accountID string) (uint64, error) {
   187  
   188  	myLogger.Debugf("insert accountID= %v", accountID)
   189  
   190  	account, err := t.queryTable(stub, accountID)
   191  	if err != nil {
   192  		return 0, err
   193  	}
   194  
   195  	return account.Amount, nil
   196  }
   197  
   198  // queryAccount queries the balance and contact information matching a correponding account ID on the chaincode state table
   199  // stub: chaincodestub
   200  // accountID: account ID
   201  func (t *depositoryHandler) queryAccount(stub shim.ChaincodeStubInterface, accountID string) (string, uint64, error) {
   202  	account, err := t.queryTable(stub, accountID)
   203  	if err != nil {
   204  		return "", 0, err
   205  	}
   206  
   207  	return account.ContactInfo, account.Amount, nil
   208  }
   209  
   210  // queryTable returns the record row matching a correponding account ID on the chaincode state table
   211  // stub: chaincodestub
   212  // accountID: account ID
   213  func (t *depositoryHandler) queryTable(stub shim.ChaincodeStubInterface, accountID string) (*depositoryAccount, error) {
   214  
   215  	accountBytes, err := stub.GetState(accountID)
   216  	if err != nil {
   217  		return nil, errors.New("Failed to get account." + err.Error())
   218  	}
   219  	if len(accountBytes) == 0 {
   220  		return nil, fmt.Errorf("Account %s not exists.", accountID)
   221  	}
   222  
   223  	account := &depositoryAccount{}
   224  	err = json.Unmarshal(accountBytes, account)
   225  	if err != nil {
   226  		return nil, errors.New("Failed to parse account Info. " + err.Error())
   227  	}
   228  	return account, nil
   229  }