github.com/s7techlab/cckit@v0.10.5/examples/erc20/erc20_ops.go (about)

     1  package erc20
     2  
     3  import (
     4  	"github.com/pkg/errors"
     5  	"github.com/s7techlab/cckit/identity"
     6  	r "github.com/s7techlab/cckit/router"
     7  )
     8  
     9  const (
    10  	BalancePrefix   = `BALANCE`
    11  	AllowancePrefix = `APPROVE`
    12  )
    13  
    14  var (
    15  	ErrNotEnoughFunds                   = errors.New(`not enough funds`)
    16  	ErrForbiddenToTransferToSameAccount = errors.New(`forbidden to transfer to same account`)
    17  	ErrSpenderNotHaveAllowance          = errors.New(`spender not have allowance for amount`)
    18  )
    19  
    20  type (
    21  	Transfer struct {
    22  		From   identity.Id
    23  		To     identity.Id
    24  		Amount int
    25  	}
    26  
    27  	Approve struct {
    28  		From    identity.Id
    29  		Spender identity.Id
    30  		Amount  int
    31  	}
    32  )
    33  
    34  func querySymbol(c r.Context) (interface{}, error) {
    35  	return c.State().Get(SymbolKey)
    36  }
    37  
    38  func queryName(c r.Context) (interface{}, error) {
    39  	return c.State().Get(NameKey)
    40  }
    41  
    42  func queryTotalSupply(c r.Context) (interface{}, error) {
    43  	return c.State().Get(TotalSupplyKey)
    44  }
    45  
    46  func queryBalanceOf(c r.Context) (interface{}, error) {
    47  	return getBalance(c, c.ArgString(`mspId`), c.ArgString(`certId`))
    48  }
    49  
    50  func invokeTransfer(c r.Context) (interface{}, error) {
    51  	// transfer target
    52  	toMspId := c.ParamString(`toMspId`)
    53  	toCertId := c.ParamString(`toCertId`)
    54  
    55  	//transfer amount
    56  	amount := c.ParamInt(`amount`)
    57  
    58  	// get information about tx creator
    59  	invoker, err := identity.FromStub(c.Stub())
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  
    64  	// Disallow to transfer token to same account
    65  	if invoker.GetMSPIdentifier() == toMspId && invoker.GetID() == toCertId {
    66  		return nil, ErrForbiddenToTransferToSameAccount
    67  	}
    68  
    69  	// get information about invoker balance from state
    70  	invokerBalance, err := getBalance(c, invoker.GetMSPIdentifier(), invoker.GetID())
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	// Check the funds sufficiency
    76  	if invokerBalance-amount < 0 {
    77  		return nil, ErrNotEnoughFunds
    78  	}
    79  
    80  	// Get information about recipient balance from state
    81  	recipientBalance, err := getBalance(c, toMspId, toCertId)
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	// Update payer and recipient balance
    87  	if err = setBalance(c, invoker.GetMSPIdentifier(), invoker.GetID(), invokerBalance-amount); err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	if err = setBalance(c, toMspId, toCertId, recipientBalance+amount); err != nil {
    92  		return nil, err
    93  	}
    94  
    95  	// Trigger event with name "transfer" and payload - serialized to json Transfer structure
    96  	if err = c.SetEvent(`transfer`, &Transfer{
    97  		From: identity.Id{
    98  			MSP:  invoker.GetMSPIdentifier(),
    99  			Cert: invoker.GetID(),
   100  		},
   101  		To: identity.Id{
   102  			MSP:  toMspId,
   103  			Cert: toCertId,
   104  		},
   105  		Amount: amount,
   106  	}); err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	// return current invoker balance
   111  	return invokerBalance - amount, nil
   112  }
   113  
   114  func queryAllowance(c r.Context) (interface{}, error) {
   115  	return getAllowance(c,
   116  		c.ParamString(`ownerMspId`), c.ParamString(`ownerCertId`),
   117  		c.ParamString(`spenderMspId`), c.ParamString(`spenderCertId`))
   118  }
   119  
   120  // Allow spender, identified by mspId add to spend amount
   121  func invokeApprove(c r.Context) (interface{}, error) {
   122  	spenderMspId := c.ParamString(`spenderMspId`)
   123  	spenderCertId := c.ParamString(`spenderCertId`)
   124  	amount := c.ParamInt(`amount`)
   125  
   126  	invoker, err := identity.FromStub(c.Stub())
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  
   131  	if err = setAllowance(c, invoker.GetMSPIdentifier(), invoker.GetID(), spenderMspId, spenderCertId, amount); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	if err = c.SetEvent(`approve`, &Approve{
   136  		From: identity.Id{
   137  			MSP:  invoker.GetMSPIdentifier(),
   138  			Cert: invoker.GetID(),
   139  		},
   140  		Spender: identity.Id{
   141  			MSP:  spenderMspId,
   142  			Cert: spenderCertId,
   143  		},
   144  		Amount: amount,
   145  	}); err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	return true, nil
   150  }
   151  
   152  // Transfer amount from wallet1 (from) to wallet2 (to)
   153  // wherein the initiator of the operation (tx creator) is not the owner of the wallet1
   154  // Tx creator has to have approval from owner of wallet1 for amount greater than or equal to transfer amount
   155  func invokeTransferFrom(c r.Context) (interface{}, error) {
   156  
   157  	fromMspId := c.ParamString(`fromMspId`)
   158  	fromCertId := c.ParamString(`fromCertId`)
   159  	toMspId := c.ParamString(`toMspId`)
   160  	toCertId := c.ParamString(`toCertId`)
   161  	amount := c.ParamInt(`amount`)
   162  
   163  	invoker, err := identity.FromStub(c.Stub())
   164  	if err != nil {
   165  		return nil, err
   166  	}
   167  
   168  	// check method invoker has allowances
   169  	allowance, err := getAllowance(c, fromMspId, fromCertId, invoker.GetMSPIdentifier(), invoker.GetID())
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  
   174  	// transfer amount must be less or equal allowance
   175  	if allowance < amount {
   176  		return nil, ErrSpenderNotHaveAllowance
   177  	}
   178  
   179  	// current payer balance
   180  	balance, err := getBalance(c, fromMspId, fromCertId)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	// payer balance must be greater or equal amount
   186  	if balance-amount < 0 {
   187  		return nil, ErrNotEnoughFunds
   188  	}
   189  
   190  	// current recipient balance
   191  	recipientBalance, err := getBalance(c, toMspId, toCertId)
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	// decrease payer balance
   197  	if err = setBalance(c, fromMspId, fromCertId, balance-amount); err != nil {
   198  		return nil, err
   199  	}
   200  
   201  	// increase recipient balance
   202  	if err = setBalance(c, toMspId, toCertId, recipientBalance+amount); err != nil {
   203  		return nil, err
   204  	}
   205  
   206  	// decrease invoker allowance
   207  	if err = setAllowance(c, fromMspId, fromCertId, invoker.GetMSPIdentifier(), invoker.GetID(), allowance-amount); err != nil {
   208  		return nil, err
   209  	}
   210  
   211  	if err = c.Event().Set(`transfer`, &Transfer{
   212  		From: identity.Id{
   213  			MSP:  fromMspId,
   214  			Cert: fromCertId,
   215  		},
   216  		To: identity.Id{
   217  			MSP:  toMspId,
   218  			Cert: toCertId,
   219  		},
   220  		Amount: amount,
   221  	}); err != nil {
   222  		return nil, err
   223  	}
   224  
   225  	// return current invoker balance
   226  	return balance - amount, nil
   227  }
   228  
   229  // === internal functions, not "public" chaincode functions
   230  
   231  // setBalance puts balance value to state
   232  func balanceKey(ownerMspId, ownerCertId string) []string {
   233  	return []string{BalancePrefix, ownerMspId, ownerCertId}
   234  }
   235  
   236  func allowanceKey(ownerMspId, ownerCertId, spenderMspId, spenderCertId string) []string {
   237  	return []string{AllowancePrefix, ownerMspId, ownerCertId, spenderMspId, spenderCertId}
   238  }
   239  
   240  func getBalance(c r.Context, mspId, certId string) (int, error) {
   241  	return c.State().GetInt(balanceKey(mspId, certId), 0)
   242  }
   243  
   244  // setBalance puts balance value to state
   245  func setBalance(c r.Context, mspId, certId string, balance int) error {
   246  	return c.State().Put(balanceKey(mspId, certId), balance)
   247  }
   248  
   249  // getAllowance gets amount of token allowed by wallet owner to spend by spender
   250  func getAllowance(c r.Context, ownerMspId, ownerCertId, spenderMspId, spenderCertId string) (int, error) {
   251  	return c.State().GetInt(allowanceKey(ownerMspId, ownerCertId, spenderMspId, spenderCertId), 0)
   252  }
   253  
   254  func setAllowance(c r.Context, ownerMspId, ownerCertId, spenderMspId, spenderCertId string, amount int) error {
   255  	return c.State().Put(allowanceKey(ownerMspId, ownerCertId, spenderMspId, spenderCertId), amount)
   256  }