github.com/inklabsfoundation/inkchain@v0.17.1-0.20181025012015-c3cef8062f19/core/scc/ascc/ascc.go (about)

     1  /*
     2  Copyright Ziggurat Corp. 2017 All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package ascc
     8  
     9  import (
    10  	"encoding/json"
    11  	"fmt"
    12  	"math/big"
    13  	"strconv"
    14  
    15  	"github.com/inklabsfoundation/inkchain/common/flogging"
    16  	"github.com/inklabsfoundation/inkchain/core/chaincode/shim"
    17  	"github.com/inklabsfoundation/inkchain/core/policy"
    18  	"github.com/inklabsfoundation/inkchain/core/policyprovider"
    19  	"github.com/inklabsfoundation/inkchain/msp/mgmt"
    20  	pb "github.com/inklabsfoundation/inkchain/protos/peer"
    21  	"bytes"
    22  )
    23  
    24  // Create Logger
    25  var tralogger = flogging.MustGetLogger("ascc")
    26  
    27  // These are function names from Invoke first parameter
    28  const (
    29  	//invoke functions
    30  	RegisterAndIssueToken	string = "registerAndIssueToken"
    31  	InvalidateToken			string = "invalidateToken"
    32  	QueryToken				string = "queryToken"
    33  
    34  	//token status
    35  	Created    string = "created"
    36  	Delivered  string = "issued"
    37  	Invalidate string = "invalidated"
    38  )
    39  
    40  // type GenAccount
    41  type Token struct {
    42  	// token name
    43  	Name string `json:"tokenName"`
    44  	// total supply of the token
    45  	totalSupply *big.Int `json:"totalSupply"`
    46  	// initial address to issue
    47  	Address string `json:"address"`
    48  	// token status : Created, Delivered, Invalidate
    49  	Status string `json:"status"`
    50  	// token decimals
    51  	Decimals int `json:"decimals"`
    52  }
    53  
    54  //-------------- the ascc ------------------
    55  type AssetSysCC struct {
    56  	// policyChecker is the interface used to perform
    57  	// access control
    58  	policyChecker policy.PolicyChecker
    59  }
    60  
    61  // Init initializes ascc
    62  func (t *AssetSysCC) Init(stub shim.ChaincodeStubInterface) pb.Response {
    63  	tralogger.Info("Init ascc")
    64  
    65  	// Init policy checker for access control
    66  	t.policyChecker = policyprovider.GetPolicyChecker()
    67  
    68  	return shim.Success(nil)
    69  }
    70  
    71  // Invoke func
    72  func (t *AssetSysCC) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
    73  	function, args := stub.GetFunctionAndParameters()
    74  
    75  	tralogger.Debugf("ascc starts: %d args", len(args))
    76  	// Handle ACL of ascc:
    77  	// 1. get the signed proposal
    78  	//	sp, err := stub.GetSignedProposal()
    79  	//	if err != nil {
    80  	//		return shim.Error(fmt.Sprintf("Failed retrieving signed proposal on executing %s with error %s", function, err))
    81  	//	}
    82  	switch function {
    83  
    84  	case RegisterAndIssueToken:
    85  		if len(args) != 4 { //name, totalSupply, decimals, address
    86  			returnMessage := fmt.Sprint("Incorrect number of arguments for IssueToken, %d", len(args))
    87  			tralogger.Debugf("Incorrect number of arguments for IssueToken, %d", len(args))
    88  			return shim.Error(returnMessage)
    89  		}
    90  
    91  		// 2. check local MSP Admins policy
    92  
    93  		sp, err := stub.GetSignedProposal()
    94  		if err != nil {
    95  			return shim.Error(fmt.Sprintf("Failed retrieving signed proposal on executing %s with error %s", function, err))
    96  		}
    97  
    98  		if err := t.policyChecker.CheckPolicyNoChannel(mgmt.Admins, sp); err != nil {
    99  			tralogger.Debugf(fmt.Sprintf("Authorization for RegisterAndIssueToken has been denied (error-%s)", err))
   100  			return shim.Error(fmt.Sprintf("Authorization for RegisterAndIssueToken has been denied (error-%s)", err))
   101  		}
   102  		tralogger.Debugf("Invoke function: %s", RegisterAndIssueToken)
   103  		return t.registerAndIssueToken(stub, args)
   104  
   105  	case InvalidateToken:
   106  		if len(args) != 1 { //name
   107  			returnMessage := fmt.Sprint("Incorrect number of arguments for InvalidateToken, %d", len(args))
   108  			tralogger.Debugf("Incorrect number of arguments for InvalidateToken, %d", len(args))
   109  			return shim.Error(returnMessage)
   110  		}
   111  
   112  		tralogger.Debugf("Invoke function: %s", InvalidateToken)
   113  
   114  		return t.invalidateToken(stub, args)
   115  
   116  	case QueryToken:
   117  		if len(args) < 1{
   118  			returnMessage := fmt.Sprint("Need at least 1 arguments for QueryToken, %d", len(args))
   119  			tralogger.Debugf("Need at least 1 arguments for QueryToken, %d", len(args))
   120  			return shim.Error(returnMessage)
   121  		}
   122  
   123  		tralogger.Debugf("Invoke function: %s", QueryToken)
   124  
   125  		return t.queryToken(stub, args)
   126  
   127  	}
   128  
   129  	return shim.Error("Invalid invoke function name. Expecting \"registerAndIssueToken\" or \"invalidateToken\" or \"queryToken\".")
   130  }
   131  
   132  // issue Tokens Invoke
   133  // invoke function
   134  func (t *AssetSysCC) registerAndIssueToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   135  	var err error
   136  
   137  	tokenName := args[0]
   138  
   139  	totalSupply := big.NewInt(0)
   140  	_, good := totalSupply.SetString(args[1], 10)
   141  	if !good {
   142  		return shim.Error("Expecting integer value for totalSupply.")
   143  	}
   144  	dec, _ := strconv.Atoi(args[2])
   145  	addr := args[3]
   146  
   147  	//Get exist token
   148  	var existToken Token
   149  	existTokenBytes, err := stub.GetState(tokenName)
   150  	if err != nil {
   151  		msgCheck := "Check token existance error, fail to getState of "
   152  		msgCheck += tokenName
   153  		tralogger.Debug(msgCheck)
   154  		return shim.Error(msgCheck)
   155  	}
   156  
   157  	//Get the information of token
   158  	//If not exist, create a new token first
   159  	if existTokenBytes == nil {
   160  		//not exist
   161  		//create the token
   162  		existToken.Status = Created
   163  		existToken.Name = tokenName
   164  		existToken.totalSupply = totalSupply
   165  		existToken.Address = addr
   166  		existToken.Decimals = dec
   167  	} else {
   168  		//exist
   169  		//unmarshal the jsonBytes & check token information
   170  		err = json.Unmarshal(existTokenBytes, &existToken)
   171  		if err != nil {
   172  			msgUnmarshal := "Unmarshal exist tokenBytes err "
   173  			msgUnmarshal += tokenName
   174  			tralogger.Debug(msgUnmarshal)
   175  			return shim.Error(msgUnmarshal)
   176  		}
   177  		//check the status of token
   178  		if existToken.Status != Created {
   179  			msgCheckTS := "Token status err, fail to issue token."
   180  			tralogger.Debug(msgCheckTS)
   181  			return shim.Error(msgCheckTS)
   182  		}
   183  		//check the information of token
   184  		if existToken.Address != addr || existToken.totalSupply.Cmp(totalSupply) != 0 || existToken.Decimals != dec {
   185  			msgCheckTInfo := "Token info err, check fialed."
   186  			tralogger.Debug(msgCheckTInfo)
   187  			return shim.Error(msgCheckTInfo)
   188  		}
   189  	}
   190  
   191  	//set the token number to address
   192  
   193  	//get account of Address
   194  	account, err := stub.GetAccount(addr)
   195  	//check if token has been issued before
   196  	if err == nil {
   197  		if account != nil {
   198  			if _, ok := account.Balance[tokenName]; ok {
   199  				msgBalanceCheck := "Token " + tokenName + " already exist in " + addr
   200  				tralogger.Debug(msgBalanceCheck)
   201  				return shim.Error(msgBalanceCheck)
   202  			}
   203  		}
   204  	}
   205  	//token hasnot been issued, then
   206  	//issue token
   207  	err = stub.IssueToken(addr, tokenName, totalSupply)
   208  	if err != nil {
   209  		return shim.Error(err.Error())
   210  	}
   211  
   212  	existToken.Status = Delivered
   213  
   214  	//store the latest status for token in ascc
   215  	existTokenJson, err := json.Marshal(&existToken)
   216  	err = stub.PutState(tokenName, existTokenJson)
   217  
   218  	if err != nil {
   219  		msgUpdate := "Store the latest token status err."
   220  		tralogger.Debug(msgUpdate)
   221  		return shim.Error(msgUpdate)
   222  	}
   223  
   224  	return shim.Success([]byte("Token issued success!"))
   225  }
   226  
   227  // invalidate Tokens Invoke
   228  // invoke function
   229  func (t *AssetSysCC) invalidateToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   230  	var err error
   231  
   232  	tokenName := args[0]
   233  
   234  	//Get exist token
   235  	var existToken Token
   236  	existTokenBytes, err := stub.GetState(tokenName)
   237  	if err != nil {
   238  		msgCheck := "Check token existance error, fail to getState of "
   239  		msgCheck += tokenName
   240  		tralogger.Debug(msgCheck)
   241  		return shim.Error(msgCheck)
   242  	}
   243  
   244  	//Check token status
   245  	//If not exist, return err
   246  	if existTokenBytes == nil {
   247  		msgCheckExist := "Token not exist, fail to get status: "
   248  		msgCheckExist += tokenName
   249  		tralogger.Debug(msgCheckExist)
   250  		return shim.Error(msgCheckExist)
   251  	}
   252  
   253  	//Unmarshal tokenBytes
   254  	err = json.Unmarshal(existTokenBytes, &existToken)
   255  	if err != nil {
   256  		msgUnmarshal := "Unmarshal exist tokenBytes err "
   257  		msgUnmarshal += tokenName
   258  		tralogger.Debug(msgUnmarshal)
   259  		return shim.Error(msgUnmarshal)
   260  	}
   261  
   262  	//check the status of token
   263  	if existToken.Status == Invalidate {
   264  		return shim.Error("Token already invalidated.")
   265  	}
   266  
   267  	existToken.Status = Invalidate
   268  
   269  	//store the latest status for token
   270  	existTokenJson, err := json.Marshal(&existToken)
   271  	err = stub.PutState(tokenName, existTokenJson)
   272  
   273  	if err != nil {
   274  		msgUpdate := "Store the latest token status err."
   275  		tralogger.Debug(msgUpdate)
   276  		return shim.Error(msgUpdate)
   277  	}
   278  
   279  	return shim.Success([]byte("Token invalidate success!"))
   280  }
   281  
   282  // query Token Invoke
   283  // implement for multi-token search
   284  // invoke function
   285  func (t *AssetSysCC) queryToken(stub shim.ChaincodeStubInterface, args []string) pb.Response {
   286  
   287  	// buffer is a JSON array containing QueryResults
   288  	var buffer bytes.Buffer
   289  	buffer.WriteString("[")
   290  
   291  	bArrayMemberAlreadyWritten := false
   292  	bArrayIndex := 1
   293  
   294  	// check every token to be searched
   295  	for i:=0; i<len(args); i++ {
   296  		tokenName := args[i]
   297  		tokenAsBytes, err := stub.GetState(tokenName)
   298  		if err != nil {
   299  			continue
   300  		}
   301  		if tokenAsBytes == nil {
   302  			continue
   303  		}
   304  
   305  		if bArrayMemberAlreadyWritten == true {
   306  			buffer.WriteString(",")
   307  		}
   308  		// index of the result
   309  		buffer.WriteString("{\"Number\":")
   310  		buffer.WriteString("\"")
   311  		bArrayIndexStr := strconv.Itoa(bArrayIndex)
   312  		buffer.WriteString(string(bArrayIndexStr))
   313  		bArrayIndex += 1
   314  		buffer.WriteString("\"")
   315  		// information about current asset
   316  		buffer.WriteString(", \"Record\":")
   317  		buffer.WriteString(string(tokenAsBytes))
   318  		buffer.WriteString("}")
   319  		bArrayMemberAlreadyWritten = true
   320  
   321  	}
   322  
   323  	buffer.WriteString("]")
   324  
   325  	return shim.Success(buffer.Bytes())
   326  }