github.com/s7techlab/cckit@v0.10.5/examples/cpaper/chaincode.go (about)

     1  package cpaper
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/s7techlab/cckit/router"
     8  	"github.com/s7techlab/cckit/router/param"
     9  )
    10  
    11  type TaskState string
    12  
    13  const (
    14  	CommercialPaperTypeName = "CommercialPaper"
    15  
    16  	CommercialPaperIssued   TaskState = "issued"
    17  	CommercialPaperTrading  TaskState = "trading"
    18  	CommercialPaperRedeemed TaskState = "redeemed"
    19  )
    20  
    21  type CommercialPaper struct {
    22  	Issuer       string    `json:"issuer,omitempty"`
    23  	PaperNumber  string    `json:"paper_number,omitempty"`
    24  	Owner        string    `json:"owner,omitempty"`
    25  	IssueDate    time.Time `json:"issue_date,omitempty"`
    26  	MaturityDate time.Time `json:"maturity_date,omitempty"`
    27  	FaceValue    int32     `json:"face_value,omitempty"`
    28  	State        TaskState `json:"state,omitempty"`
    29  }
    30  
    31  // Key commercial paper has a composite key < CommercialPaper, Issuer, PaperNumber >
    32  func (c CommercialPaper) Key() ([]string, error) {
    33  	return []string{CommercialPaperTypeName, c.Issuer, c.PaperNumber}, nil
    34  }
    35  
    36  type IssueCommercialPaper struct {
    37  	Issuer       string    `json:"issuer,omitempty"`
    38  	PaperNumber  string    `json:"paper_number,omitempty"`
    39  	IssueDate    time.Time `json:"issue_date,omitempty"`
    40  	MaturityDate time.Time `json:"maturity_date,omitempty"`
    41  	FaceValue    int32     `json:"face_value,omitempty"`
    42  }
    43  
    44  type BuyCommercialPaper struct {
    45  	Issuer       string    `json:"issuer,omitempty"`
    46  	PaperNumber  string    `json:"paper_number,omitempty"`
    47  	CurrentOwner string    `json:"current_owner,omitempty"`
    48  	NewOwner     string    `json:"new_owner,omitempty"`
    49  	Price        int32     `json:"price,omitempty"`
    50  	PurchaseDate time.Time `json:"purchase_date,omitempty"`
    51  }
    52  
    53  type RedeemCommercialPaper struct {
    54  	Issuer         string    `json:"issuer,omitempty"`
    55  	PaperNumber    string    `json:"paper_number,omitempty"`
    56  	RedeemingOwner string    `json:"redeeming_owner,omitempty"`
    57  	RedeemDate     time.Time `json:"redeem_date,omitempty"`
    58  }
    59  
    60  func NewCC() *router.Chaincode {
    61  	r := router.New(`commercial_paper`)
    62  
    63  	r.Init(func(context router.Context) (i interface{}, e error) {
    64  		// No implementation required with this example
    65  		// It could be where data migration is performed, if necessary
    66  		return nil, nil
    67  	})
    68  
    69  	r.
    70  		// Read methods
    71  		Query(`list`, list).
    72  		// Get method has 2 params - commercial paper primary key components
    73  		Query(`get`, get, param.String("issuer"), param.String("paper_number")).
    74  
    75  		// Transaction methods
    76  		Invoke(`issue`, issue, param.Struct("issueData", &IssueCommercialPaper{})).
    77  		Invoke(`buy`, buy, param.Struct("buyData", &BuyCommercialPaper{})).
    78  		Invoke(`redeem`, redeem, param.Struct("redeemData", &RedeemCommercialPaper{}))
    79  
    80  	return router.NewChaincode(r)
    81  }
    82  
    83  func get(c router.Context) (interface{}, error) {
    84  	var (
    85  		issuer      = c.ParamString("issuer")
    86  		paperNumber = c.ParamString("paper_number")
    87  	)
    88  
    89  	return c.State().Get(&CommercialPaper{
    90  		Issuer:      issuer,
    91  		PaperNumber: paperNumber,
    92  	})
    93  }
    94  
    95  func list(c router.Context) (interface{}, error) {
    96  	// List method retrieves all entries from the ledger using GetStateByPartialCompositeKey method and passing it the
    97  	// namespace of our contract type, in this example that's `CommercialPaper`, then it unmarshalls received bytes via
    98  	// json.Unmarshal method and creates a JSON array of CommercialPaper entities
    99  	return c.State().List(CommercialPaperTypeName, &CommercialPaper{})
   100  }
   101  
   102  func issue(c router.Context) (interface{}, error) {
   103  	var (
   104  		issueData       = c.Param("issueData").(IssueCommercialPaper) // Assert the chaincode parameter
   105  		commercialPaper = &CommercialPaper{
   106  			Issuer:       issueData.Issuer,
   107  			PaperNumber:  issueData.PaperNumber,
   108  			Owner:        issueData.Issuer,
   109  			IssueDate:    issueData.IssueDate,
   110  			MaturityDate: issueData.MaturityDate,
   111  			FaceValue:    issueData.FaceValue,
   112  			State:        CommercialPaperIssued, // Initial state
   113  		}
   114  	)
   115  
   116  	return commercialPaper, c.State().Insert(commercialPaper)
   117  }
   118  
   119  func buy(c router.Context) (interface{}, error) {
   120  	var (
   121  		commercialPaper CommercialPaper
   122  		// Buy transaction payload
   123  		buyData = c.Param("buyData").(BuyCommercialPaper)
   124  
   125  		// Get the current commercial paper state
   126  		cp, err = c.State().Get(&CommercialPaper{
   127  			Issuer:      buyData.Issuer,
   128  			PaperNumber: buyData.PaperNumber,
   129  		}, &CommercialPaper{})
   130  	)
   131  
   132  	if err != nil {
   133  		return nil, fmt.Errorf("paper not found: %w", err)
   134  	}
   135  
   136  	commercialPaper = cp.(CommercialPaper)
   137  
   138  	// Validate current owner
   139  	if commercialPaper.Owner != buyData.CurrentOwner {
   140  		return nil, fmt.Errorf(
   141  			"paper %s %s is not owned by %s",
   142  			commercialPaper.Issuer, commercialPaper.PaperNumber, buyData.CurrentOwner)
   143  	}
   144  
   145  	// First buyData moves state from ISSUED to TRADING
   146  	if commercialPaper.State == CommercialPaperIssued {
   147  		commercialPaper.State = CommercialPaperTrading
   148  	}
   149  
   150  	// Check paper is not already REDEEMED
   151  	if commercialPaper.State == CommercialPaperTrading {
   152  		commercialPaper.Owner = buyData.NewOwner
   153  	} else {
   154  		return nil, fmt.Errorf(
   155  			"paper %s %s is not trading.current state = %s",
   156  			commercialPaper.Issuer, commercialPaper.PaperNumber, commercialPaper.State)
   157  	}
   158  
   159  	return commercialPaper, c.State().Put(commercialPaper)
   160  }
   161  
   162  func redeem(c router.Context) (interface{}, error) {
   163  	var (
   164  		commercialPaper CommercialPaper
   165  
   166  		// Buy transaction payload
   167  		redeemData = c.Param("redeemData").(RedeemCommercialPaper)
   168  
   169  		// Get the current commercial paper state
   170  		cp, err = c.State().Get(&CommercialPaper{
   171  			Issuer:      redeemData.Issuer,
   172  			PaperNumber: redeemData.PaperNumber,
   173  		}, &CommercialPaper{})
   174  	)
   175  
   176  	if err != nil {
   177  		return nil, fmt.Errorf("paper not found: %w", err)
   178  	}
   179  
   180  	commercialPaper = cp.(CommercialPaper)
   181  
   182  	// Check paper is not REDEEMED
   183  	if commercialPaper.State == CommercialPaperRedeemed {
   184  		return nil, fmt.Errorf(
   185  			"paper %s %s is already redeemed",
   186  			commercialPaper.Issuer, commercialPaper.PaperNumber)
   187  	}
   188  
   189  	// Verify that the redeemer owns the commercial paper before redeeming it
   190  	if commercialPaper.Owner == redeemData.RedeemingOwner {
   191  		commercialPaper.Owner = redeemData.Issuer
   192  		commercialPaper.State = CommercialPaperRedeemed
   193  	} else {
   194  		return nil, fmt.Errorf(
   195  			"redeeming owner does not own paper %s %s",
   196  			commercialPaper.Issuer, commercialPaper.PaperNumber)
   197  	}
   198  
   199  	return commercialPaper, c.State().Put(commercialPaper)
   200  }